From 1bf7e2e0fb57821bd0c744cf0c4b5b4550f73cb8 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 21 Aug 2017 12:17:46 -0600 Subject: [PATCH 01/28] Washington DC sample data links from US Army Geospatial Center --- mapcache/src/main/res/values/strings.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mapcache/src/main/res/values/strings.xml b/mapcache/src/main/res/values/strings.xml index da7cc53c..741f7d7a 100644 --- a/mapcache/src/main/res/values/strings.xml +++ b/mapcache/src/main/res/values/strings.xml @@ -209,14 +209,20 @@ OGC Sample Data NGA Sample Data + Washington DC + Washington DC Vector Blue Marble Whitehorse + http://portal.opengeospatial.org/files/74983 + http://portal.opengeospatial.org/files/74984 http://portal.opengeospatial.org/files/73648 http://portal.opengeospatial.org/files/63156 + washington_dc + washington_dc_vector blue_marble whitehorse From 1a82c35398405526960535121ae2ccc9ddfccca9 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Thu, 24 Aug 2017 13:51:15 -0600 Subject: [PATCH 02/28] geopackage android map 1.4.2 --- mapcache/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 0b1defdf..37a0425e 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -50,7 +50,7 @@ task androidAppVersion { } dependencies { - compile "mil.nga.geopackage.map:geopackage-android-map:1.4.1" // comment out to build locally + compile "mil.nga.geopackage.map:geopackage-android-map:1.4.2" // comment out to build locally compile 'com.android.support:multidex:1.0.1' //compile project(':geopackage-map') // uncomment me to build locally } From 9c648382b7ae4789c45298a7317aa8dabe16ddb7 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Wed, 6 Sep 2017 15:21:13 -0600 Subject: [PATCH 03/28] compile and target android sdk version 26 appcompat dependency to replace lost geopackage android map play services dependency multidex version 1.0.2 gradle version 2.3.3 android maven gradle plugin version 2.0 --- build.gradle | 10 ++++++++-- mapcache/build.gradle | 10 ++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/build.gradle b/build.gradle index 74bb64f7..9a32f7bd 100644 --- a/build.gradle +++ b/build.gradle @@ -2,17 +2,23 @@ buildscript { repositories { jcenter() + maven { + url "https://maven.google.com" + } mavenLocal() } dependencies { - classpath 'com.android.tools.build:gradle:2.3.2' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + classpath 'com.android.tools.build:gradle:2.3.3' + classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' } } allprojects { repositories { jcenter() + maven { + url "https://maven.google.com" + } mavenLocal() } } diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 37a0425e..0b7e1fc9 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -5,8 +5,8 @@ def googleMapsApiReleaseKey = hasProperty('RELEASE_MAPS_MAPCACHE_API_KEY') ? REL def googleMapsApiKeyDebug = hasProperty('DEBUG_MAPS_API_KEY') ? DEBUG_MAPS_API_KEY: '' android { - compileSdkVersion 25 - buildToolsVersion "25.0.3" + compileSdkVersion 26 + buildToolsVersion "26.0.1" dexOptions { javaMaxHeapSize "4g" @@ -15,7 +15,7 @@ android { defaultConfig { applicationId "mil.nga.mapcache" minSdkVersion 14 - targetSdkVersion 25 + targetSdkVersion 26 versionCode 18 versionName '1.17' multiDexEnabled true @@ -50,9 +50,11 @@ task androidAppVersion { } dependencies { + compile 'com.android.support:appcompat-v7:26.0.2' compile "mil.nga.geopackage.map:geopackage-android-map:1.4.2" // comment out to build locally - compile 'com.android.support:multidex:1.0.1' //compile project(':geopackage-map') // uncomment me to build locally + compile 'com.android.support:multidex:1.0.2' + androidTestCompile 'com.android.support:multidex:1.0.2' } configure extensions.android, { From cb95990849ac80136421a96a4c41d2738405de16 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 6 Oct 2017 13:34:42 -0600 Subject: [PATCH 04/28] update max features and feature tiles max default limits --- mapcache/src/main/res/values/integers.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mapcache/src/main/res/values/integers.xml b/mapcache/src/main/res/values/integers.xml index c001c21b..cd0d558b 100644 --- a/mapcache/src/main/res/values/integers.xml +++ b/mapcache/src/main/res/values/integers.xml @@ -1,7 +1,7 @@ - 1000 + 5000 0 10000 10 @@ -16,8 +16,8 @@ 8 50 50 - 1000 - 500 + 5000 + 2000 -1 From f5c962d201e9d97ad4b1e7134c1ae57aa9f6f702 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 10 Oct 2017 15:46:42 -0600 Subject: [PATCH 05/28] changes in progress for adding simplified geometries only when visible on the map view --- .../nga/mapcache/GeoPackageMapFragment.java | 620 +++++++++++++----- 1 file changed, 469 insertions(+), 151 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index ab78a19d..97911714 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -44,6 +44,7 @@ import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.GoogleMap.OnCameraIdleListener; import com.google.android.gms.maps.GoogleMap.OnMapClickListener; import com.google.android.gms.maps.GoogleMap.OnMapLongClickListener; import com.google.android.gms.maps.GoogleMap.OnMarkerClickListener; @@ -64,15 +65,20 @@ import com.google.android.gms.maps.model.TileProvider; import org.osgeo.proj4j.units.DegreeUnit; +import org.osgeo.proj4j.units.Unit; +import org.osgeo.proj4j.units.Units; import java.sql.SQLException; import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -90,10 +96,13 @@ import mil.nga.geopackage.factory.GeoPackageFactory; import mil.nga.geopackage.features.columns.GeometryColumns; import mil.nga.geopackage.features.index.FeatureIndexManager; +import mil.nga.geopackage.features.index.FeatureIndexResults; import mil.nga.geopackage.features.user.FeatureCursor; import mil.nga.geopackage.features.user.FeatureDao; import mil.nga.geopackage.features.user.FeatureRow; import mil.nga.geopackage.geom.GeoPackageGeometryData; +import mil.nga.geopackage.map.MapUtils; +import mil.nga.geopackage.map.geom.FeatureShapes; import mil.nga.geopackage.map.geom.GoogleMapShape; import mil.nga.geopackage.map.geom.GoogleMapShapeConverter; import mil.nga.geopackage.map.geom.GoogleMapShapeMarkers; @@ -125,6 +134,7 @@ import mil.nga.mapcache.data.GeoPackageDatabase; import mil.nga.mapcache.data.GeoPackageDatabases; import mil.nga.mapcache.data.GeoPackageFeatureOverlayTable; +import mil.nga.mapcache.data.GeoPackageFeatureTable; import mil.nga.mapcache.data.GeoPackageTable; import mil.nga.mapcache.data.GeoPackageTileTable; import mil.nga.mapcache.filter.InputFilterMinMax; @@ -132,8 +142,10 @@ import mil.nga.mapcache.load.ILoadTilesTask; import mil.nga.mapcache.load.LoadTilesTask; import mil.nga.wkb.geom.Geometry; +import mil.nga.wkb.geom.GeometryEnvelope; import mil.nga.wkb.geom.GeometryType; import mil.nga.wkb.geom.LineString; +import mil.nga.wkb.util.GeometryEnvelopeBuilder; import mil.nga.wkb.util.GeometryPrinter; /** @@ -143,7 +155,7 @@ */ public class GeoPackageMapFragment extends Fragment implements OnMapReadyCallback, OnMapLongClickListener, OnMapClickListener, OnMarkerClickListener, - OnMarkerDragListener, ILoadTilesTask, IIndexerTask { + OnMarkerDragListener, ILoadTilesTask, IIndexerTask, OnCameraIdleListener { /** * Max features key for saving to preferences @@ -203,7 +215,12 @@ public class GeoPackageMapFragment extends Fragment implements /** * Mapping of open GeoPackages by name */ - private Map geoPackages = new HashMap(); + private Map geoPackages = new HashMap<>(); + + /** + * Mapping of open GeoPackage feature DAOs + */ + private Map> featureDaos = new HashMap<>(); /** * Vibrator @@ -270,6 +287,16 @@ public class GeoPackageMapFragment extends Fragment implements */ private String editFeaturesTable; + /** + * Feature shapes + */ + private FeatureShapes featureShapes = new FeatureShapes(); + + /** + * Current zoom level + */ + private int currentZoom = -1; + /** * Mapping between marker ids and the feature ids */ @@ -468,7 +495,6 @@ public void onMapReady(GoogleMap googleMap) { initializeMap(); } - private void initializeMap() { if (map == null) return; @@ -484,9 +510,28 @@ private void initializeMap() { map.setOnMapClickListener(this); map.setOnMarkerClickListener(this); map.setOnMarkerDragListener(this); + map.setOnCameraIdleListener(this); map.getUiSettings().setZoomControlsEnabled(true); - if (visible) { + } + + @Override + public void onCameraIdle(){ + + // If visible & not editing a shape, update the feature shapes for the current map view region + if(visible && (!editFeaturesMode || editFeatureType == null || (editPoints.isEmpty() && editFeatureMarker == null))){ + + int previousZoom = currentZoom; + int zoom = (int) MapUtils.getCurrentZoom(map); + currentZoom = zoom; + if(zoom != previousZoom){ + // Zoom level changed, remove all feature shapes + featureShapes.removeShapes(); + }else{ + // Remove shapes no longer visible on the map view + featureShapes.removeShapesNotWithinMap(map); + } + updateInBackground(active.isModified()); } @@ -1424,14 +1469,20 @@ private void updateInBackground(boolean zoom) { } } geoPackages.clear(); + featureDaos.clear(); featuresBoundingBox = null; tilesBoundingBox = null; featureOverlayTiles = false; featureOverlayQueries.clear(); + featureShapes.clear(); markerIds.clear(); updateTask = new MapUpdateTask(); int maxFeatures = getMaxFeatures(); - updateTask.execute(zoom, maxFeatures); + + BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); + double toleranceDistance = MapUtils.getToleranceDistance(view, map); + + updateTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, true); // TODO when to filter? } @@ -1455,6 +1506,21 @@ private class MapUpdateTask extends AsyncTask { */ private int maxFeatures; + /** + * Map view bounding box + */ + private BoundingBox mapViewBoundingBox; + + /** + * Tolerance distance for simplification + */ + private double toleranceDistance; + + /** + * Filter flag + */ + private boolean filter; + /** * Update start time */ @@ -1478,7 +1544,10 @@ protected void onPreExecute() { protected Integer doInBackground(Object... params) { zoom = (Boolean) params[0]; maxFeatures = (Integer) params[1]; - int count = update(this, maxFeatures); + mapViewBoundingBox = (BoundingBox) params[2]; + toleranceDistance = (Double) params[3]; + filter = (Boolean) params[4]; + int count = update(this, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); return count; } @@ -1487,20 +1556,31 @@ protected Integer doInBackground(Object... params) { */ @Override protected void onProgressUpdate(Object... shapeUpdate) { - GoogleMapShape shape = (GoogleMapShape) shapeUpdate[3]; - - GoogleMapShape mapShape = GoogleMapShapeConverter.addShapeToMap( - map, shape); long featureId = (Long) shapeUpdate[0]; String database = (String) shapeUpdate[1]; String tableName = (String) shapeUpdate[2]; - if (editFeaturesMode) { - addEditableShape(featureId, mapShape); - } else { - addMarkerShape(featureId, database, tableName, mapShape); - } + GoogleMapShape shape = (GoogleMapShape) shapeUpdate[3]; + + synchronized (featureShapes) { + + if (!featureShapes.exists(featureId, database, tableName)) { + + GoogleMapShape mapShape = GoogleMapShapeConverter.addShapeToMap( + map, shape); + if (editFeaturesMode) { + Marker marker = addEditableShape(featureId, mapShape); + if (marker != null) { + GoogleMapShape mapPointShape = new GoogleMapShape(GeometryType.POINT, GoogleMapShapeType.MARKER, marker); + featureShapes.addMapShape(mapPointShape, featureId, database, tableName); + } + } else { + addMarkerShape(featureId, database, tableName, mapShape); + } + featureShapes.addMapShape(mapShape, featureId, database, tableName); + } + } } /** @@ -1526,7 +1606,7 @@ protected void onPostExecute(Integer count) { } } if (zoom) { - zoomToActive(); + zoomToActive(true); } } @@ -1549,41 +1629,78 @@ public void addToMap(long featureId, String database, String tableName, GoogleMa * * @param task * @param maxFeatures + * @param mapViewBoundingBox + * @param toleranceDistance + * @param filter * @return feature count */ - private int update(MapUpdateTask task, final int maxFeatures) { + private int update(MapUpdateTask task, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { - AtomicInteger count = new AtomicInteger(); + int count = 0; if (active != null) { - // Add tile overlays first - List activeDatabases = new ArrayList(); + // Open active GeoPackages and create feature DAOS, display tiles and feature tiles + List activeDatabases = new ArrayList<>(); activeDatabases.addAll(active.getDatabases()); for (GeoPackageDatabase database : activeDatabases) { - // Open each GeoPackage + if (task.isCancelled()) { + break; + } + GeoPackage geoPackage = manager.open(database.getDatabase()); if (geoPackage != null) { geoPackages.put(database.getDatabase(), geoPackage); + Set featureTableDaos = new HashSet<>(); + Collection features = database.getFeatures(); + if (!features.isEmpty()) { + for (GeoPackageFeatureTable featureTable : features) { + featureTableDaos.add(featureTable.getName()); + } + } + + for (GeoPackageFeatureOverlayTable featureOverlay : database.getFeatureOverlays()) { + if (featureOverlay.isActive()) { + featureTableDaos.add(featureOverlay.getFeatureTable()); + } + } + + if (!featureTableDaos.isEmpty()) { + Map databaseFeatureDaos = new HashMap<>(); + featureDaos.put(database.getDatabase(), databaseFeatureDaos); + for (String featureTable : featureTableDaos) { + + if (task.isCancelled()) { + break; + } + + FeatureDao featureDao = geoPackage.getFeatureDao(featureTable); + databaseFeatureDaos.put(featureTable, featureDao); + } + } + // Display the tiles for (GeoPackageTileTable tiles : database.getTiles()) { + if (task.isCancelled()) { + break; + } try { displayTiles(tiles); } catch (Exception e) { Log.e(GeoPackageMapFragment.class.getSimpleName(), e.getMessage()); } - if (task.isCancelled()) { - break; - } } // Display the feature tiles for (GeoPackageFeatureOverlayTable featureOverlay : database.getFeatureOverlays()) { + if (task.isCancelled()) { + break; + } if (featureOverlay.isActive()) { try { displayFeatureTiles(featureOverlay); @@ -1592,87 +1709,126 @@ private int update(MapUpdateTask task, final int maxFeatures) { e.getMessage()); } } - if (task.isCancelled()) { - break; - } } + } else { active.removeDatabase(database.getDatabase(), false); } - - if (task.isCancelled()) { - break; - } } // Add features - Map> featureTables = new HashMap>(); - if (editFeaturesMode) { - List databaseFeatures = new ArrayList(); - databaseFeatures.add(editFeaturesTable); - featureTables.put(editFeaturesDatabase, databaseFeatures); - GeoPackage geoPackage = geoPackages.get(editFeaturesDatabase); - if (geoPackage == null) { - geoPackage = manager.open(editFeaturesDatabase); - geoPackages.put(editFeaturesDatabase, geoPackage); - } - } else { - for (GeoPackageDatabase database : active.getDatabases()) { - if (!database.getFeatures().isEmpty()) { - List databaseFeatures = new ArrayList(); - featureTables.put(database.getDatabase(), - databaseFeatures); - for (GeoPackageTable features : database.getFeatures()) { - databaseFeatures.add(features.getName()); - } + if (!task.isCancelled()) { + count = addFeatures(task, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); + } + + } + + return count; + } + + /** + * Add features to the map + * + * @param task udpate task + * @param maxFeatures max features + * @param mapViewBoundingBox map view bounding box + * @param toleranceDistance tolerance distance + * @param filter filter + * @return feature count + */ + private int addFeatures(MapUpdateTask task, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { + + AtomicInteger count = new AtomicInteger(); + + Map> featureTables = new HashMap<>(); + if (editFeaturesMode) { + List databaseFeatures = new ArrayList<>(); + databaseFeatures.add(editFeaturesTable); + featureTables.put(editFeaturesDatabase, databaseFeatures); + GeoPackage geoPackage = geoPackages.get(editFeaturesDatabase); + if (geoPackage == null) { + geoPackage = manager.open(editFeaturesDatabase); + geoPackages.put(editFeaturesDatabase, geoPackage); + } + Map databaseFeatureDaos = featureDaos.get(editFeaturesDatabase); + if (databaseFeatureDaos == null) { + databaseFeatureDaos = new HashMap<>(); + featureDaos.put(editFeaturesDatabase, databaseFeatureDaos); + } + FeatureDao featureDao = databaseFeatureDaos.get(editFeaturesTable); + if (featureDao == null) { + featureDao = geoPackage.getFeatureDao(editFeaturesTable); + databaseFeatureDaos.put(editFeaturesTable, featureDao); + } + } else { + for (GeoPackageDatabase database : active.getDatabases()) { + if (!database.getFeatures().isEmpty()) { + List databaseFeatures = new ArrayList<>(); + featureTables.put(database.getDatabase(), + databaseFeatures); + for (GeoPackageTable features : database.getFeatures()) { + databaseFeatures.add(features.getName()); } } } + } - // Get the thread pool size, or 0 if single threaded - int threadPoolSize = getActivity().getResources().getInteger( - R.integer.map_update_thread_pool_size); + // Get the thread pool size, or 0 if single threaded + int threadPoolSize = getActivity().getResources().getInteger( + R.integer.map_update_thread_pool_size); - // Create a thread pool for processing features - ExecutorService threadPool = null; - if (threadPoolSize > 0) { - threadPool = Executors.newFixedThreadPool(threadPoolSize); + // Create a thread pool for processing features + ExecutorService threadPool = null; + if (threadPoolSize > 0) { + threadPool = Executors.newFixedThreadPool(threadPoolSize); + } + + for (Map.Entry> databaseFeaturesEntry : featureTables + .entrySet()) { + + if (count.get() >= maxFeatures) { + break; } - for (Map.Entry> databaseFeatures : featureTables - .entrySet()) { + String databaseName = databaseFeaturesEntry.getKey(); - if (count.get() >= maxFeatures) { - break; - } + if (geoPackages.containsKey(databaseName)) { - for (String features : databaseFeatures.getValue()) { - displayFeatures(task, threadPool, - databaseFeatures.getKey(), features, count, - maxFeatures, editFeaturesMode); - if (task.isCancelled() || count.get() >= maxFeatures) { - break; + List databaseFeatures = databaseFeaturesEntry.getValue(); + Map databaseFeatureDaos = featureDaos.get(databaseName); + + if (databaseFeatureDaos != null) { + for (String features : databaseFeatures) { + + if (databaseFeatureDaos.containsKey(features)) { + + displayFeatures(task, threadPool, + databaseName, features, count, + maxFeatures, editFeaturesMode, mapViewBoundingBox, toleranceDistance, filter); + if (task.isCancelled() || count.get() >= maxFeatures) { + break; + } + } } } + } - if (task.isCancelled()) { - break; - } + if (task.isCancelled()) { + break; } + } - if (threadPool != null) { - threadPool.shutdown(); - if (!task.isCancelled() && count.get() < maxFeatures) { - try { - threadPool.awaitTermination(getActivity().getResources().getInteger( - R.integer.map_update_thread_pool_finish_wait), - TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - Log.w(GeoPackageMapFragment.class.getSimpleName(), e); - } + if (threadPool != null) { + threadPool.shutdown(); + if (!task.isCancelled() && count.get() < maxFeatures) { + try { + threadPool.awaitTermination(getActivity().getResources().getInteger( + R.integer.map_update_thread_pool_finish_wait), + TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + Log.w(GeoPackageMapFragment.class.getSimpleName(), e); } } - } return Math.min(count.get(), maxFeatures); @@ -1682,6 +1838,15 @@ private int update(MapUpdateTask task, final int maxFeatures) { * Zoom to features on the map, or tiles if no features */ private void zoomToActive() { + zoomToActive(false); + } + + /** + * Zoom to features on the map, or tiles if no features + * + * @param nothingVisible zoom only if nothing is currently visible + */ + private void zoomToActive(boolean nothingVisible) { BoundingBox bbox = featuresBoundingBox; @@ -1702,37 +1867,45 @@ private void zoomToActive() { if (bbox != null) { - double minLatitude = Math.max(bbox.getMinLatitude(), ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE); - double maxLatitude = Math.min(bbox.getMaxLatitude(), ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE); + boolean zoomToActive = true; + if (nothingVisible) { + BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); + zoomToActive = TileBoundingBoxUtils.overlap(bbox, mapViewBoundingBox, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) != null; + } - LatLng lowerLeft = new LatLng(minLatitude, bbox.getMinLongitude()); - LatLng lowerRight = new LatLng(minLatitude, bbox.getMaxLongitude()); - LatLng topLeft = new LatLng(maxLatitude, bbox.getMinLongitude()); - LatLng topRight = new LatLng(maxLatitude, bbox.getMaxLongitude()); + if (zoomToActive) { + double minLatitude = Math.max(bbox.getMinLatitude(), ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE); + double maxLatitude = Math.min(bbox.getMaxLatitude(), ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE); - if (lowerLeft.longitude == lowerRight.longitude) { - double adjustLongitude = lowerRight.longitude - .0000000000001; - lowerRight = new LatLng(minLatitude, adjustLongitude); - topRight = new LatLng(maxLatitude, adjustLongitude); - } + LatLng lowerLeft = new LatLng(minLatitude, bbox.getMinLongitude()); + LatLng lowerRight = new LatLng(minLatitude, bbox.getMaxLongitude()); + LatLng topLeft = new LatLng(maxLatitude, bbox.getMinLongitude()); + LatLng topRight = new LatLng(maxLatitude, bbox.getMaxLongitude()); - final LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder(); - boundsBuilder.include(lowerLeft); - boundsBuilder.include(lowerRight); - boundsBuilder.include(topLeft); - boundsBuilder.include(topRight); + if (lowerLeft.longitude == lowerRight.longitude) { + double adjustLongitude = lowerRight.longitude - .0000000000001; + lowerRight = new LatLng(minLatitude, adjustLongitude); + topRight = new LatLng(maxLatitude, adjustLongitude); + } - View view = getView(); - int minViewLength = Math.min(view.getWidth(), view.getHeight()); - final int padding = (int) Math.floor(minViewLength - * paddingPercentage); + final LatLngBounds.Builder boundsBuilder = new LatLngBounds.Builder(); + boundsBuilder.include(lowerLeft); + boundsBuilder.include(lowerRight); + boundsBuilder.include(topLeft); + boundsBuilder.include(topRight); - try { - map.animateCamera(CameraUpdateFactory.newLatLngBounds( - boundsBuilder.build(), padding)); - } catch (Exception e) { - Log.w(GeoPackageMapFragment.class.getSimpleName(), - "Unable to move camera", e); + View view = getView(); + int minViewLength = Math.min(view.getWidth(), view.getHeight()); + final int padding = (int) Math.floor(minViewLength + * paddingPercentage); + + try { + map.animateCamera(CameraUpdateFactory.newLatLngBounds( + boundsBuilder.build(), padding)); + } catch (Exception e) { + Log.w(GeoPackageMapFragment.class.getSimpleName(), + "Unable to move camera", e); + } } } } @@ -1747,42 +1920,137 @@ private void zoomToActive() { * @param count * @param maxFeatures * @param editable + * @param mapViewBoundingBox + * @param toleranceDistance + * @param filter */ private void displayFeatures(MapUpdateTask task, ExecutorService threadPool, String database, String features, - AtomicInteger count, final int maxFeatures, final boolean editable) { + AtomicInteger count, final int maxFeatures, final boolean editable, + BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { // Get the GeoPackage and feature DAO GeoPackage geoPackage = geoPackages.get(database); - FeatureDao featureDao = geoPackage.getFeatureDao(features); + FeatureDao featureDao = featureDaos.get(database).get(features); + GoogleMapShapeConverter converter = new GoogleMapShapeConverter(featureDao.getProjection()); + + converter.setSimplifyTolerance(toleranceDistance); + + count.getAndAdd(featureShapes.getFeatureIdsCount(database, features)); + + if (!task.isCancelled() && count.get() < maxFeatures) { + + mil.nga.geopackage.projection.Projection mapViewProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); + + FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); + if (filter && indexer.isIndexed()) { + + FeatureIndexResults indexResults = indexer.query(mapViewBoundingBox, mapViewProjection); + processFeatureIndexResults(task, threadPool, indexResults, database, featureDao, converter, + count, maxFeatures, editable, filter); + + BoundingBox complementary = mapViewBoundingBox.complementaryWgs84(); + if (complementary != null) { + indexResults = indexer.query(complementary, mapViewProjection); + processFeatureIndexResults(task, threadPool, indexResults, database, featureDao, converter, + count, maxFeatures, editable, filter); + } + + } else { + + mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); + ProjectionTransform projectionTransform = mapViewProjection.getTransformation(featureProjection); + BoundingBox filterBoundingBox = projectionTransform.transform(mapViewBoundingBox); + double filterMaxLongitude = 0; + Unit unit = featureProjection.getUnit(); + if (unit instanceof DegreeUnit) { + filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; + } else if (unit == Units.METRES) { + filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; + } + + // Query for all rows + FeatureCursor cursor = featureDao.queryForAll(); + try { + while (!task.isCancelled() && count.get() < maxFeatures + && cursor.moveToNext()) { + try { + FeatureRow row = cursor.getRow(); + + if (threadPool != null) { + // Process the feature row in the thread pool + FeatureRowProcessor processor = new FeatureRowProcessor( + task, database, featureDao, row, count, maxFeatures, editable, converter, + filterBoundingBox, filterMaxLongitude, filter); + threadPool.execute(processor); + } else { + + processFeatureRow(task, database, featureDao, converter, row, count, maxFeatures, editable, + filterBoundingBox, filterMaxLongitude, filter); + } + } catch (Exception e) { + Log.e(GeoPackageMapFragment.class.getSimpleName(), + "Failed to display feature. database: " + database + + ", feature table: " + features + + ", row: " + cursor.getPosition(), e); + } + } + + } finally { + cursor.close(); + } + } + + } + + } + + /** + * + * @param task + * @param threadPool + * @param indexResults + * @param database + * @param featureDao + * @param converter + * @param count + * @param maxFeatures + * @param editable + * @param filter + */ + private void processFeatureIndexResults(MapUpdateTask task, ExecutorService threadPool, FeatureIndexResults indexResults, String database, FeatureDao featureDao, + GoogleMapShapeConverter converter, AtomicInteger count, final int maxFeatures, final boolean editable, + boolean filter) { - // Query for all rows - FeatureCursor cursor = featureDao.queryForAll(); try { - while (!task.isCancelled() && count.get() < maxFeatures - && cursor.moveToNext()) { + for (FeatureRow row : indexResults) { + + if (task.isCancelled() || count.get() >= maxFeatures) { + break; + } + try { - FeatureRow row = cursor.getRow(); if (threadPool != null) { // Process the feature row in the thread pool FeatureRowProcessor processor = new FeatureRowProcessor( - task, database, featureDao, row, count, maxFeatures, editable); + task, database, featureDao, row, count, maxFeatures, editable, converter, + null, 0, filter); threadPool.execute(processor); } else { - processFeatureRow(task, database, featureDao, row, count, - maxFeatures, editable); + + processFeatureRow(task, database, featureDao, converter, row, count, maxFeatures, editable, null, 0, filter); } + } catch (Exception e) { Log.e(GeoPackageMapFragment.class.getSimpleName(), "Failed to display feature. database: " + database - + ", feature table: " + features - + ", row: " + cursor.getPosition(), e); + + ", feature table: " + featureDao.getTableName() + + ", row id: " + row.getId(), e); } } - } finally { - cursor.close(); + indexResults.close(); } } @@ -1828,6 +2096,14 @@ private class FeatureRowProcessor implements Runnable { */ private final boolean editable; + private final GoogleMapShapeConverter converter; + + private final BoundingBox filterBoundingBox; + + private final double maxLongitude; + + private final boolean filter; + /** * Constructor * @@ -1838,10 +2114,15 @@ private class FeatureRowProcessor implements Runnable { * @param count * @param maxFeatures * @param editable + * @param converter + * @param filterBoundingBox + * @param maxLongitude + * @param filter */ public FeatureRowProcessor(MapUpdateTask task, String database, FeatureDao featureDao, FeatureRow row, AtomicInteger count, int maxFeatures, - boolean editable) { + boolean editable, GoogleMapShapeConverter converter, + BoundingBox filterBoundingBox, double maxLongitude, boolean filter) { this.task = task; this.database = database; this.featureDao = featureDao; @@ -1849,6 +2130,10 @@ public FeatureRowProcessor(MapUpdateTask task, String database, FeatureDao featu this.count = count; this.maxFeatures = maxFeatures; this.editable = editable; + this.converter = converter; + this.filterBoundingBox = filterBoundingBox; + this.maxLongitude = maxLongitude; + this.filter = filter; } /** @@ -1856,8 +2141,8 @@ public FeatureRowProcessor(MapUpdateTask task, String database, FeatureDao featu */ @Override public void run() { - processFeatureRow(task, database, featureDao, row, count, maxFeatures, - editable); + processFeatureRow(task, database, featureDao, converter, row, count, maxFeatures, + editable, filterBoundingBox, maxLongitude, filter); } } @@ -1872,27 +2157,54 @@ public void run() { * @param count * @param maxFeatures * @param editable + * @param boundingBox + * @param maxLongitude + * @param filter */ private void processFeatureRow(MapUpdateTask task, String database, FeatureDao featureDao, - FeatureRow row, AtomicInteger count, int maxFeatures, - boolean editable) { - mil.nga.geopackage.projection.Projection projection = featureDao - .getProjection(); - final GoogleMapShapeConverter converter = new GoogleMapShapeConverter( - projection); - - final long featureId = row.getId(); - GeoPackageGeometryData geometryData = row.getGeometry(); - if (geometryData != null && !geometryData.isEmpty()) { - - final Geometry geometry = geometryData.getGeometry(); - - if (geometry != null) { - if (count.getAndIncrement() < maxFeatures) { - final GoogleMapShape shape = converter.toShape(geometry); - updateFeaturesBoundingBox(shape); - prepareShapeOptions(shape, editable, true); - task.addToMap(featureId, database, featureDao.getTableName(), shape); + GoogleMapShapeConverter converter, FeatureRow row, AtomicInteger count, + int maxFeatures, boolean editable, BoundingBox boundingBox, double maxLongitude, + boolean filter) { + + boolean exists = false; + synchronized (featureShapes) { + exists = featureShapes.exists(row.getId(), database, featureDao.getTableName()); + } + + if (!exists) { + + GeoPackageGeometryData geometryData = row.getGeometry(); + if (geometryData != null && !geometryData.isEmpty()) { + + final Geometry geometry = geometryData.getGeometry(); + + if (geometry != null) { + + boolean passesFilter = true; + + if (filter && boundingBox != null) { + GeometryEnvelope envelope = geometryData.getEnvelope(); + if (envelope == null) { + envelope = GeometryEnvelopeBuilder.buildEnvelope(geometry); + } + if (envelope != null) { + if (geometry.getGeometryType() == GeometryType.POINT) { + mil.nga.wkb.geom.Point point = (mil.nga.wkb.geom.Point) geometry; + passesFilter = TileBoundingBoxUtils.isPointInBoundingBox(point, boundingBox, maxLongitude); + } else { + BoundingBox geometryBoundingBox = new BoundingBox(envelope); + passesFilter = TileBoundingBoxUtils.overlap(boundingBox, geometryBoundingBox, maxLongitude) != null; + } + } + } + + if (passesFilter && count.getAndIncrement() < maxFeatures) { + final long featureId = row.getId(); + final GoogleMapShape shape = converter.toShape(geometry); + updateFeaturesBoundingBox(shape); + prepareShapeOptions(shape, editable, true); + task.addToMap(featureId, database, featureDao.getTableName(), shape); + } } } } @@ -2053,19 +2365,26 @@ private void setPolygonOptions(boolean editable, * * @param featureId * @param shape + * @return marker */ - private void addEditableShape(long featureId, GoogleMapShape shape) { + private Marker addEditableShape(long featureId, GoogleMapShape shape) { + + Marker marker = null; if (shape.getShapeType() == GoogleMapShapeType.MARKER) { - Marker marker = (Marker) shape.getShape(); - editFeatureIds.put(marker.getId(), featureId); + marker = (Marker) shape.getShape(); } else { - Marker marker = getMarker(shape); + marker = getMarker(shape); if (marker != null) { - editFeatureIds.put(marker.getId(), featureId); editFeatureObjects.put(marker.getId(), shape); } } + + if (marker != null) { + editFeatureIds.put(marker.getId(), featureId); + } + + return marker; } /** @@ -2231,8 +2550,7 @@ private void displayTiles(GeoPackageTileTable tiles) { private void displayFeatureTiles(GeoPackageFeatureOverlayTable featureOverlayTable) { GeoPackage geoPackage = geoPackages.get(featureOverlayTable.getDatabase()); - - FeatureDao featureDao = geoPackage.getFeatureDao(featureOverlayTable.getFeatureTable()); + FeatureDao featureDao = featureDaos.get(featureOverlayTable.getDatabase()).get(featureOverlayTable.getFeatureTable()); BoundingBox boundingBox = new BoundingBox(featureOverlayTable.getMinLon(), featureOverlayTable.getMaxLon(), featureOverlayTable.getMinLat(), featureOverlayTable.getMaxLat()); From 4375c8b7e9f9b22704ecb4d218f44e2a146b2183 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Wed, 11 Oct 2017 13:27:06 -0600 Subject: [PATCH 06/28] features update task --- .../nga/mapcache/GeoPackageMapFragment.java | 278 ++++++++++-------- 1 file changed, 163 insertions(+), 115 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 97911714..80ae96e9 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -40,7 +40,6 @@ import android.widget.RadioButton; import android.widget.Spinner; import android.widget.TextView; -import android.widget.Toast; import com.google.android.gms.maps.CameraUpdateFactory; import com.google.android.gms.maps.GoogleMap; @@ -212,6 +211,16 @@ public class GeoPackageMapFragment extends Fragment implements */ private MapUpdateTask updateTask; + /** + * Update features task + */ + private MapFeaturesUpdateTask updateFeaturesTask; + + /** + * Update lock + */ + private Lock updateLock = new ReentrantLock(); + /** * Mapping of open GeoPackages by name */ @@ -532,7 +541,20 @@ public void onCameraIdle(){ featureShapes.removeShapesNotWithinMap(map); } - updateInBackground(active.isModified()); + BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); + double toleranceDistance = MapUtils.getToleranceDistance(view, map); + int maxFeatures = getMaxFeatures(); + + updateLock.lock(); + try { + if(updateFeaturesTask != null){ + updateFeaturesTask.cancel(false); + } + updateFeaturesTask = new MapFeaturesUpdateTask(); + updateFeaturesTask.execute(false, maxFeatures, mapViewBoundingBox, toleranceDistance, true); + } finally { + updateLock.unlock(); + } } } @@ -1457,9 +1479,21 @@ private void setMapType(int mapType) { */ private void updateInBackground(boolean zoom) { - if (updateTask != null) { - updateTask.cancel(false); + MapUpdateTask localUpdateTask = null; + updateLock.lock(); + try { + if (updateTask != null) { + updateTask.cancel(false); + } + if(updateFeaturesTask != null){ + updateFeaturesTask.cancel(false); + } + updateTask = new MapUpdateTask(); + localUpdateTask = updateTask; + } finally { + updateLock.unlock(); } + map.clear(); for (GeoPackage geoPackage : geoPackages.values()) { try { @@ -1476,25 +1510,19 @@ private void updateInBackground(boolean zoom) { featureOverlayQueries.clear(); featureShapes.clear(); markerIds.clear(); - updateTask = new MapUpdateTask(); int maxFeatures = getMaxFeatures(); BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); double toleranceDistance = MapUtils.getToleranceDistance(view, map); - updateTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, true); // TODO when to filter? + localUpdateTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, false); } /** * Update the map in the background */ - private class MapUpdateTask extends AsyncTask { - - /** - * Updating tiles and features toast - */ - private Toast updateToast; + private class MapUpdateTask extends AsyncTask { /** * Zoom after update flag @@ -1521,105 +1549,18 @@ private class MapUpdateTask extends AsyncTask { */ private boolean filter; - /** - * Update start time - */ - private Date startTime; - - /** - * {@inheritDoc} - */ - @Override - protected void onPreExecute() { - updateToast = Toast.makeText(getActivity(), - "Updating Tiles and Features", Toast.LENGTH_LONG); - updateToast.show(); - startTime = new Date(); - } - /** * {@inheritDoc} */ @Override - protected Integer doInBackground(Object... params) { + protected Void doInBackground(Object... params) { zoom = (Boolean) params[0]; maxFeatures = (Integer) params[1]; mapViewBoundingBox = (BoundingBox) params[2]; toleranceDistance = (Double) params[3]; filter = (Boolean) params[4]; - int count = update(this, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); - return count; - } - - /** - * {@inheritDoc} - */ - @Override - protected void onProgressUpdate(Object... shapeUpdate) { - - long featureId = (Long) shapeUpdate[0]; - String database = (String) shapeUpdate[1]; - String tableName = (String) shapeUpdate[2]; - GoogleMapShape shape = (GoogleMapShape) shapeUpdate[3]; - - synchronized (featureShapes) { - - if (!featureShapes.exists(featureId, database, tableName)) { - - GoogleMapShape mapShape = GoogleMapShapeConverter.addShapeToMap( - map, shape); - - if (editFeaturesMode) { - Marker marker = addEditableShape(featureId, mapShape); - if (marker != null) { - GoogleMapShape mapPointShape = new GoogleMapShape(GeometryType.POINT, GoogleMapShapeType.MARKER, marker); - featureShapes.addMapShape(mapPointShape, featureId, database, tableName); - } - } else { - addMarkerShape(featureId, database, tableName, mapShape); - } - featureShapes.addMapShape(mapShape, featureId, database, tableName); - } - } - } - - /** - * {@inheritDoc} - */ - @Override - protected void onPostExecute(Integer count) { - Date stopTime = new Date(); - DecimalFormat format = new DecimalFormat("0.##"); - String time = format.format((stopTime.getTime() - startTime - .getTime()) / 1000.0); - updateToast.cancel(); - if (count > 0) { - if (count >= maxFeatures) { - Toast.makeText( - getActivity(), - "Max Features Drawn: " + count + " (" + time - + " sec)", Toast.LENGTH_SHORT).show(); - } else { - Toast.makeText(getActivity(), - "Features Drawn: " + count + " (" + time + " sec)", - Toast.LENGTH_SHORT).show(); - } - } - if (zoom) { - zoomToActive(true); - } - } - - /** - * Add a shape to the map - * - * @param featureId - * @param database - * @param tableName - * @param shape - */ - public void addToMap(long featureId, String database, String tableName, GoogleMapShape shape) { - publishProgress(new Object[]{featureId, database, tableName, shape}); + update(this, zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); + return null; } } @@ -1627,16 +1568,14 @@ public void addToMap(long featureId, String database, String tableName, GoogleMa /** * Update the map * + * @param zoom * @param task * @param maxFeatures * @param mapViewBoundingBox * @param toleranceDistance * @param filter - * @return feature count */ - private int update(MapUpdateTask task, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { - - int count = 0; + private void update(MapUpdateTask task, boolean zoom, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { if (active != null) { @@ -1718,25 +1657,134 @@ private int update(MapUpdateTask task, final int maxFeatures, BoundingBox mapVie // Add features if (!task.isCancelled()) { - count = addFeatures(task, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); + updateLock.lock(); + try { + if(updateFeaturesTask != null){ + updateFeaturesTask.cancel(false); + } + updateFeaturesTask = new MapFeaturesUpdateTask(); + updateFeaturesTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); + } finally { + updateLock.unlock(); + } + } + + } + + } + + /** + * Update the map features in the background + */ + private class MapFeaturesUpdateTask extends AsyncTask { + + /** + * Zoom after update flag + */ + private boolean zoom; + + /** + * Max features to draw + */ + private int maxFeatures; + + /** + * Map view bounding box + */ + private BoundingBox mapViewBoundingBox; + + /** + * Tolerance distance for simplification + */ + private double toleranceDistance; + + /** + * Filter flag + */ + private boolean filter; + + /** + * {@inheritDoc} + */ + @Override + protected Integer doInBackground(Object... params) { + zoom = (Boolean) params[0]; + maxFeatures = (Integer) params[1]; + mapViewBoundingBox = (BoundingBox) params[2]; + toleranceDistance = (Double) params[3]; + filter = (Boolean) params[4]; + int count = addFeatures(this, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); + return count; + } + + /** + * {@inheritDoc} + */ + @Override + protected void onProgressUpdate(Object... shapeUpdate) { + + long featureId = (Long) shapeUpdate[0]; + String database = (String) shapeUpdate[1]; + String tableName = (String) shapeUpdate[2]; + GoogleMapShape shape = (GoogleMapShape) shapeUpdate[3]; + + synchronized (featureShapes) { + + if (!featureShapes.exists(featureId, database, tableName)) { + + GoogleMapShape mapShape = GoogleMapShapeConverter.addShapeToMap( + map, shape); + + if (editFeaturesMode) { + Marker marker = addEditableShape(featureId, mapShape); + if (marker != null) { + GoogleMapShape mapPointShape = new GoogleMapShape(GeometryType.POINT, GoogleMapShapeType.MARKER, marker); + featureShapes.addMapShape(mapPointShape, featureId, database, tableName); + } + } else { + addMarkerShape(featureId, database, tableName, mapShape); + } + featureShapes.addMapShape(mapShape, featureId, database, tableName); + } + } + } + + /** + * {@inheritDoc} + */ + @Override + protected void onPostExecute(Integer count) { + + if (zoom) { + zoomToActive(true); } + } + /** + * Add a shape to the map + * + * @param featureId + * @param database + * @param tableName + * @param shape + */ + public void addToMap(long featureId, String database, String tableName, GoogleMapShape shape) { + publishProgress(new Object[]{featureId, database, tableName, shape}); } - return count; } /** * Add features to the map * - * @param task udpate task + * @param task udpate features task * @param maxFeatures max features * @param mapViewBoundingBox map view bounding box * @param toleranceDistance tolerance distance * @param filter filter * @return feature count */ - private int addFeatures(MapUpdateTask task, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { + private int addFeatures(MapFeaturesUpdateTask task, final int maxFeatures, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { AtomicInteger count = new AtomicInteger(); @@ -1924,7 +1972,7 @@ private void zoomToActive(boolean nothingVisible) { * @param toleranceDistance * @param filter */ - private void displayFeatures(MapUpdateTask task, + private void displayFeatures(MapFeaturesUpdateTask task, ExecutorService threadPool, String database, String features, AtomicInteger count, final int maxFeatures, final boolean editable, BoundingBox mapViewBoundingBox, double toleranceDistance, boolean filter) { @@ -2018,7 +2066,7 @@ private void displayFeatures(MapUpdateTask task, * @param editable * @param filter */ - private void processFeatureIndexResults(MapUpdateTask task, ExecutorService threadPool, FeatureIndexResults indexResults, String database, FeatureDao featureDao, + private void processFeatureIndexResults(MapFeaturesUpdateTask task, ExecutorService threadPool, FeatureIndexResults indexResults, String database, FeatureDao featureDao, GoogleMapShapeConverter converter, AtomicInteger count, final int maxFeatures, final boolean editable, boolean filter) { @@ -2064,7 +2112,7 @@ private class FeatureRowProcessor implements Runnable { /** * Map update task */ - private final MapUpdateTask task; + private final MapFeaturesUpdateTask task; /** * Database @@ -2119,7 +2167,7 @@ private class FeatureRowProcessor implements Runnable { * @param maxLongitude * @param filter */ - public FeatureRowProcessor(MapUpdateTask task, String database, FeatureDao featureDao, + public FeatureRowProcessor(MapFeaturesUpdateTask task, String database, FeatureDao featureDao, FeatureRow row, AtomicInteger count, int maxFeatures, boolean editable, GoogleMapShapeConverter converter, BoundingBox filterBoundingBox, double maxLongitude, boolean filter) { @@ -2161,7 +2209,7 @@ public void run() { * @param maxLongitude * @param filter */ - private void processFeatureRow(MapUpdateTask task, String database, FeatureDao featureDao, + private void processFeatureRow(MapFeaturesUpdateTask task, String database, FeatureDao featureDao, GoogleMapShapeConverter converter, FeatureRow row, AtomicInteger count, int maxFeatures, boolean editable, BoundingBox boundingBox, double maxLongitude, boolean filter) { From b0df229be9fc2826456ce60286c0958ea96bab4f Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Thu, 12 Oct 2017 08:26:25 -0600 Subject: [PATCH 07/28] for non indexed filtered features, handle bounding, transforming, and expanding bounding box --- .../nga/mapcache/GeoPackageMapFragment.java | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 80ae96e9..27c21669 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -2006,15 +2006,21 @@ private void displayFeatures(MapFeaturesUpdateTask task, } else { - mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); - ProjectionTransform projectionTransform = mapViewProjection.getTransformation(featureProjection); - BoundingBox filterBoundingBox = projectionTransform.transform(mapViewBoundingBox); + BoundingBox filterBoundingBox = null; double filterMaxLongitude = 0; - Unit unit = featureProjection.getUnit(); - if (unit instanceof DegreeUnit) { - filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; - } else if (unit == Units.METRES) { - filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; + + if(filter) { + mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); + ProjectionTransform projectionTransform = mapViewProjection.getTransformation(featureProjection); + BoundingBox boundedMapViewBoundingBox = mapViewBoundingBox.boundWgs84Coordinates(); + BoundingBox transformedBoundingBox = projectionTransform.transform(boundedMapViewBoundingBox); + Unit unit = featureProjection.getUnit(); + if (unit instanceof DegreeUnit) { + filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; + } else if (unit == Units.METRES) { + filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; + } + filterBoundingBox = transformedBoundingBox.expandCoordinates(filterMaxLongitude); } // Query for all rows From b6ff7557801e105eeceb5cff5b89ef97f01796f8 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Thu, 12 Oct 2017 13:59:05 -0600 Subject: [PATCH 08/28] handle initial zoom and zooming when not already on or zoomed close enough to visible features or tiles --- .../nga/mapcache/GeoPackageMapFragment.java | 69 +++++++++++++++---- mapcache/src/main/res/values/integers.xml | 2 + 2 files changed, 58 insertions(+), 13 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 27c21669..e4d761ab 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -115,6 +115,7 @@ import mil.nga.geopackage.map.geom.PolygonHoleMarkers; import mil.nga.geopackage.map.geom.ShapeMarkers; import mil.nga.geopackage.map.geom.ShapeWithChildrenMarkers; +import mil.nga.geopackage.map.tiles.TileBoundingBoxMapUtils; import mil.nga.geopackage.map.tiles.overlay.BoundedOverlay; import mil.nga.geopackage.map.tiles.overlay.FeatureOverlay; import mil.nga.geopackage.map.tiles.overlay.FeatureOverlayQuery; @@ -124,6 +125,7 @@ import mil.nga.geopackage.projection.ProjectionTransform; import mil.nga.geopackage.schema.columns.DataColumns; import mil.nga.geopackage.schema.columns.DataColumnsDao; +import mil.nga.geopackage.tiles.TileBoundingBoxAndroidUtils; import mil.nga.geopackage.tiles.TileBoundingBoxUtils; import mil.nga.geopackage.tiles.features.DefaultFeatureTiles; import mil.nga.geopackage.tiles.features.FeatureTiles; @@ -217,7 +219,7 @@ public class GeoPackageMapFragment extends Fragment implements private MapFeaturesUpdateTask updateFeaturesTask; /** - * Update lock + * Update lock for creating and cancelling update tasks */ private Lock updateLock = new ReentrantLock(); @@ -306,6 +308,11 @@ public class GeoPackageMapFragment extends Fragment implements */ private int currentZoom = -1; + /** + * Flag indicating if the initial zoom is still needed + */ + private boolean needsInitialZoom = true; + /** * Mapping between marker ids and the feature ids */ @@ -498,12 +505,18 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, return touch; } + /** + * {@inheritDoc} + */ @Override public void onMapReady(GoogleMap googleMap) { map = googleMap; initializeMap(); } + /** + * Initialize the map + */ private void initializeMap() { if (map == null) return; @@ -524,19 +537,22 @@ private void initializeMap() { } + /** + * {@inheritDoc} + */ @Override - public void onCameraIdle(){ + public void onCameraIdle() { // If visible & not editing a shape, update the feature shapes for the current map view region - if(visible && (!editFeaturesMode || editFeatureType == null || (editPoints.isEmpty() && editFeatureMarker == null))){ + if (visible && (!editFeaturesMode || editFeatureType == null || (editPoints.isEmpty() && editFeatureMarker == null))) { int previousZoom = currentZoom; int zoom = (int) MapUtils.getCurrentZoom(map); currentZoom = zoom; - if(zoom != previousZoom){ + if (zoom != previousZoom) { // Zoom level changed, remove all feature shapes featureShapes.removeShapes(); - }else{ + } else { // Remove shapes no longer visible on the map view featureShapes.removeShapesNotWithinMap(map); } @@ -547,7 +563,7 @@ public void onCameraIdle(){ updateLock.lock(); try { - if(updateFeaturesTask != null){ + if (updateFeaturesTask != null) { updateFeaturesTask.cancel(false); } updateFeaturesTask = new MapFeaturesUpdateTask(); @@ -1485,7 +1501,7 @@ private void updateInBackground(boolean zoom) { if (updateTask != null) { updateTask.cancel(false); } - if(updateFeaturesTask != null){ + if (updateFeaturesTask != null) { updateFeaturesTask.cancel(false); } updateTask = new MapUpdateTask(); @@ -1659,7 +1675,7 @@ private void update(MapUpdateTask task, boolean zoom, final int maxFeatures, Bou if (!task.isCancelled()) { updateLock.lock(); try { - if(updateFeaturesTask != null){ + if (updateFeaturesTask != null) { updateFeaturesTask.cancel(false); } updateFeaturesTask = new MapFeaturesUpdateTask(); @@ -1755,8 +1771,9 @@ protected void onProgressUpdate(Object... shapeUpdate) { @Override protected void onPostExecute(Integer count) { - if (zoom) { + if (needsInitialZoom || zoom) { zoomToActive(true); + needsInitialZoom = false; } } @@ -1897,10 +1914,12 @@ private void zoomToActive() { private void zoomToActive(boolean nothingVisible) { BoundingBox bbox = featuresBoundingBox; + boolean tileBox = false; float paddingPercentage; if (bbox == null) { bbox = tilesBoundingBox; + tileBox = true; if (featureOverlayTiles) { paddingPercentage = getActivity().getResources().getInteger( R.integer.map_feature_tiles_zoom_padding_percentage) * .01f; @@ -1918,7 +1937,32 @@ private void zoomToActive(boolean nothingVisible) { boolean zoomToActive = true; if (nothingVisible) { BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); - zoomToActive = TileBoundingBoxUtils.overlap(bbox, mapViewBoundingBox, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) != null; + if(TileBoundingBoxUtils.overlap(bbox, mapViewBoundingBox, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) != null){ + + double longitudeDistance = TileBoundingBoxMapUtils.getLongitudeDistance(bbox); + double latitudeDistance = TileBoundingBoxMapUtils.getLatitudeDistance(bbox); + double mapViewLongitudeDistance = TileBoundingBoxMapUtils.getLongitudeDistance(mapViewBoundingBox); + double mapViewLatitudeDistance = TileBoundingBoxMapUtils.getLatitudeDistance(mapViewBoundingBox); + + if(mapViewLongitudeDistance > longitudeDistance && mapViewLatitudeDistance > latitudeDistance){ + + double longitudeRatio = longitudeDistance / mapViewLongitudeDistance; + double latitudeRatio = latitudeDistance / mapViewLatitudeDistance; + + double zoomAlreadyVisiblePercentage; + if(tileBox){ + zoomAlreadyVisiblePercentage = getActivity().getResources().getInteger( + R.integer.map_tiles_zoom_already_visible_percentage) * .01f; + }else{ + zoomAlreadyVisiblePercentage = getActivity().getResources().getInteger( + R.integer.map_features_zoom_already_visible_percentage) * .01f; + } + + if(longitudeRatio >= zoomAlreadyVisiblePercentage && latitudeRatio >= zoomAlreadyVisiblePercentage){ + zoomToActive = false; + } + } + } } if (zoomToActive) { @@ -2009,7 +2053,7 @@ private void displayFeatures(MapFeaturesUpdateTask task, BoundingBox filterBoundingBox = null; double filterMaxLongitude = 0; - if(filter) { + if (filter) { mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); ProjectionTransform projectionTransform = mapViewProjection.getTransformation(featureProjection); BoundingBox boundedMapViewBoundingBox = mapViewBoundingBox.boundWgs84Coordinates(); @@ -2040,7 +2084,7 @@ private void displayFeatures(MapFeaturesUpdateTask task, } else { processFeatureRow(task, database, featureDao, converter, row, count, maxFeatures, editable, - filterBoundingBox, filterMaxLongitude, filter); + filterBoundingBox, filterMaxLongitude, filter); } } catch (Exception e) { Log.e(GeoPackageMapFragment.class.getSimpleName(), @@ -2060,7 +2104,6 @@ private void displayFeatures(MapFeaturesUpdateTask task, } /** - * * @param task * @param threadPool * @param indexResults diff --git a/mapcache/src/main/res/values/integers.xml b/mapcache/src/main/res/values/integers.xml index cd0d558b..241b6cc6 100644 --- a/mapcache/src/main/res/values/integers.xml +++ b/mapcache/src/main/res/values/integers.xml @@ -5,7 +5,9 @@ 0 10000 10 + 1 0 + 65 10 0 21 From 8b414517c556ff0519d780c58f68b749d078bc24 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Thu, 12 Oct 2017 16:51:36 -0600 Subject: [PATCH 09/28] map loaded check, initial zoom to declared bounds, using tile matrix set bounds over contents --- .../nga/mapcache/GeoPackageMapFragment.java | 207 ++++++++++++++---- 1 file changed, 167 insertions(+), 40 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index e4d761ab..7ed8b780 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -91,6 +91,7 @@ import mil.nga.geopackage.GeoPackageManager; import mil.nga.geopackage.core.contents.Contents; import mil.nga.geopackage.core.contents.ContentsDao; +import mil.nga.geopackage.core.srs.SpatialReferenceSystem; import mil.nga.geopackage.extension.link.FeatureTileTableLinker; import mil.nga.geopackage.factory.GeoPackageFactory; import mil.nga.geopackage.features.columns.GeometryColumns; @@ -125,12 +126,12 @@ import mil.nga.geopackage.projection.ProjectionTransform; import mil.nga.geopackage.schema.columns.DataColumns; import mil.nga.geopackage.schema.columns.DataColumnsDao; -import mil.nga.geopackage.tiles.TileBoundingBoxAndroidUtils; import mil.nga.geopackage.tiles.TileBoundingBoxUtils; import mil.nga.geopackage.tiles.features.DefaultFeatureTiles; import mil.nga.geopackage.tiles.features.FeatureTiles; import mil.nga.geopackage.tiles.features.custom.NumberFeaturesTile; import mil.nga.geopackage.tiles.matrixset.TileMatrixSet; +import mil.nga.geopackage.tiles.matrixset.TileMatrixSetDao; import mil.nga.geopackage.tiles.user.TileDao; import mil.nga.mapcache.data.GeoPackageDatabase; import mil.nga.mapcache.data.GeoPackageDatabases; @@ -178,6 +179,11 @@ public class GeoPackageMapFragment extends Fragment implements */ private GoogleMap map; + /** + * Map loaded flag + */ + private boolean mapLoaded = false; + /** * View */ @@ -535,6 +541,13 @@ private void initializeMap() { map.setOnCameraIdleListener(this); map.getUiSettings().setZoomControlsEnabled(true); + map.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() { + @Override + public void onMapLoaded() { + updateInBackground(true); + mapLoaded = true; + } + }); } /** @@ -1136,7 +1149,29 @@ public void onClick(DialogInterface dialog, int which) { active.setModified(false); resetBoundingBox(); resetEditFeatures(); - updateInBackground(true); + if (mapLoaded) { + updateInBackground(true); + } + } else if (!visible) { + updateLock.lock(); + try { + if (updateTask != null) { + if (updateTask.getStatus() != AsyncTask.Status.FINISHED) { + updateTask.cancel(false); + active.setModified(true); + } + updateTask = null; + } + if (updateFeaturesTask != null) { + if (updateFeaturesTask.getStatus() != AsyncTask.Status.FINISHED) { + updateFeaturesTask.cancel(false); + active.setModified(true); + } + updateFeaturesTask = null; + } + } finally { + updateLock.unlock(); + } } } @@ -1520,6 +1555,11 @@ private void updateInBackground(boolean zoom) { } geoPackages.clear(); featureDaos.clear(); + + if (zoom) { + zoomToActiveBounds(); + } + featuresBoundingBox = null; tilesBoundingBox = null; featureOverlayTiles = false; @@ -1535,6 +1575,118 @@ private void updateInBackground(boolean zoom) { } + /** + * Zoom to the active feature and tile table data bounds + */ + private void zoomToActiveBounds() { + + featuresBoundingBox = null; + tilesBoundingBox = null; + + // Pre zoom + List activeDatabases = new ArrayList<>(); + activeDatabases.addAll(active.getDatabases()); + for (GeoPackageDatabase database : activeDatabases) { + GeoPackage geoPackage = manager.open(database.getDatabase()); + if (geoPackage != null) { + + Set featureTableDaos = new HashSet<>(); + Collection features = database.getFeatures(); + if (!features.isEmpty()) { + for (GeoPackageFeatureTable featureTable : features) { + featureTableDaos.add(featureTable.getName()); + } + } + + for (GeoPackageFeatureOverlayTable featureOverlay : database.getFeatureOverlays()) { + if (featureOverlay.isActive()) { + featureTableDaos.add(featureOverlay.getFeatureTable()); + } + } + + if (!featureTableDaos.isEmpty()) { + + ContentsDao contentsDao = geoPackage.getContentsDao(); + + for (String featureTable : featureTableDaos) { + + try { + Contents contents = contentsDao.queryForId(featureTable); + BoundingBox contentsBoundingBox = contents.getBoundingBox(); + + contentsBoundingBox = transformBoundingBoxToWgs84(contentsBoundingBox, contents.getSrs()); + + if (featuresBoundingBox != null) { + featuresBoundingBox = TileBoundingBoxUtils.union(featuresBoundingBox, contentsBoundingBox); + } else { + featuresBoundingBox = contentsBoundingBox; + } + } catch (SQLException e) { + Log.e(GeoPackageMapFragment.class.getSimpleName(), + e.getMessage()); + } + } + } + + Collection tileTables = database.getTiles(); + if (!tileTables.isEmpty()) { + + TileMatrixSetDao tileMatrixSetDao = geoPackage.getTileMatrixSetDao(); + + for (GeoPackageTileTable tileTable : database.getTiles()) { + + try { + TileMatrixSet tileMatrixSet = tileMatrixSetDao.queryForId(tileTable.getName()); + BoundingBox tileMatrixSetBoundingBox = tileMatrixSet.getBoundingBox(); + + tileMatrixSetBoundingBox = transformBoundingBoxToWgs84(tileMatrixSetBoundingBox, tileMatrixSet.getSrs()); + + if (tilesBoundingBox != null) { + tilesBoundingBox = TileBoundingBoxUtils.union(tilesBoundingBox, tileMatrixSetBoundingBox); + } else { + tilesBoundingBox = tileMatrixSetBoundingBox; + } + } catch (SQLException e) { + Log.e(GeoPackageMapFragment.class.getSimpleName(), + e.getMessage()); + } + } + } + + geoPackage.close(); + } + } + + zoomToActive(); + } + + /** + * Transform the bounding box in the spatial reference to a WGS84 bounding box + * + * @param boundingBox bounding box + * @param srs spatial reference system + * @return bounding box + */ + private BoundingBox transformBoundingBoxToWgs84(BoundingBox boundingBox, SpatialReferenceSystem srs) { + + mil.nga.geopackage.projection.Projection projection = ProjectionFactory.getProjection( + srs); + if (projection.getUnit() instanceof DegreeUnit) { + boundingBox = TileBoundingBoxUtils.boundDegreesBoundingBoxWithWebMercatorLimits(boundingBox); + } + ProjectionTransform transformToWebMercator = projection + .getTransformation( + ProjectionConstants.EPSG_WEB_MERCATOR); + BoundingBox webMercatorBoundingBox = transformToWebMercator.transform(boundingBox); + ProjectionTransform transform = ProjectionFactory.getProjection( + ProjectionConstants.EPSG_WEB_MERCATOR) + .getTransformation( + ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); + boundingBox = transform + .transform(webMercatorBoundingBox); + return boundingBox; + } + /** * Update the map in the background */ @@ -1937,28 +2089,28 @@ private void zoomToActive(boolean nothingVisible) { boolean zoomToActive = true; if (nothingVisible) { BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); - if(TileBoundingBoxUtils.overlap(bbox, mapViewBoundingBox, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) != null){ + if (TileBoundingBoxUtils.overlap(bbox, mapViewBoundingBox, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH) != null) { double longitudeDistance = TileBoundingBoxMapUtils.getLongitudeDistance(bbox); double latitudeDistance = TileBoundingBoxMapUtils.getLatitudeDistance(bbox); double mapViewLongitudeDistance = TileBoundingBoxMapUtils.getLongitudeDistance(mapViewBoundingBox); double mapViewLatitudeDistance = TileBoundingBoxMapUtils.getLatitudeDistance(mapViewBoundingBox); - if(mapViewLongitudeDistance > longitudeDistance && mapViewLatitudeDistance > latitudeDistance){ + if (mapViewLongitudeDistance > longitudeDistance && mapViewLatitudeDistance > latitudeDistance) { double longitudeRatio = longitudeDistance / mapViewLongitudeDistance; double latitudeRatio = latitudeDistance / mapViewLatitudeDistance; double zoomAlreadyVisiblePercentage; - if(tileBox){ + if (tileBox) { zoomAlreadyVisiblePercentage = getActivity().getResources().getInteger( R.integer.map_tiles_zoom_already_visible_percentage) * .01f; - }else{ + } else { zoomAlreadyVisiblePercentage = getActivity().getResources().getInteger( R.integer.map_features_zoom_already_visible_percentage) * .01f; } - if(longitudeRatio >= zoomAlreadyVisiblePercentage && latitudeRatio >= zoomAlreadyVisiblePercentage){ + if (longitudeRatio >= zoomAlreadyVisiblePercentage && latitudeRatio >= zoomAlreadyVisiblePercentage) { zoomToActive = false; } } @@ -2608,7 +2760,6 @@ private void displayTiles(GeoPackageTileTable tiles) { .getBoundedOverlay(tileDao); TileMatrixSet tileMatrixSet = tileDao.getTileMatrixSet(); - Contents contents = tileMatrixSet.getContents(); FeatureTileTableLinker linker = new FeatureTileTableLinker(geoPackage); List featureDaos = linker.getFeatureDaosForTileTable(tileDao.getTableName()); @@ -2636,7 +2787,7 @@ private void displayTiles(GeoPackageTileTable tiles) { zIndex = -1; } - displayTiles(overlay, contents, zIndex, null); + displayTiles(overlay, tileMatrixSet.getBoundingBox(), tileMatrixSet.getSrs(), zIndex, null); } /** @@ -2710,40 +2861,27 @@ private void displayFeatureTiles(GeoPackageFeatureOverlayTable featureOverlayTab FeatureOverlayQuery featureOverlayQuery = new FeatureOverlayQuery(getActivity(), overlay); featureOverlayQueries.add(featureOverlayQuery); - displayTiles(overlay, contents, -1, boundingBox); + displayTiles(overlay, contents.getBoundingBox(), contents.getSrs(), -1, boundingBox); } /** * Display tiles * * @param overlay - * @param contents + * @param dataBoundingBox + * @param srs * @param zIndex * @param specifiedBoundingBox */ - private void displayTiles(TileProvider overlay, Contents contents, int zIndex, BoundingBox specifiedBoundingBox) { + private void displayTiles(TileProvider overlay, BoundingBox dataBoundingBox, SpatialReferenceSystem srs, int zIndex, BoundingBox specifiedBoundingBox) { final TileOverlayOptions overlayOptions = new TileOverlayOptions(); overlayOptions.tileProvider(overlay); overlayOptions.zIndex(zIndex); - BoundingBox boundingBox = contents.getBoundingBox(); + BoundingBox boundingBox = dataBoundingBox; if (boundingBox != null) { - mil.nga.geopackage.projection.Projection projection = ProjectionFactory.getProjection( - contents.getSrs()); - if (projection.getUnit() instanceof DegreeUnit) { - boundingBox = TileBoundingBoxUtils.boundDegreesBoundingBoxWithWebMercatorLimits(boundingBox); - } - ProjectionTransform transformToWebMercator = projection - .getTransformation( - ProjectionConstants.EPSG_WEB_MERCATOR); - BoundingBox webMercatorBoundingBox = transformToWebMercator.transform(boundingBox); - ProjectionTransform transform = ProjectionFactory.getProjection( - ProjectionConstants.EPSG_WEB_MERCATOR) - .getTransformation( - ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); - boundingBox = transform - .transform(webMercatorBoundingBox); + boundingBox = transformBoundingBoxToWgs84(boundingBox, srs); } else { boundingBox = new BoundingBox(-ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, @@ -2758,18 +2896,7 @@ private void displayTiles(TileProvider overlay, Contents contents, int zIndex, B if (tilesBoundingBox == null) { tilesBoundingBox = boundingBox; } else { - tilesBoundingBox.setMinLongitude(Math.min( - tilesBoundingBox.getMinLongitude(), - boundingBox.getMinLongitude())); - tilesBoundingBox.setMaxLongitude(Math.max( - tilesBoundingBox.getMaxLongitude(), - boundingBox.getMaxLongitude())); - tilesBoundingBox.setMinLatitude(Math.min( - tilesBoundingBox.getMinLatitude(), - boundingBox.getMinLatitude())); - tilesBoundingBox.setMaxLatitude(Math.max( - tilesBoundingBox.getMaxLatitude(), - boundingBox.getMaxLatitude())); + tilesBoundingBox = TileBoundingBoxUtils.union(tilesBoundingBox, boundingBox); } getActivity().runOnUiThread(new Runnable() { From 04ef4bcb3802edd5b49067e59547a13cc2045600 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 06:57:58 -0600 Subject: [PATCH 10/28] error handling fix to use table name instead of null edit feature type --- .../src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 7ed8b780..e03b568b 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1078,7 +1078,7 @@ private void saveEditFeatures() { .showMessage( getActivity(), getString(R.string.edit_features_save_label) - + " " + editFeatureType.name(), + + " " + editFeaturesTable, "GeoPackage was created using a more recent SQLite version unsupported by Android"); } else { GeoPackageUtils.showMessage(getActivity(), From b8d0ba91d966fa4441eae744888b66b93d8d81e3 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 08:19:33 -0600 Subject: [PATCH 11/28] additional is active exist methods for databases and tables --- .../nga/mapcache/data/GeoPackageDatabase.java | 46 +++++++++++++------ .../mapcache/data/GeoPackageDatabases.java | 39 +++++++++++----- 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabase.java b/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabase.java index 7d7b899d..26fc6627 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabase.java +++ b/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabase.java @@ -51,9 +51,10 @@ public Collection getFeatures() { /** * Get the feature count + * * @return */ - public int getFeatureCount(){ + public int getFeatureCount() { return features.size(); } @@ -68,20 +69,22 @@ public Collection getFeatureOverlays() { /** * Get the feature overlay count + * * @return */ - public int getFeatureOverlayCount(){ + public int getFeatureOverlayCount() { return featureOverlays.size(); } /** * Get the feature overlay count + * * @return */ - public int getActiveFeatureOverlayCount(){ + public int getActiveFeatureOverlayCount() { int count = 0; - for(GeoPackageTable table : featureOverlays.values()){ - if(table.isActive()){ + for (GeoPackageTable table : featureOverlays.values()) { + if (table.isActive()) { count++; } } @@ -100,23 +103,25 @@ public Collection getTiles() { /** * Get the tile count */ - public int getTileCount(){ + public int getTileCount() { return tiles.size(); } /** * Get the table count + * * @return */ - public int getTableCount(){ + public int getTableCount() { return getFeatureCount() + getTileCount() + getFeatureOverlayCount(); } /** * Get the active table count + * * @return */ - public int getActiveTableCount(){ + public int getActiveTableCount() { return getFeatureCount() + getTileCount() + getActiveFeatureOverlayCount(); } @@ -131,29 +136,40 @@ public String getDatabase() { /** * Check if the table exists in this table, is active * - * @param table - * @return + * @param table table + * @return true if exists */ public boolean exists(GeoPackageTable table) { + return exists(table.getName(), table.getType()); + } + + /** + * Check if the table name of type exists, is active + * + * @param table table name + * @param tableType table type + * @return true if exists + */ + public boolean exists(String table, GeoPackageTableType tableType) { boolean exists = false; - switch (table.getType()) { + switch (tableType) { case FEATURE: - exists = features.containsKey(table.getName()); + exists = features.containsKey(table); break; case TILE: - exists = tiles.containsKey(table.getName()); + exists = tiles.containsKey(table); break; case FEATURE_OVERLAY: - exists = featureOverlays.containsKey(table.getName()); + exists = featureOverlays.containsKey(table); break; default: - throw new IllegalArgumentException("Unsupported table type: " + table.getType()); + throw new IllegalArgumentException("Unsupported table type: " + tableType); } return exists; diff --git a/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabases.java b/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabases.java index 6f23e74e..3dd6e3aa 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabases.java +++ b/mapcache/src/main/java/mil/nga/mapcache/data/GeoPackageDatabases.java @@ -126,6 +126,23 @@ public boolean exists(GeoPackageTable table) { return exists; } + /** + * Check if the database, table, and type exists, is active + * + * @param database database name + * @param table table name + * @param tableType table type + * @return true if exists + */ + public boolean exists(String database, String table, GeoPackageTableType tableType) { + boolean exists = false; + GeoPackageDatabase db = getDatabase(database); + if (db != null) { + exists = db.exists(table, tableType); + } + return exists; + } + /** * Get feature overlays for the database * @@ -227,14 +244,14 @@ public void removeTable(GeoPackageTable table, boolean preserveOverlays) { databases.remove(database.getDatabase()); removeDatabaseFromPreferences(database.getDatabase(), preserveOverlays); } - if(!preserveOverlays && table.getType() == GeoPackageTableType.FEATURE){ + if (!preserveOverlays && table.getType() == GeoPackageTableType.FEATURE) { List deleteFeatureOverlays = new ArrayList(); - for(GeoPackageFeatureOverlayTable featureOverlay: database.getFeatureOverlays()){ - if(featureOverlay.getFeatureTable().equals(table.getName())){ + for (GeoPackageFeatureOverlayTable featureOverlay : database.getFeatureOverlays()) { + if (featureOverlay.getFeatureTable().equals(table.getName())) { deleteFeatureOverlays.add(featureOverlay); } } - for(GeoPackageFeatureOverlayTable featureOverlay: deleteFeatureOverlays){ + for (GeoPackageFeatureOverlayTable featureOverlay : deleteFeatureOverlays) { removeTable(featureOverlay); } } @@ -242,21 +259,21 @@ public void removeTable(GeoPackageTable table, boolean preserveOverlays) { } } - public boolean isEmpty(){ + public boolean isEmpty() { return getTableCount() == 0; } - public int getTableCount(){ + public int getTableCount() { int count = 0; - for(GeoPackageDatabase database : databases.values()){ + for (GeoPackageDatabase database : databases.values()) { count += database.getTableCount(); } return count; } - public int getActiveTableCount(){ + public int getActiveTableCount() { int count = 0; - for(GeoPackageDatabase database : databases.values()){ + for (GeoPackageDatabase database : databases.values()) { count += database.getActiveTableCount(); } return count; @@ -271,7 +288,7 @@ public void clearActive() { for (String database : allDatabases) { GeoPackageDatabase db = databases.get(database); for (GeoPackageTable table : db.getFeatureOverlays()) { - if(table.isActive()) { + if (table.isActive()) { table.setActive(false); addTable(table, true); } @@ -374,7 +391,7 @@ private void removeDatabaseFromPreferences(String database, boolean preserveOver } editor.remove(getTileTablesPreferenceKey(database)); editor.remove(getFeatureTablesPreferenceKey(database)); - if(!preserveOverlays) { + if (!preserveOverlays) { editor.remove(getFeatureOverlayTablesPreferenceKey(database)); deleteTableFiles(database); } From 7f83f47ff9095dd23aa7cee182e8fb97b41b4e4c Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 08:44:41 -0600 Subject: [PATCH 12/28] select the first active feature database and table when editing features on the map --- .../nga/mapcache/GeoPackageMapFragment.java | 40 ++++++++++++++++--- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index e03b568b..27ab80a0 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -138,6 +138,7 @@ import mil.nga.mapcache.data.GeoPackageFeatureOverlayTable; import mil.nga.mapcache.data.GeoPackageFeatureTable; import mil.nga.mapcache.data.GeoPackageTable; +import mil.nga.mapcache.data.GeoPackageTableType; import mil.nga.mapcache.data.GeoPackageTileTable; import mil.nga.mapcache.filter.InputFilterMinMax; import mil.nga.mapcache.indexer.IIndexerTask; @@ -4423,14 +4424,32 @@ private AlertDialog.Builder getFeatureSelectionDialog(View editFeaturesSelection AlertDialog.Builder dialog = new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle); dialog.setView(editFeaturesSelectionView); + boolean searchForActive = true; + int defaultDatabase = 0; + int defaultTable = 0; + List databases = getDatabases(); List featureDatabases = new ArrayList(); if (databases != null) { for (String database : databases) { GeoPackage geoPackage = manager.open(database); try { - if (!geoPackage.getFeatureTables().isEmpty()) { + List featureTables = geoPackage.getFeatureTables(); + if (!featureTables.isEmpty()) { featureDatabases.add(database); + + if(searchForActive) { + for(int i = 0; i < featureTables.size(); i++){ + String featureTable = featureTables.get(i); + boolean isActive = active.exists(database, featureTable, GeoPackageTableType.FEATURE); + if(isActive){ + defaultDatabase = featureDatabases.size() - 1; + defaultTable = i; + searchForActive = false; + break; + } + } + } } } finally { if (geoPackage != null) { @@ -4450,16 +4469,27 @@ private AlertDialog.Builder getFeatureSelectionDialog(View editFeaturesSelection featureDatabases); geoPackageInput.setAdapter(geoPackageAdapter); - updateFeaturesSelection(featuresInput, featureDatabases.get(0)); + updateFeaturesSelection(featuresInput, featureDatabases.get(defaultDatabase)); + + geoPackageInput.setSelection(defaultDatabase); + featuresInput.setSelection(defaultTable); geoPackageInput .setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + + boolean firstTime = true; + @Override public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - String geoPackage = geoPackageInput.getSelectedItem() - .toString(); - updateFeaturesSelection(featuresInput, geoPackage); + + if(firstTime){ + firstTime = false; + }else { + String geoPackage = geoPackageInput.getSelectedItem() + .toString(); + updateFeaturesSelection(featuresInput, geoPackage); + } } @Override From 9544f6e102bec6429e354fbad019f53c36b7cd90 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 08:57:37 -0600 Subject: [PATCH 13/28] handle null contents bounds when initially zooming to declared bounds --- .../mil/nga/mapcache/GeoPackageMapFragment.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 27ab80a0..456be18b 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1615,12 +1615,15 @@ private void zoomToActiveBounds() { Contents contents = contentsDao.queryForId(featureTable); BoundingBox contentsBoundingBox = contents.getBoundingBox(); - contentsBoundingBox = transformBoundingBoxToWgs84(contentsBoundingBox, contents.getSrs()); + if(contentsBoundingBox != null) { - if (featuresBoundingBox != null) { - featuresBoundingBox = TileBoundingBoxUtils.union(featuresBoundingBox, contentsBoundingBox); - } else { - featuresBoundingBox = contentsBoundingBox; + contentsBoundingBox = transformBoundingBoxToWgs84(contentsBoundingBox, contents.getSrs()); + + if (featuresBoundingBox != null) { + featuresBoundingBox = TileBoundingBoxUtils.union(featuresBoundingBox, contentsBoundingBox); + } else { + featuresBoundingBox = contentsBoundingBox; + } } } catch (SQLException e) { Log.e(GeoPackageMapFragment.class.getSimpleName(), From ff6a4a5ab3beb15991699370346436070da7f512 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 09:33:00 -0600 Subject: [PATCH 14/28] fix for sharing GeoPackages stored externally --- .../java/mil/nga/mapcache/GeoPackageManagerFragment.java | 5 ++++- mapcache/src/main/res/xml/share_paths.xml | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java index 99702d7b..4a1a98ec 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java @@ -867,7 +867,10 @@ private void shareDatabaseOption(final String database) { // If external database, no permission is needed if (manager.isExternal(database)) { // Create the Uri and share - Uri databaseUri = Uri.fromFile(databaseFile); + Uri databaseUri = FileProvider.getUriForFile(getActivity(), + "mil.nga.mapcache.fileprovider", + databaseFile); + shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); launchShareIntent(shareIntent, databaseUri); } // If internal database, file must be copied to cache for permission diff --git a/mapcache/src/main/res/xml/share_paths.xml b/mapcache/src/main/res/xml/share_paths.xml index 8b014880..333f0228 100644 --- a/mapcache/src/main/res/xml/share_paths.xml +++ b/mapcache/src/main/res/xml/share_paths.xml @@ -1,4 +1,5 @@ + \ No newline at end of file From c38266863312999aed27e1c28dbeaf0282b69866 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 13 Oct 2017 15:34:41 -0600 Subject: [PATCH 15/28] handle map clicks on raw features --- .../nga/mapcache/GeoPackageMapFragment.java | 93 ++++++++++++++----- 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 456be18b..ba6f0563 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -97,11 +97,13 @@ import mil.nga.geopackage.features.columns.GeometryColumns; import mil.nga.geopackage.features.index.FeatureIndexManager; import mil.nga.geopackage.features.index.FeatureIndexResults; +import mil.nga.geopackage.features.index.MultipleFeatureIndexResults; import mil.nga.geopackage.features.user.FeatureCursor; import mil.nga.geopackage.features.user.FeatureDao; import mil.nga.geopackage.features.user.FeatureRow; import mil.nga.geopackage.geom.GeoPackageGeometryData; import mil.nga.geopackage.map.MapUtils; +import mil.nga.geopackage.map.features.FeatureInfoBuilder; import mil.nga.geopackage.map.geom.FeatureShapes; import mil.nga.geopackage.map.geom.GoogleMapShape; import mil.nga.geopackage.map.geom.GoogleMapShapeConverter; @@ -1615,7 +1617,7 @@ private void zoomToActiveBounds() { Contents contents = contentsDao.queryForId(featureTable); BoundingBox contentsBoundingBox = contents.getBoundingBox(); - if(contentsBoundingBox != null) { + if (contentsBoundingBox != null) { contentsBoundingBox = transformBoundingBoxToWgs84(contentsBoundingBox, contents.getSrs()); @@ -2194,16 +2196,15 @@ private void displayFeatures(MapFeaturesUpdateTask task, if (filter && indexer.isIndexed()) { FeatureIndexResults indexResults = indexer.query(mapViewBoundingBox, mapViewProjection); - processFeatureIndexResults(task, threadPool, indexResults, database, featureDao, converter, - count, maxFeatures, editable, filter); - BoundingBox complementary = mapViewBoundingBox.complementaryWgs84(); if (complementary != null) { - indexResults = indexer.query(complementary, mapViewProjection); - processFeatureIndexResults(task, threadPool, indexResults, database, featureDao, converter, - count, maxFeatures, editable, filter); + FeatureIndexResults indexResults2 = indexer.query(complementary, mapViewProjection); + indexResults = new MultipleFeatureIndexResults(indexResults, indexResults2); } + processFeatureIndexResults(task, threadPool, indexResults, database, featureDao, converter, + count, maxFeatures, editable, filter); + } else { BoundingBox filterBoundingBox = null; @@ -3292,8 +3293,9 @@ private boolean isWithinDistance(Projection projection, Point point, @Override public void onMapClick(LatLng point) { + StringBuilder clickMessage = new StringBuilder(); + if (!featureOverlayQueries.isEmpty()) { - StringBuilder clickMessage = new StringBuilder(); for (FeatureOverlayQuery query : featureOverlayQueries) { String message = query.buildMapClickMessage(point, view, map); if (message != null) { @@ -3303,19 +3305,68 @@ public void onMapClick(LatLng point) { clickMessage.append(message); } } - if (clickMessage.length() > 0) { - new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) - .setMessage(clickMessage.toString()) - .setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { + } + + for (GeoPackageDatabase database : active.getDatabases()) { + if (!database.getFeatures().isEmpty()) { + + BoundingBox clickBoundingBox = MapUtils.buildClickBoundingBox(point, view, map, .03f); + clickBoundingBox = clickBoundingBox.expandWgs84Coordinates(); + mil.nga.geopackage.projection.Projection clickProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); + + for (GeoPackageTable features : database.getFeatures()) { + + GeoPackage geoPackage = geoPackages.get(database.getDatabase()); + Map databaseFeatureDaos = featureDaos.get(database.getDatabase()); + + if (geoPackage != null && databaseFeatureDaos != null) { + + FeatureDao featureDao = databaseFeatureDaos.get(features.getName()); + + if (featureDao != null) { + + FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); + if (indexer.isIndexed()) { + + FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); + + FeatureIndexResults indexResults = indexer.query(clickBoundingBox, clickProjection); + BoundingBox complementary = clickBoundingBox.complementaryWgs84(); + if (complementary != null) { + FeatureIndexResults indexResults2 = indexer.query(complementary, clickProjection); + indexResults = new MultipleFeatureIndexResults(indexResults, indexResults2); + } + + if (indexResults.count() > 0) { + String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, point); + if (message != null) { + if (clickMessage.length() > 0) { + clickMessage.append("\n\n"); + } + clickMessage.append(message); } } - ) - .show(); + + } + } + + } + } } } + if (clickMessage.length() > 0) { + new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) + .setMessage(clickMessage.toString()) + .setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } + } + ) + .show(); + } + } @Override @@ -4441,11 +4492,11 @@ private AlertDialog.Builder getFeatureSelectionDialog(View editFeaturesSelection if (!featureTables.isEmpty()) { featureDatabases.add(database); - if(searchForActive) { - for(int i = 0; i < featureTables.size(); i++){ + if (searchForActive) { + for (int i = 0; i < featureTables.size(); i++) { String featureTable = featureTables.get(i); boolean isActive = active.exists(database, featureTable, GeoPackageTableType.FEATURE); - if(isActive){ + if (isActive) { defaultDatabase = featureDatabases.size() - 1; defaultTable = i; searchForActive = false; @@ -4486,9 +4537,9 @@ private AlertDialog.Builder getFeatureSelectionDialog(View editFeaturesSelection public void onItemSelected(AdapterView parentView, View selectedItemView, int position, long id) { - if(firstTime){ + if (firstTime) { firstTime = false; - }else { + } else { String geoPackage = geoPackageInput.getSelectedItem() .toString(); updateFeaturesSelection(featuresInput, geoPackage); From dd741b43724c18c1592ed1c458914dc36d1fa123 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 16 Oct 2017 06:34:15 -0600 Subject: [PATCH 16/28] configured screen click percentage --- .../main/java/mil/nga/mapcache/GeoPackageMapFragment.java | 6 +++++- mapcache/src/main/res/values/floats.xml | 5 +++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 mapcache/src/main/res/values/floats.xml diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index ba6f0563..2858b34a 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -3310,7 +3310,11 @@ public void onMapClick(LatLng point) { for (GeoPackageDatabase database : active.getDatabases()) { if (!database.getFeatures().isEmpty()) { - BoundingBox clickBoundingBox = MapUtils.buildClickBoundingBox(point, view, map, .03f); + TypedValue screenPercentage = new TypedValue(); + getResources().getValue(R.dimen.map_feature_click_screen_percentage, screenPercentage, true); + float screenClickPercentage = screenPercentage.getFloat(); + + BoundingBox clickBoundingBox = MapUtils.buildClickBoundingBox(point, view, map, screenClickPercentage); clickBoundingBox = clickBoundingBox.expandWgs84Coordinates(); mil.nga.geopackage.projection.Projection clickProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); diff --git a/mapcache/src/main/res/values/floats.xml b/mapcache/src/main/res/values/floats.xml new file mode 100644 index 00000000..47a6e18c --- /dev/null +++ b/mapcache/src/main/res/values/floats.xml @@ -0,0 +1,5 @@ + + + .03 + + From 11800bb37fd7aebfb96cb85f5cf5c3f895016050 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 17 Oct 2017 09:35:36 -0600 Subject: [PATCH 17/28] feature click info builder with tolerance color and drawable calls to use compat library --- .../mapcache/GeoPackageManagerFragment.java | 9 ++-- .../nga/mapcache/GeoPackageMapFragment.java | 43 +++++++------------ mapcache/src/main/res/values/floats.xml | 2 +- 3 files changed, 20 insertions(+), 34 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java index 4a1a98ec..b19351ad 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java @@ -4120,18 +4120,15 @@ public boolean onLongClick(View v) { break; } } - imageView.setImageDrawable(getResources().getDrawable( - drawableId)); + imageView.setImageDrawable(ContextCompat.getDrawable(getActivity(), drawableId)); break; case TILE: - imageView.setImageDrawable(getResources().getDrawable( - R.drawable.ic_tiles)); + imageView.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_tiles)); break; case FEATURE_OVERLAY: - imageView.setImageDrawable(getResources().getDrawable( - R.drawable.ic_format_paint)); + imageView.setImageDrawable(ContextCompat.getDrawable(getActivity(), R.drawable.ic_format_paint)); break; default: diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 2858b34a..9125782d 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -2585,11 +2585,9 @@ private MarkerOptions getMarkerOptions(boolean editable, boolean clickable) { private void setPolylineOptions(boolean editable, PolylineOptions polylineOptions) { if (editable) { - polylineOptions.color(getResources().getColor( - R.color.polyline_edit_color)); + polylineOptions.color(ContextCompat.getColor(getActivity(), R.color.polyline_edit_color)); } else { - polylineOptions.color(getResources().getColor( - R.color.polyline_color)); + polylineOptions.color(ContextCompat.getColor(getActivity(), R.color.polyline_color)); } } @@ -2602,15 +2600,11 @@ private void setPolylineOptions(boolean editable, private void setPolygonOptions(boolean editable, PolygonOptions polygonOptions) { if (editable) { - polygonOptions.strokeColor(getResources().getColor( - R.color.polygon_edit_color)); - polygonOptions.fillColor(getResources().getColor( - R.color.polygon_edit_fill_color)); + polygonOptions.strokeColor(ContextCompat.getColor(getActivity(), R.color.polygon_edit_color)); + polygonOptions.fillColor(ContextCompat.getColor(getActivity(), R.color.polygon_edit_fill_color)); } else { - polygonOptions.strokeColor(getResources().getColor( - R.color.polygon_color)); - polygonOptions.fillColor(getResources().getColor( - R.color.polygon_fill_color)); + polygonOptions.strokeColor(ContextCompat.getColor(getActivity(), R.color.polygon_color)); + polygonOptions.fillColor(ContextCompat.getColor(getActivity(), R.color.polygon_fill_color)); } } @@ -2967,10 +2961,8 @@ public void onMapLongClick(LatLng point) { boundingBoxStartCorner = point; boundingBoxEndCorner = point; PolygonOptions polygonOptions = new PolygonOptions(); - polygonOptions.strokeColor(getResources().getColor( - R.color.bounding_box_draw_color)); - polygonOptions.fillColor(getResources().getColor( - R.color.bounding_box_draw_fill_color)); + polygonOptions.strokeColor(ContextCompat.getColor(getActivity(), R.color.bounding_box_draw_color)); + polygonOptions.fillColor(ContextCompat.getColor(getActivity(), R.color.bounding_box_draw_fill_color)); List points = getPolygonPoints(boundingBoxStartCorner, boundingBoxEndCorner); polygonOptions.addAll(points); @@ -3213,8 +3205,7 @@ private void updateEditState(boolean updateAcceptClear) { */ private PolylineOptions getDrawPolylineOptions() { PolylineOptions polylineOptions = new PolylineOptions(); - polylineOptions.color(getResources().getColor( - R.color.polyline_draw_color)); + polylineOptions.color(ContextCompat.getColor(getActivity(), R.color.polyline_draw_color)); return polylineOptions; } @@ -3225,10 +3216,8 @@ private PolylineOptions getDrawPolylineOptions() { */ private PolygonOptions getDrawPolygonOptions() { PolygonOptions polygonOptions = new PolygonOptions(); - polygonOptions.strokeColor(getResources().getColor( - R.color.polygon_draw_color)); - polygonOptions.fillColor(getResources().getColor( - R.color.polygon_draw_fill_color)); + polygonOptions.strokeColor(ContextCompat.getColor(getActivity(), R.color.polygon_draw_color)); + polygonOptions.fillColor(ContextCompat.getColor(getActivity(), R.color.polygon_draw_fill_color)); return polygonOptions; } @@ -3239,10 +3228,8 @@ private PolygonOptions getDrawPolygonOptions() { */ private PolygonOptions getHoleDrawPolygonOptions() { PolygonOptions polygonOptions = new PolygonOptions(); - polygonOptions.strokeColor(getResources().getColor( - R.color.polygon_hole_draw_color)); - polygonOptions.fillColor(getResources().getColor( - R.color.polygon_hole_draw_fill_color)); + polygonOptions.strokeColor(ContextCompat.getColor(getActivity(), R.color.polygon_hole_draw_color)); + polygonOptions.fillColor(ContextCompat.getColor(getActivity(), R.color.polygon_hole_draw_fill_color)); return polygonOptions; } @@ -3318,6 +3305,8 @@ public void onMapClick(LatLng point) { clickBoundingBox = clickBoundingBox.expandWgs84Coordinates(); mil.nga.geopackage.projection.Projection clickProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); + double tolerance = MapUtils.getToleranceDistance(point, view, map, screenClickPercentage); + for (GeoPackageTable features : database.getFeatures()) { GeoPackage geoPackage = geoPackages.get(database.getDatabase()); @@ -3342,7 +3331,7 @@ public void onMapClick(LatLng point) { } if (indexResults.count() > 0) { - String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, point); + String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); if (message != null) { if (clickMessage.length() > 0) { clickMessage.append("\n\n"); diff --git a/mapcache/src/main/res/values/floats.xml b/mapcache/src/main/res/values/floats.xml index 47a6e18c..9eda9106 100644 --- a/mapcache/src/main/res/values/floats.xml +++ b/mapcache/src/main/res/values/floats.xml @@ -1,5 +1,5 @@ - .03 + .02 From d9fc28ffe383576e9dd366ac83bcd37a09cff3f9 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 17 Oct 2017 11:46:16 -0600 Subject: [PATCH 18/28] query for non indexed features on clicks --- .../nga/mapcache/GeoPackageMapFragment.java | 80 ++++++++++++++++--- 1 file changed, 71 insertions(+), 9 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 9125782d..3b3683f8 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -95,6 +95,7 @@ import mil.nga.geopackage.extension.link.FeatureTileTableLinker; import mil.nga.geopackage.factory.GeoPackageFactory; import mil.nga.geopackage.features.columns.GeometryColumns; +import mil.nga.geopackage.features.index.FeatureIndexListResults; import mil.nga.geopackage.features.index.FeatureIndexManager; import mil.nga.geopackage.features.index.FeatureIndexResults; import mil.nga.geopackage.features.index.MultipleFeatureIndexResults; @@ -3318,28 +3319,89 @@ public void onMapClick(LatLng point) { if (featureDao != null) { + FeatureIndexResults indexResults = null; + FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); if (indexer.isIndexed()) { - FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); - - FeatureIndexResults indexResults = indexer.query(clickBoundingBox, clickProjection); + indexResults = indexer.query(clickBoundingBox, clickProjection); BoundingBox complementary = clickBoundingBox.complementaryWgs84(); if (complementary != null) { FeatureIndexResults indexResults2 = indexer.query(complementary, clickProjection); indexResults = new MultipleFeatureIndexResults(indexResults, indexResults2); } - if (indexResults.count() > 0) { - String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); - if (message != null) { - if (clickMessage.length() > 0) { - clickMessage.append("\n\n"); + } else { + + mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); + ProjectionTransform projectionTransform = clickProjection.getTransformation(featureProjection); + BoundingBox boundedClickBoundingBox = clickBoundingBox.boundWgs84Coordinates(); + BoundingBox transformedBoundingBox = projectionTransform.transform(boundedClickBoundingBox); + Unit unit = featureProjection.getUnit(); + double filterMaxLongitude = 0; + if (unit instanceof DegreeUnit) { + filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; + } else if (unit == Units.METRES) { + filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; + } + + FeatureIndexListResults listResults = new FeatureIndexListResults(); + + // Query for all rows + FeatureCursor cursor = featureDao.queryForAll(); + try { + + while (cursor.moveToNext()) { + + try { + FeatureRow row = cursor.getRow(); + + GeoPackageGeometryData geometryData = row.getGeometry(); + if (geometryData != null && !geometryData.isEmpty()) { + + Geometry geometry = geometryData.getGeometry(); + + if (geometry != null) { + + GeometryEnvelope envelope = geometryData.getEnvelope(); + if (envelope == null) { + envelope = GeometryEnvelopeBuilder.buildEnvelope(geometry); + } + if (envelope != null) { + BoundingBox geometryBoundingBox = new BoundingBox(envelope); + + if (TileBoundingBoxUtils.overlap(transformedBoundingBox, geometryBoundingBox, filterMaxLongitude) != null) { + listResults.addRow(row); + } + + } + } + } + + } catch (Exception e) { + Log.e(GeoPackageMapFragment.class.getSimpleName(), + "Failed to query feature. database: " + database.getDatabase() + + ", feature table: " + features.getName() + + ", row: " + cursor.getPosition(), e); } - clickMessage.append(message); } + + } finally { + cursor.close(); } + indexResults = listResults; + } + + if (indexResults.count() > 0) { + FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); + String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); + if (message != null) { + if (clickMessage.length() > 0) { + clickMessage.append("\n\n"); + } + clickMessage.append(message); + } } } From a7fc088c2d50f9bc2a5dbf144e595ed352436374 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 17 Oct 2017 13:45:36 -0600 Subject: [PATCH 19/28] maintain indexes when editing features --- .../nga/mapcache/GeoPackageMapFragment.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 3b3683f8..7a19d582 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -98,6 +98,7 @@ import mil.nga.geopackage.features.index.FeatureIndexListResults; import mil.nga.geopackage.features.index.FeatureIndexManager; import mil.nga.geopackage.features.index.FeatureIndexResults; +import mil.nga.geopackage.features.index.FeatureIndexType; import mil.nga.geopackage.features.index.MultipleFeatureIndexResults; import mil.nga.geopackage.features.user.FeatureCursor; import mil.nga.geopackage.features.user.FeatureDao; @@ -1005,6 +1006,8 @@ private void saveEditFeatures() { try { FeatureDao featureDao = geoPackage.getFeatureDao(editFeaturesTable); long srsId = featureDao.getGeometryColumns().getSrsId(); + FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); + List indexedTypes = indexer.getIndexedTypes(); GoogleMapShapeConverter converter = new GoogleMapShapeConverter( featureDao.getProjection()); @@ -1021,6 +1024,10 @@ private void saveEditFeatures() { pointGeomData.setGeometry(point); newPoint.setGeometry(pointGeomData); featureDao.insert(newPoint); + updateLastChange(geoPackage, featureDao); + if (!indexedTypes.isEmpty()) { + indexer.index(newPoint, indexedTypes); + } } changesMade = true; break; @@ -1034,6 +1041,10 @@ private void saveEditFeatures() { lineStringGeomData.setGeometry(lineString); newLineString.setGeometry(lineStringGeomData); featureDao.insert(newLineString); + updateLastChange(geoPackage, featureDao); + if (!indexedTypes.isEmpty()) { + indexer.index(newLineString, indexedTypes); + } changesMade = true; break; @@ -1048,6 +1059,10 @@ private void saveEditFeatures() { polygonGeomData.setGeometry(polygon); newPolygon.setGeometry(polygonGeomData); featureDao.insert(newPolygon); + updateLastChange(geoPackage, featureDao); + if (!indexedTypes.isEmpty()) { + indexer.index(newPolygon, indexedTypes); + } changesMade = true; break; @@ -1063,19 +1078,23 @@ private void saveEditFeatures() { GeoPackageGeometryData geomData = featureRow.getGeometry(); geomData.setGeometry(geometry); featureDao.update(featureRow); + updateLastChange(geoPackage, featureDao); + if (!indexedTypes.isEmpty()) { + indexer.index(featureRow, indexedTypes); + } } else { featureDao.deleteById(featureId); editFeatureMarker = null; + updateLastChange(geoPackage, featureDao); + if (!indexedTypes.isEmpty()) { + indexer.deleteIndex(featureId, indexedTypes); + } } - updateLastChange(geoPackage, featureDao); active.setModified(true); break; } - if (changesMade) { - updateLastChange(geoPackage, featureDao); - } } catch (Exception e) { if (GeoPackageUtils.isFutureSQLiteException(e)) { GeoPackageUtils From b72a8ac4f5eb055ef2120a2a78b3ef2ff7feb2ea Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Wed, 18 Oct 2017 10:18:04 -0600 Subject: [PATCH 20/28] filter features displayed when changing between edit feature modes --- .../nga/mapcache/GeoPackageMapFragment.java | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 7a19d582..e52c5e64 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1118,7 +1118,7 @@ private void saveEditFeatures() { if (changesMade) { active.setModified(true); - updateInBackground(false); + updateInBackground(false, true); } } @@ -1252,7 +1252,7 @@ public boolean handleMenuClick(MenuItem item) { selectEditFeatures(); } else { resetEditFeatures(); - updateInBackground(false); + updateInBackground(false, true); } break; case R.id.map_bounding_box: @@ -1261,7 +1261,7 @@ public boolean handleMenuClick(MenuItem item) { if (editFeaturesMode) { resetEditFeatures(); - updateInBackground(false); + updateInBackground(false, true); } boundingBoxMode = true; @@ -1335,7 +1335,7 @@ public void onClick(DialogInterface dialog, int id) { editFeaturesMenuItem .setIcon(R.drawable.ic_features_active); - updateInBackground(false); + updateInBackground(false, true); } catch (Exception e) { GeoPackageUtils @@ -1502,7 +1502,7 @@ public void onClick(DialogInterface dialog, Editor editor = settings.edit(); editor.putInt(MAX_FEATURES_KEY, maxFeature); editor.commit(); - updateInBackground(false); + updateInBackground(false, true); } } }) @@ -1549,9 +1549,19 @@ private void setMapType(int mapType) { /** * Update the map by kicking off a background task * - * @param zoom + * @param zoom zoom flag */ private void updateInBackground(boolean zoom) { + updateInBackground(zoom, false); + } + + /** + * Update the map by kicking off a background task + * + * @param zoom zoom flag + * @param filter filter features flag + */ + private void updateInBackground(boolean zoom, boolean filter) { MapUpdateTask localUpdateTask = null; updateLock.lock(); @@ -1594,7 +1604,7 @@ private void updateInBackground(boolean zoom) { BoundingBox mapViewBoundingBox = MapUtils.getBoundingBox(map); double toleranceDistance = MapUtils.getToleranceDistance(view, map); - localUpdateTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, false); + localUpdateTask.execute(zoom, maxFeatures, mapViewBoundingBox, toleranceDistance, filter); } From b027891510b8eca3f5e9154c67b34903f88af5f5 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 23 Oct 2017 13:44:52 -0600 Subject: [PATCH 21/28] geopackage-android-map version 1.5.0 version 1.17 changelog --- CHANGELOG.md | 19 +++++++++- mapcache/build.gradle | 2 +- .../nga/mapcache/GeoPackageMapFragment.java | 35 +++++++++++++++++++ 3 files changed, 54 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 231dd43a..3f556b41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,24 @@ Adheres to [Semantic Versioning](http://semver.org/). ## 1.17 (TBD) -* TBD +* geopackage-android-map version updated to 1.5.0 +* GeoPackage share fix for external GeoPackages +* Geometry simplifications for displayed map features based upon zoom level +* Only display and maintain features features in the current map views +* Maintain active feature indices when editing map features +* Queryable map features (previously only available for feature tiles) +* Automatically select active feature table when editing features +* Increase default max map features & max points per tile to 5000, max features per tile to 2000 +* Updated preloaded GeoPackage url example files +* Updated various now deprecated Android library calls +* gradle plugin updated to 2.3.3 +* android maven gradle plugin updated to 2.0 +* maven google dependency +* compile SDK version 26 +* build tools version updated to 26.0.1 +* target SDK version updated to 26 +* appcompat v7:26.0.2 +* multidex version 1.0.2 ## [1.16](https://github.com/ngageoint/geopackage-mapcache-android/releases/tag/1.16) (07-13-2017) diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 0b7e1fc9..0010c209 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -51,7 +51,7 @@ task androidAppVersion { dependencies { compile 'com.android.support:appcompat-v7:26.0.2' - compile "mil.nga.geopackage.map:geopackage-android-map:1.4.2" // comment out to build locally + compile "mil.nga.geopackage.map:geopackage-android-map:1.5.0" // comment out to build locally //compile project(':geopackage-map') // uncomment me to build locally compile 'com.android.support:multidex:1.0.2' androidTestCompile 'com.android.support:multidex:1.0.2' diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index e52c5e64..96fc9b64 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -2291,6 +2291,8 @@ private void displayFeatures(MapFeaturesUpdateTask task, } /** + * Process the feature index results + * * @param task * @param threadPool * @param indexResults @@ -2380,12 +2382,24 @@ private class FeatureRowProcessor implements Runnable { */ private final boolean editable; + /** + * Shape converter + */ private final GoogleMapShapeConverter converter; + /** + * Filter bounding box + */ private final BoundingBox filterBoundingBox; + /** + * Max projection longitude + */ private final double maxLongitude; + /** + * Filter flag + */ private final boolean filter; /** @@ -2936,6 +2950,9 @@ public void run() { }); } + /** + * {@inheritDoc} + */ @Override public void onMapLongClick(LatLng point) { @@ -3307,6 +3324,9 @@ private boolean isWithinDistance(Projection projection, Point point, return withinDistance; } + /** + * {@inheritDoc} + */ @Override public void onMapClick(LatLng point) { @@ -3453,6 +3473,9 @@ public void onClick(DialogInterface dialog, int which) { } + /** + * {@inheritDoc} + */ @Override public boolean onMarkerClick(Marker marker) { @@ -3498,16 +3521,25 @@ public boolean onMarkerClick(Marker marker) { return false; } + /** + * {@inheritDoc} + */ @Override public void onMarkerDrag(Marker marker) { updateEditState(false); } + /** + * {@inheritDoc} + */ @Override public void onMarkerDragEnd(Marker marker) { updateEditState(false); } + /** + * {@inheritDoc} + */ @Override public void onMarkerDragStart(Marker marker) { vibrator.vibrate(getActivity().getResources().getInteger( @@ -3938,6 +3970,9 @@ public TouchableMap(Context context) { super(context); } + /** + * {@inheritDoc} + */ @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { From 28d9a489c4c610307cea9598a46b756aa5ad42cd Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 24 Oct 2017 13:57:52 -0600 Subject: [PATCH 22/28] minor changelog fix --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f556b41..7f1758dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ Adheres to [Semantic Versioning](http://semver.org/). * geopackage-android-map version updated to 1.5.0 * GeoPackage share fix for external GeoPackages * Geometry simplifications for displayed map features based upon zoom level -* Only display and maintain features features in the current map views +* Only display and maintain features in the current map views * Maintain active feature indices when editing map features * Queryable map features (previously only available for feature tiles) * Automatically select active feature table when editing features From 554299040bc1fea396513ed671adf70e127bae65 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Tue, 7 Nov 2017 11:38:31 -0700 Subject: [PATCH 23/28] ignore map clicks for feature points, handled instead by the markers --- .../src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 96fc9b64..85864156 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1669,7 +1669,7 @@ private void zoomToActiveBounds() { TileMatrixSetDao tileMatrixSetDao = geoPackage.getTileMatrixSetDao(); - for (GeoPackageTileTable tileTable : database.getTiles()) { + for (GeoPackageTileTable tileTable : tileTables) { try { TileMatrixSet tileMatrixSet = tileMatrixSetDao.queryForId(tileTable.getName()); @@ -3444,6 +3444,7 @@ public void onMapClick(LatLng point) { if (indexResults.count() > 0) { FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); + featureInfoBuilder.ignoreGeometryType(GeometryType.POINT); String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); if (message != null) { if (clickMessage.length() > 0) { From 4c76b5f42b75693f9df4948c0aaf043b2a64f2c3 Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Wed, 8 Nov 2017 15:06:38 -0700 Subject: [PATCH 24/28] don't query on taps when editing features updated geometry envelopes on edited geometries --- .../nga/mapcache/GeoPackageMapFragment.java | 196 +++++++++--------- 1 file changed, 101 insertions(+), 95 deletions(-) diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index 85864156..b6bb8e47 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -1077,6 +1077,9 @@ private void saveEditFeatures() { .queryForIdRow(featureId); GeoPackageGeometryData geomData = featureRow.getGeometry(); geomData.setGeometry(geometry); + if(geomData.getEnvelope() != null){ + geomData.setEnvelope(GeometryEnvelopeBuilder.buildEnvelope(geometry)); + } featureDao.update(featureRow); updateLastChange(geoPackage, featureDao); if (!indexedTypes.isEmpty()) { @@ -3330,146 +3333,149 @@ private boolean isWithinDistance(Projection projection, Point point, @Override public void onMapClick(LatLng point) { - StringBuilder clickMessage = new StringBuilder(); + if(!editFeaturesMode) { + + StringBuilder clickMessage = new StringBuilder(); - if (!featureOverlayQueries.isEmpty()) { - for (FeatureOverlayQuery query : featureOverlayQueries) { - String message = query.buildMapClickMessage(point, view, map); - if (message != null) { - if (clickMessage.length() > 0) { - clickMessage.append("\n\n"); + if (!featureOverlayQueries.isEmpty()) { + for (FeatureOverlayQuery query : featureOverlayQueries) { + String message = query.buildMapClickMessage(point, view, map); + if (message != null) { + if (clickMessage.length() > 0) { + clickMessage.append("\n\n"); + } + clickMessage.append(message); } - clickMessage.append(message); } } - } - for (GeoPackageDatabase database : active.getDatabases()) { - if (!database.getFeatures().isEmpty()) { + for (GeoPackageDatabase database : active.getDatabases()) { + if (!database.getFeatures().isEmpty()) { - TypedValue screenPercentage = new TypedValue(); - getResources().getValue(R.dimen.map_feature_click_screen_percentage, screenPercentage, true); - float screenClickPercentage = screenPercentage.getFloat(); + TypedValue screenPercentage = new TypedValue(); + getResources().getValue(R.dimen.map_feature_click_screen_percentage, screenPercentage, true); + float screenClickPercentage = screenPercentage.getFloat(); - BoundingBox clickBoundingBox = MapUtils.buildClickBoundingBox(point, view, map, screenClickPercentage); - clickBoundingBox = clickBoundingBox.expandWgs84Coordinates(); - mil.nga.geopackage.projection.Projection clickProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); + BoundingBox clickBoundingBox = MapUtils.buildClickBoundingBox(point, view, map, screenClickPercentage); + clickBoundingBox = clickBoundingBox.expandWgs84Coordinates(); + mil.nga.geopackage.projection.Projection clickProjection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); - double tolerance = MapUtils.getToleranceDistance(point, view, map, screenClickPercentage); + double tolerance = MapUtils.getToleranceDistance(point, view, map, screenClickPercentage); - for (GeoPackageTable features : database.getFeatures()) { + for (GeoPackageTable features : database.getFeatures()) { - GeoPackage geoPackage = geoPackages.get(database.getDatabase()); - Map databaseFeatureDaos = featureDaos.get(database.getDatabase()); + GeoPackage geoPackage = geoPackages.get(database.getDatabase()); + Map databaseFeatureDaos = featureDaos.get(database.getDatabase()); - if (geoPackage != null && databaseFeatureDaos != null) { + if (geoPackage != null && databaseFeatureDaos != null) { - FeatureDao featureDao = databaseFeatureDaos.get(features.getName()); + FeatureDao featureDao = databaseFeatureDaos.get(features.getName()); - if (featureDao != null) { + if (featureDao != null) { - FeatureIndexResults indexResults = null; + FeatureIndexResults indexResults = null; - FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); - if (indexer.isIndexed()) { + FeatureIndexManager indexer = new FeatureIndexManager(getActivity(), geoPackage, featureDao); + if (indexer.isIndexed()) { - indexResults = indexer.query(clickBoundingBox, clickProjection); - BoundingBox complementary = clickBoundingBox.complementaryWgs84(); - if (complementary != null) { - FeatureIndexResults indexResults2 = indexer.query(complementary, clickProjection); - indexResults = new MultipleFeatureIndexResults(indexResults, indexResults2); - } - - } else { + indexResults = indexer.query(clickBoundingBox, clickProjection); + BoundingBox complementary = clickBoundingBox.complementaryWgs84(); + if (complementary != null) { + FeatureIndexResults indexResults2 = indexer.query(complementary, clickProjection); + indexResults = new MultipleFeatureIndexResults(indexResults, indexResults2); + } - mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); - ProjectionTransform projectionTransform = clickProjection.getTransformation(featureProjection); - BoundingBox boundedClickBoundingBox = clickBoundingBox.boundWgs84Coordinates(); - BoundingBox transformedBoundingBox = projectionTransform.transform(boundedClickBoundingBox); - Unit unit = featureProjection.getUnit(); - double filterMaxLongitude = 0; - if (unit instanceof DegreeUnit) { - filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; - } else if (unit == Units.METRES) { - filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; - } + } else { - FeatureIndexListResults listResults = new FeatureIndexListResults(); + mil.nga.geopackage.projection.Projection featureProjection = featureDao.getProjection(); + ProjectionTransform projectionTransform = clickProjection.getTransformation(featureProjection); + BoundingBox boundedClickBoundingBox = clickBoundingBox.boundWgs84Coordinates(); + BoundingBox transformedBoundingBox = projectionTransform.transform(boundedClickBoundingBox); + Unit unit = featureProjection.getUnit(); + double filterMaxLongitude = 0; + if (unit instanceof DegreeUnit) { + filterMaxLongitude = ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH; + } else if (unit == Units.METRES) { + filterMaxLongitude = ProjectionConstants.WEB_MERCATOR_HALF_WORLD_WIDTH; + } - // Query for all rows - FeatureCursor cursor = featureDao.queryForAll(); - try { + FeatureIndexListResults listResults = new FeatureIndexListResults(); - while (cursor.moveToNext()) { + // Query for all rows + FeatureCursor cursor = featureDao.queryForAll(); + try { - try { - FeatureRow row = cursor.getRow(); + while (cursor.moveToNext()) { - GeoPackageGeometryData geometryData = row.getGeometry(); - if (geometryData != null && !geometryData.isEmpty()) { + try { + FeatureRow row = cursor.getRow(); - Geometry geometry = geometryData.getGeometry(); + GeoPackageGeometryData geometryData = row.getGeometry(); + if (geometryData != null && !geometryData.isEmpty()) { - if (geometry != null) { + Geometry geometry = geometryData.getGeometry(); - GeometryEnvelope envelope = geometryData.getEnvelope(); - if (envelope == null) { - envelope = GeometryEnvelopeBuilder.buildEnvelope(geometry); - } - if (envelope != null) { - BoundingBox geometryBoundingBox = new BoundingBox(envelope); + if (geometry != null) { - if (TileBoundingBoxUtils.overlap(transformedBoundingBox, geometryBoundingBox, filterMaxLongitude) != null) { - listResults.addRow(row); + GeometryEnvelope envelope = geometryData.getEnvelope(); + if (envelope == null) { + envelope = GeometryEnvelopeBuilder.buildEnvelope(geometry); } + if (envelope != null) { + BoundingBox geometryBoundingBox = new BoundingBox(envelope); + + if (TileBoundingBoxUtils.overlap(transformedBoundingBox, geometryBoundingBox, filterMaxLongitude) != null) { + listResults.addRow(row); + } + } } } - } - } catch (Exception e) { - Log.e(GeoPackageMapFragment.class.getSimpleName(), - "Failed to query feature. database: " + database.getDatabase() - + ", feature table: " + features.getName() - + ", row: " + cursor.getPosition(), e); + } catch (Exception e) { + Log.e(GeoPackageMapFragment.class.getSimpleName(), + "Failed to query feature. database: " + database.getDatabase() + + ", feature table: " + features.getName() + + ", row: " + cursor.getPosition(), e); + } } + + } finally { + cursor.close(); } - } finally { - cursor.close(); + indexResults = listResults; } - indexResults = listResults; - } - - if (indexResults.count() > 0) { - FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); - featureInfoBuilder.ignoreGeometryType(GeometryType.POINT); - String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); - if (message != null) { - if (clickMessage.length() > 0) { - clickMessage.append("\n\n"); + if (indexResults.count() > 0) { + FeatureInfoBuilder featureInfoBuilder = new FeatureInfoBuilder(getActivity(), featureDao); + featureInfoBuilder.ignoreGeometryType(GeometryType.POINT); + String message = featureInfoBuilder.buildResultsInfoMessageAndClose(indexResults, tolerance, point); + if (message != null) { + if (clickMessage.length() > 0) { + clickMessage.append("\n\n"); + } + clickMessage.append(message); } - clickMessage.append(message); } } - } + } } } } - } - if (clickMessage.length() > 0) { - new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) - .setMessage(clickMessage.toString()) - .setPositiveButton(android.R.string.yes, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { + if (clickMessage.length() > 0) { + new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle) + .setMessage(clickMessage.toString()) + .setPositiveButton(android.R.string.yes, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + } } - } - ) - .show(); + ) + .show(); + } } } From 84bb7684286bb8d5774ad625e53a7e7772fc1cac Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Fri, 10 Nov 2017 07:29:53 -0700 Subject: [PATCH 25/28] change log update --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7f1758dc..a67ad545 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Adheres to [Semantic Versioning](http://semver.org/). * Maintain active feature indices when editing map features * Queryable map features (previously only available for feature tiles) * Automatically select active feature table when editing features +* Update geometry envelopes when editing features * Increase default max map features & max points per tile to 5000, max features per tile to 2000 * Updated preloaded GeoPackage url example files * Updated various now deprecated Android library calls From fdfcde3d338a6232688b760b6d070611b5c8fc6d Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 13 Nov 2017 09:11:14 -0700 Subject: [PATCH 26/28] geopackage-android-map version 2.0.0 --- CHANGELOG.md | 2 +- mapcache/build.gradle | 2 +- .../mil/nga/mapcache/GeoPackageManagerFragment.java | 12 ++++++------ .../java/mil/nga/mapcache/GeoPackageMapFragment.java | 12 ++++++------ 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a67ad545..a2763b7f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ Adheres to [Semantic Versioning](http://semver.org/). ## 1.17 (TBD) -* geopackage-android-map version updated to 1.5.0 +* geopackage-android-map version updated to 2.0.0 * GeoPackage share fix for external GeoPackages * Geometry simplifications for displayed map features based upon zoom level * Only display and maintain features in the current map views diff --git a/mapcache/build.gradle b/mapcache/build.gradle index 0010c209..b53f47af 100644 --- a/mapcache/build.gradle +++ b/mapcache/build.gradle @@ -51,7 +51,7 @@ task androidAppVersion { dependencies { compile 'com.android.support:appcompat-v7:26.0.2' - compile "mil.nga.geopackage.map:geopackage-android-map:1.5.0" // comment out to build locally + compile "mil.nga.geopackage.map:geopackage-android-map:2.0.0" // comment out to build locally //compile project(':geopackage-map') // uncomment me to build locally compile 'com.android.support:multidex:1.0.2' androidTestCompile 'com.android.support:multidex:1.0.2' diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java index b19351ad..8fa5e788 100644 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageManagerFragment.java @@ -1086,7 +1086,7 @@ public void onClick(DialogInterface dialog, int id) { } BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); GeometryType geometryType = GeometryType .fromName(geometryTypeSpinner @@ -1241,7 +1241,7 @@ public void onClick(DialogInterface dialog, int id) { .isChecked(); BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); // If not importing tiles, just create the table if (tileUrl == null || tileUrl.isEmpty()) { @@ -2052,7 +2052,7 @@ public void onClick(DialogInterface dialog, int id) { .isChecked(); BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); // Load tiles LoadTilesTask.loadTiles(getActivity(), @@ -2331,8 +2331,8 @@ private void createFeatureTilesTableOption(final GeoPackageTable table) { Projection projection = null; if (boundingBox == null) { boundingBox = new BoundingBox(-ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, - ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE, + ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE); projection = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM); } else { @@ -2452,7 +2452,7 @@ public void onClick(DialogInterface dialog, int id) { .isChecked(); BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); GeoPackageManager manager = GeoPackageFactory.getManager(getActivity()); GeoPackage geoPackage = manager.open(table.getDatabase()); @@ -2629,8 +2629,8 @@ private void addFeatureOverlayTableOption(final GeoPackageTable table) { worldGeodeticBoundingBox = worldGeodeticTransform.transform(webMercatorBoundingBox); } else { worldGeodeticBoundingBox = new BoundingBox(-ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, - ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE, + ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE); } diff --git a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java index b6bb8e47..0069bc9c 100755 --- a/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java +++ b/mapcache/src/main/java/mil/nga/mapcache/GeoPackageMapFragment.java @@ -2847,7 +2847,7 @@ private void displayFeatureTiles(GeoPackageFeatureOverlayTable featureOverlayTab FeatureDao featureDao = featureDaos.get(featureOverlayTable.getDatabase()).get(featureOverlayTable.getFeatureTable()); BoundingBox boundingBox = new BoundingBox(featureOverlayTable.getMinLon(), - featureOverlayTable.getMaxLon(), featureOverlayTable.getMinLat(), featureOverlayTable.getMaxLat()); + featureOverlayTable.getMinLat(), featureOverlayTable.getMaxLon(), featureOverlayTable.getMaxLat()); // Load tiles FeatureTiles featureTiles = new DefaultFeatureTiles(getActivity(), featureDao); @@ -2930,8 +2930,8 @@ private void displayTiles(TileProvider overlay, BoundingBox dataBoundingBox, Spa boundingBox = transformBoundingBoxToWgs84(boundingBox, srs); } else { boundingBox = new BoundingBox(-ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, - ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MIN_LAT_RANGE, + ProjectionConstants.WGS84_HALF_WORLD_LON_WIDTH, ProjectionConstants.WEB_MERCATOR_MAX_LAT_RANGE); } @@ -4080,7 +4080,7 @@ private void createTiles() { // Try to find a good zoom starting point ProjectionTransform webMercatorTransform = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM) .getTransformation(ProjectionConstants.EPSG_WEB_MERCATOR); - BoundingBox bbox = new BoundingBox(minLon, maxLon, minLat, maxLat); + BoundingBox bbox = new BoundingBox(minLon, minLat, maxLon, maxLat); BoundingBox webMercatorBoundingBox = webMercatorTransform.transform(bbox); int zoomLevel = TileBoundingBoxUtils.getZoomLevel(webMercatorBoundingBox); int maxZoomLevel = getActivity().getResources().getInteger( @@ -4191,7 +4191,7 @@ public void onClick(DialogInterface dialog, int id) { .isChecked(); BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); // Create the database if it doesn't exist if (!manager.exists(database)) { @@ -4375,7 +4375,7 @@ private void createFeatureTiles(final String database, final String featureTable // Try to find a good zoom starting point ProjectionTransform webMercatorTransform = ProjectionFactory.getProjection(ProjectionConstants.EPSG_WORLD_GEODETIC_SYSTEM) .getTransformation(ProjectionConstants.EPSG_WEB_MERCATOR); - BoundingBox bbox = new BoundingBox(minLon, maxLon, minLat, maxLat); + BoundingBox bbox = new BoundingBox(minLon, minLat, maxLon, maxLat); BoundingBox webMercatorBoundingBox = webMercatorTransform.transform(bbox); int zoomLevel = TileBoundingBoxUtils.getZoomLevel(webMercatorBoundingBox); int maxZoomLevel = getActivity().getResources().getInteger( @@ -4504,7 +4504,7 @@ public void onClick(DialogInterface dialog, int id) { .isChecked(); BoundingBox boundingBox = new BoundingBox(minLon, - maxLon, minLat, maxLat); + minLat, maxLon, maxLat); GeoPackageManager manager = GeoPackageFactory.getManager(getActivity()); GeoPackage geoPackage = manager.open(database); From 5073a1796d237c1a41898c8a01a489c1f77a528d Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 20 Nov 2017 16:59:18 -0700 Subject: [PATCH 27/28] release 1.17 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a2763b7f..c0eb0bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ Adheres to [Semantic Versioning](http://semver.org/). --- -## 1.17 (TBD) +## [1.17](https://github.com/ngageoint/geopackage-mapcache-android/releases/tag/1.17) (11-21-2017) * geopackage-android-map version updated to 2.0.0 * GeoPackage share fix for external GeoPackages From f252323fe00f315221df6c62f66172d335b4abfb Mon Sep 17 00:00:00 2001 From: Brian Osborn Date: Mon, 20 Nov 2017 17:00:39 -0700 Subject: [PATCH 28/28] release 1.17 --- docs/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/index.html b/docs/index.html index 6f78a317..3c06e174 100644 --- a/docs/index.html +++ b/docs/index.html @@ -15,7 +15,7 @@

GeoPackage MapCache Android

GeoPackage

by the National Geospatial-Intelligence Agency

GitHub - APK + APK .zip .tar.gz