Skip to content
Permalink
Browse files

Feature/#906 ESRI ShapeFile reader + Bounds checks on drawing lines, …

…polygons and markers (#937)

* #906 exerpimental shape reader

* #906 adding additional overrides for shape conversion

* #906 work in progress with bounds calculation

* #908 reasonable balance of performance with polygins + point reduction + view bounds trimming

* #906 shape file +  #890 polygon automagic point reduction + #770 adding bounding box

* #906 temporarily disabling the dbase reader until that's published

* #906 fixing the unit tests and adding a field to polygon for defining the minimum zoom level required for drawing.

* #906 disabling point reduction by default

* #906 merging master and fixing the unit tests

* #906 typo

* #906 removing the id field to alighn with #968. typos

* #906 improved shape file support

* #906 enables bounds checks on overlays significant improvement in drawing speeds with zoomed in, zoomed out is still poor

* #906 readme
  • Loading branch information...
spyhunter99 committed Oct 3, 2019
1 parent 72863b0 commit 3dcef1e28fd7a6632cb879c09261ba7b86004324
Showing with 4,389 additions and 91 deletions.
  1. +1 −0 OpenStreetMapViewer/build.gradle
  2. +94 −29 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/BaseSampleFragment.java
  3. +2 −0 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/SampleFactory.java
  4. +130 −0 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/data/SampleShapeFile.java
  5. +4 −2 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/drawing/CustomPaintingSurface.java
  6. +2 −1 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/drawing/DrawPolygon.java
  7. +1 −1 OpenStreetMapViewer/src/main/java/org/osmdroid/samplefragments/events/SampleMapEventListener.java
  8. +44 −0 OpenStreetMapViewer/src/main/res/layout/gotolocation.xml
  9. +154 −2 osmdroid-android/src/main/java/org/osmdroid/util/BoundingBox.java
  10. +4 −4 osmdroid-android/src/main/java/org/osmdroid/util/TileSystemWebMercator.java
  11. +33 −30 osmdroid-android/src/main/java/org/osmdroid/views/overlay/DefaultOverlayManager.java
  12. +33 −8 osmdroid-android/src/main/java/org/osmdroid/views/overlay/FolderOverlay.java
  13. +7 −0 osmdroid-android/src/main/java/org/osmdroid/views/overlay/GroundOverlay.java
  14. +5 −1 osmdroid-android/src/main/java/org/osmdroid/views/overlay/Marker.java
  15. +10 −1 osmdroid-android/src/main/java/org/osmdroid/views/overlay/Polygon.java
  16. +10 −8 osmdroid-android/src/main/java/org/osmdroid/views/overlay/Polyline.java
  17. +57 −0 osmdroid-android/src/test/java/org/osmdroid/util/BoundBoxTest.java
  18. +366 −3 osmdroid-android/src/test/java/org/osmdroid/util/BoundingBoxTest.java
  19. +27 −0 osmdroid-android/src/test/java/org/osmdroid/util/PointReducerTest.java
  20. +17 −0 osmdroid-shape/build.gradle
  21. +24 −0 osmdroid-shape/readme.md
  22. +6 −0 osmdroid-shape/src/main/AndroidManifest.xml
  23. +254 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/ShapeFileReader.java
  24. +144 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/ValidationPreferences.java
  25. +11 −0 ...ape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/exception/DataStreamEOFException.java
  26. +23 −0 .../src/main/java/org/nocrala/tools/gis/data/esri/shapefile/exception/InvalidShapeFileException.java
  27. +166 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/header/ShapeFileHeader.java
  28. +29 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/AbstractShape.java
  29. +11 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/Const.java
  30. +40 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/PartType.java
  31. +21 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/PointData.java
  32. +39 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/ShapeHeader.java
  33. +52 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/ShapeType.java
  34. +85 −0 ...src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractMultiPointShape.java
  35. +36 −0 ...hape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractPointShape.java
  36. +89 −0 ...hape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractPolyMShape.java
  37. +40 −0 .../src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractPolyPlainShape.java
  38. +151 −0 ...shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractPolyShape.java
  39. +138 −0 ...hape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/AbstractPolyZShape.java
  40. +291 −0 ...d-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/MultiPatchShape.java
  41. +73 −0 ...-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/MultiPointMShape.java
  42. +47 −0 ...pe/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/MultiPointPlainShape.java
  43. +97 −0 ...-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/MultiPointZShape.java
  44. +36 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/NullShape.java
  45. +46 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PointMShape.java
  46. +35 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PointShape.java
  47. +52 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PointZShape.java
  48. +32 −0 ...oid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolygonMShape.java
  49. +31 −0 ...roid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolygonShape.java
  50. +32 −0 ...oid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolygonZShape.java
  51. +32 −0 ...id-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolylineMShape.java
  52. +32 −0 ...oid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolylineShape.java
  53. +32 −0 ...id-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/shape/shapes/PolylineZShape.java
  54. +23 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/BAUtil.java
  55. +67 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/DoubleSerializer.java
  56. +63 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/HexaUtil.java
  57. +94 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/ISUtil.java
  58. +67 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/IntSerializer.java
  59. +1 −0 osmdroid-shape/src/main/java/org/nocrala/tools/gis/data/esri/shapefile/util/test.jpage
  60. +193 −0 osmdroid-shape/src/main/java/org/osmdroid/shape/ShapeConverter.java
  61. +124 −0 osmdroid-shape/src/test/java/manualtests/TestShapeFileReader.java
  62. +184 −0 osmdroid-shape/src/test/java/tests/DoubleTests.java
  63. +55 −0 osmdroid-shape/src/test/java/tests/HexaTests.java
  64. +158 −0 osmdroid-shape/src/test/java/tests/IntTests.java
  65. +129 −0 osmdroid-shape/src/test/java/tests/ReaderTests.java
  66. BIN osmdroid-shape/src/test/resources/freefiles/badfiles/multipointm-marked-as-multipointz.shp
  67. BIN osmdroid-shape/src/test/resources/freefiles/multipoint/admin_font_point.shp
  68. BIN osmdroid-shape/src/test/resources/freefiles/pointfiles/prop_text.shp
  69. BIN osmdroid-shape/src/test/resources/freefiles/pointfiles/roadtext.shp
  70. BIN osmdroid-shape/src/test/resources/freefiles/pointfiles/sbuild.shp
  71. BIN osmdroid-shape/src/test/resources/freefiles/polygonfiles/lbuild.shp
  72. BIN osmdroid-shape/src/test/resources/freefiles/polygonfiles/property.shp
  73. BIN osmdroid-shape/src/test/resources/freefiles/polygonfiles/subdiv.shp
  74. BIN osmdroid-shape/src/test/resources/freefiles/polygonfiles/water.shp
  75. BIN osmdroid-shape/src/test/resources/freefiles/polylinefiles/roadcl.shp
  76. BIN osmdroid-shape/src/test/resources/freefiles/polylinefiles/roadeop.shp
  77. BIN osmdroid-shape/src/test/resources/freeworld/10m-coastline/10m_coastline.dbf
  78. +1 −0 osmdroid-shape/src/test/resources/freeworld/10m-coastline/10m_coastline.prj
  79. BIN osmdroid-shape/src/test/resources/freeworld/10m-coastline/10m_coastline.shp
  80. BIN osmdroid-shape/src/test/resources/freeworld/10m-coastline/10m_coastline.shx
  81. +1 −0 settings.gradle
  82. +1 −1 src/site
@@ -59,6 +59,7 @@ dependencies {
compile project(':osmdroid-geopackage')
compile project(':osmdroid-mapsforge')
compile project(':osmdroid-wms')
compile project(':osmdroid-shape')

compile 'mil.army.missioncommand:mil-sym-android-renderer:0.1.37'

@@ -1,6 +1,8 @@
package org.osmdroid.samplefragments;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.DisplayMetrics;
@@ -13,14 +15,21 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import org.osmdroid.R;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.util.TileSystem;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.CopyrightOverlay;

public abstract class BaseSampleFragment extends Fragment {
private static int MENU_LAST_ID = Menu.FIRST; // Always set to last unused id
public static final String TAG = "osmBaseFrag";

AlertDialog gotoLocationDialog=null;
public abstract String getSampleTitle();

// ===========================================================
@@ -43,30 +52,30 @@ public void onCreate(Bundle savedInstanceState) {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mMapView = new MapView(inflater.getContext());
mMapView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
/**
* mouse wheel zooming ftw
* http://stackoverflow.com/questions/11024809/how-can-my-view-respond-to-a-mousewheel
* @param v
* @param event
* @return
*/
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
if (0 != (event.getSource() & InputDevice.SOURCE_CLASS_POINTER)) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0.0f)
mMapView.getController().zoomOut();
else {
mMapView.getController().zoomIn();
}
return true;
mMapView.setOnGenericMotionListener(new View.OnGenericMotionListener() {
/**
* mouse wheel zooming ftw
* http://stackoverflow.com/questions/11024809/how-can-my-view-respond-to-a-mousewheel
* @param v
* @param event
* @return
*/
@Override
public boolean onGenericMotion(View v, MotionEvent event) {
if (0 != (event.getSource() & InputDevice.SOURCE_CLASS_POINTER)) {
switch (event.getAction()) {
case MotionEvent.ACTION_SCROLL:
if (event.getAxisValue(MotionEvent.AXIS_VSCROLL) < 0.0f)
mMapView.getController().zoomOut();
else {
mMapView.getController().zoomIn();
}
return true;
}
}
return false;
}
return false;
}
});
});
Log.d(TAG, "onCreateView");
return mMapView;
}
@@ -75,17 +84,17 @@ public boolean onGenericMotion(View v, MotionEvent event) {
@Override
public void onPause(){
if (mMapView != null) {
mMapView.onPause();
}
mMapView.onPause();
}
super.onPause();
}

@Override
public void onResume(){
super.onResume();
if (mMapView != null) {
mMapView.onResume();
}
mMapView.onResume();
}
}

@Override
@@ -102,7 +111,7 @@ public void onActivityCreated(Bundle savedInstanceState) {
CopyrightOverlay copyrightOverlay = new CopyrightOverlay(getActivity());
copyrightOverlay.setTextSize(10);

mMapView.getOverlays().add(copyrightOverlay);
mMapView.getOverlays().add(copyrightOverlay);
mMapView.setMultiTouchControls(true);
mMapView.setTilesScaledToDpi(true);
}
@@ -115,20 +124,23 @@ public void onDestroyView() {
if (mMapView != null)
mMapView.onDetach();
mMapView = null;

}

@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");

if (gotoLocationDialog!=null)
gotoLocationDialog.dismiss();
}

int MENU_VERTICAL_REPLICATION = 0;
int MENU_HORIZTONAL_REPLICATION = 0;
int MENU_ROTATE_CLOCKWISE = 0;
int MENU_ROTATE_COUNTER_CLOCKWISE = 0;
int MENU_SCALE_TILES = 0;
int MENU_GOTO = 0;


@Override
@@ -145,6 +157,10 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
MENU_SCALE_TILES = MENU_LAST_ID;
menu.add(0, MENU_SCALE_TILES, Menu.NONE, "Scale Tiles").setCheckable(true);

MENU_LAST_ID++;
MENU_GOTO = MENU_LAST_ID;
menu.add(0, MENU_GOTO, Menu.NONE, "Go To");

MENU_LAST_ID++;
MENU_ROTATE_CLOCKWISE = MENU_LAST_ID;
menu.add(0, MENU_ROTATE_CLOCKWISE, Menu.NONE, "Rotate Clockwise");
@@ -217,6 +233,54 @@ public void run() {
currentRotation = currentRotation + 360;
mMapView.setMapOrientation(currentRotation, true);
return true;
} else if (item.getItemId()==MENU_GOTO) {
//TODO dialog with lat/lon prompt
//prompt for input params
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());

View view = View.inflate(getActivity(), R.layout.gotolocation, null);

final EditText lat = (EditText) view.findViewById(R.id.latlonPicker_latitude);
final EditText lon = (EditText) view.findViewById(R.id.latlonPicker_longitude);
final Button cancel = (Button) view.findViewById(R.id.latlonPicker_cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoLocationDialog.dismiss();
}
});

Button ok = (Button) view.findViewById(R.id.latlonPicker_ok);
ok.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
gotoLocationDialog.dismiss();
try {
double latd = Double.parseDouble(lat.getText().toString());
if (latd < TileSystem.MinLatitude || latd > TileSystem.MaxLatitude)
throw new Exception();
double lond = Double.parseDouble(lon.getText().toString());
if (lond < TileSystem.MinLongitude || lond > TileSystem.MaxLongitude)
throw new Exception();
GeoPoint pt = new GeoPoint(latd, lond);
mMapView.getController().animateTo(pt);
}catch (Exception ex) {
Toast.makeText(getActivity(), "Invalid input", Toast.LENGTH_SHORT).show();
}
}
});

builder.setView(view);
builder.setCancelable(true);
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
gotoLocationDialog.dismiss();
}
});
gotoLocationDialog = builder.create();
gotoLocationDialog.show();

} else if (mMapView.getOverlayManager().onOptionsItemSelected(item, MENU_LAST_ID, mMapView)) {
return true;
}
@@ -227,7 +291,8 @@ public void run() {
* An appropriate place to override and add overlays.
*/
protected void addOverlays() {
//


}

public boolean skipOnCiTests() {
@@ -34,6 +34,7 @@
import org.osmdroid.samplefragments.data.SampleMilitaryIconsMarker;
import org.osmdroid.samplefragments.data.SampleOsmPath;
import org.osmdroid.samplefragments.data.SampleRace;
import org.osmdroid.samplefragments.data.SampleShapeFile;
import org.osmdroid.samplefragments.data.SampleSimpleFastPointOverlay;
import org.osmdroid.samplefragments.data.SampleSimpleLocation;
import org.osmdroid.samplefragments.drawing.SampleDrawPolylineAsPath;
@@ -296,6 +297,7 @@ private SampleFactory() {
if (Build.VERSION.SDK_INT >= 15)
mSamples.add(Plotter.class);
mSamples.add(WeatherGroundOverlaySample.class);
mSamples.add(SampleShapeFile.class);
mSamples.add(CompassPointerSample.class);
mSamples.add(CompassRoseSample.class);
mSamples.add(SampleZoomRounding.class);
@@ -0,0 +1,130 @@
package org.osmdroid.samplefragments.data;

import android.graphics.Color;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.widget.Toast;

import com.github.angads25.filepicker.controller.DialogSelectionListener;
import com.github.angads25.filepicker.model.DialogConfigs;
import com.github.angads25.filepicker.model.DialogProperties;
import com.github.angads25.filepicker.view.FilePickerDialog;

import org.osmdroid.samplefragments.events.SampleMapEventListener;
import org.osmdroid.shape.ShapeConverter;
import org.osmdroid.tileprovider.modules.ArchiveFileFactory;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.util.BoundingBox;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.overlay.FolderOverlay;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.Polygon;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
* A simple how to for importing and display an ESRI shape file
* created on 1/28/2018.
*
* @author Alex O'Ree
*/

public class SampleShapeFile extends SampleMapEventListener {


@Override
public String getSampleTitle() {
return "Shape File Import";
}

final int MENU_ADD_SHAPE = Menu.FIRST;
final int MENU_ADD_BOUNDS = MENU_ADD_SHAPE + 1;

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
menu.add(0, MENU_ADD_SHAPE, Menu.NONE, "Import a shape file");
menu.add(0, MENU_ADD_BOUNDS, Menu.NONE, "Draw bounds");
super.onCreateOptionsMenu(menu, inflater);
}

@Override
public void onPrepareOptionsMenu(Menu menu) {

super.onPrepareOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_ADD_SHAPE:
showPicker();
return true;
case MENU_ADD_BOUNDS:
List<GeoPoint> pts = new ArrayList<>();
BoundingBox boundingBox = mMapView.getBoundingBox();
pts.add(new GeoPoint(boundingBox.getLatNorth(), boundingBox.getLonEast()));
pts.add(new GeoPoint(boundingBox.getLatSouth(), boundingBox.getLonEast()));
pts.add(new GeoPoint(boundingBox.getLatSouth(), boundingBox.getLonWest()));
pts.add(new GeoPoint(boundingBox.getLatNorth(), boundingBox.getLonWest()));
pts.add(new GeoPoint(boundingBox.getLatNorth(), boundingBox.getLonEast()));
Polygon bounds = new Polygon(mMapView);
bounds.setPoints(pts);
bounds.setSubDescription(boundingBox.toString());
// bounds.setStrokeColor(Color.RED);
mMapView.getOverlayManager().add(bounds);
mMapView.invalidate();
return true;
}
return super.onOptionsItemSelected(item);
}

@Override
protected void addOverlays() {
super.addOverlays();
mMapView.setTileSource(TileSourceFactory.DEFAULT_TILE_SOURCE);
mMapView.invalidate();
}

private void showPicker() {
DialogProperties properties = new DialogProperties();
properties.selection_mode = DialogConfigs.SINGLE_MODE;
properties.selection_type = DialogConfigs.FILE_SELECT;
properties.root = new File(DialogConfigs.DEFAULT_DIR);
properties.error_dir = new File(DialogConfigs.DEFAULT_DIR);
properties.offset = new File(DialogConfigs.DEFAULT_DIR);

Set<String> registeredExtensions = ArchiveFileFactory.getRegisteredExtensions();
registeredExtensions.add("shp");


String[] ret = new String[registeredExtensions.size()];
ret = registeredExtensions.toArray(ret);
properties.extensions = ret;

FilePickerDialog dialog = new FilePickerDialog(getContext(), properties);
dialog.setTitle("Select a File");
dialog.setDialogSelectionListener(new DialogSelectionListener() {
@Override
public void onSelectedFilePaths(String[] files) {
//files is the array of the paths of files selected by the Application User.
try {
List<Overlay> folder = ShapeConverter.convert(mMapView, new File(files[0]));
mMapView.getOverlayManager().addAll(folder);
mMapView.invalidate();
} catch (Exception e) {
Toast.makeText(getActivity(), "Error importing file: " + e.getMessage(), Toast.LENGTH_LONG).show();
Log.e(TAG, "error importing file from " + files[0], e);
}

}

});
dialog.show();

}
}

0 comments on commit 3dcef1e

Please sign in to comment.
You can’t perform that action at this time.