summaryrefslogtreecommitdiff
path: root/platform
diff options
context:
space:
mode:
authorIvo van Dongen <info@ivovandongen.nl>2016-08-04 14:45:50 -0400
committerIvo van Dongen <info@ivovandongen.nl>2016-08-16 11:38:30 +0200
commit4e211548d5029a353aaa8814c40599970a8098bd (patch)
tree9cad650747190f6fbd1bb53f034baee3e86f6517 /platform
parentdcd9e1016cd20f107c4e34be62c631a1f4189af1 (diff)
downloadqtlocation-mapboxgl-4e211548d5029a353aaa8814c40599970a8098bd.tar.gz
[android] #5869 - visible feature querying
Diffstat (limited to 'platform')
-rw-r--r--platform/android/MapboxGLAndroidSDK/build.gradle5
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java30
-rwxr-xr-xplatform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java31
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml128
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java157
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java155
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java258
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_box.xml27
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_point.xml19
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml7
-rw-r--r--platform/android/src/conversion/collection.hpp42
-rw-r--r--platform/android/src/conversion/constant.hpp62
-rw-r--r--platform/android/src/conversion/conversion.hpp2
-rw-r--r--platform/android/src/geometry/conversion/feature.hpp209
-rw-r--r--platform/android/src/geometry/conversion/geometry.hpp184
-rwxr-xr-xplatform/android/src/jni.cpp45
-rw-r--r--platform/android/src/jni/local_object.hpp33
17 files changed, 1340 insertions, 54 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/build.gradle b/platform/android/MapboxGLAndroidSDK/build.gradle
index e2042fa6da..09c5887752 100644
--- a/platform/android/MapboxGLAndroidSDK/build.gradle
+++ b/platform/android/MapboxGLAndroidSDK/build.gradle
@@ -27,6 +27,11 @@ dependencies {
compile "com.android.support:design:${supportLibVersion}"
compile 'com.squareup.okhttp3:okhttp:3.3.0'
compile 'com.mapzen.android:lost:1.1.0'
+
+ // Mapbox Android Services
+ compile('com.mapbox.mapboxsdk:mapbox-android-services:2.0.0-SNAPSHOT@aar') {
+ transitive = true
+ }
}
android {
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
index b2c48be69c..fbc3f2f461 100644
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapboxMap.java
@@ -2,6 +2,8 @@ package com.mapbox.mapboxsdk.maps;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.PointF;
+import android.graphics.RectF;
import android.location.Location;
import android.os.SystemClock;
import android.support.annotation.FloatRange;
@@ -42,6 +44,7 @@ import com.mapbox.mapboxsdk.maps.widgets.MyLocationViewSettings;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
import com.mapbox.mapboxsdk.style.sources.Source;
+import com.mapbox.services.commons.geojson.Feature;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
@@ -1722,6 +1725,33 @@ public class MapboxMap {
mMapView.snapshot(callback, null);
}
+ /**
+ * Queries the map for rendered features
+ *
+ * @param coordinates the point to query
+ * @param layerIds optionally - only query these layers
+ * @return the list of feature
+ */
+ @UiThread
+ @NonNull
+ public List<Feature> queryRenderedFeatures(@NonNull PointF coordinates, @Nullable String... layerIds) {
+ return mMapView.getNativeMapView().queryRenderedFeatures(coordinates, layerIds);
+ }
+
+ /**
+ * Queries the map for rendered features
+ *
+ * @param coordinates the box to query
+ * @param layerIds optionally - only query these layers
+ * @return the list of feature
+ */
+ @UiThread
+ @NonNull
+ public List<Feature> queryRenderedFeatures(@NonNull RectF coordinates, @Nullable String... layerIds) {
+ return mMapView.getNativeMapView().queryRenderedFeatures(coordinates, layerIds);
+ }
+
+
//
// Interfaces
//
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
index c93ac9c145..1010e9baa6 100755
--- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
+++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java
@@ -20,7 +20,10 @@ import com.mapbox.mapboxsdk.offline.OfflineManager;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.NoSuchLayerException;
import com.mapbox.mapboxsdk.style.sources.Source;
+import com.mapbox.services.commons.geojson.Feature;
+import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
// Class that wraps the native methods for convenience
@@ -42,6 +45,8 @@ final class NativeMapView {
// Used for callbacks
private MapView mMapView;
+ private final float pixelRatio;
+
//
// Static methods
//
@@ -63,7 +68,7 @@ final class NativeMapView {
// the system
String cachePath = dataPath;
- float pixelRatio = context.getResources().getDisplayMetrics().density;
+ pixelRatio = context.getResources().getDisplayMetrics().density;
String apkPath = context.getPackageCodePath();
int availableProcessors = Runtime.getRuntime().availableProcessors();
ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
@@ -501,6 +506,26 @@ final class NativeMapView {
nativeRemoveSource(mNativeMapViewPtr, sourceId);
}
+ // Feature querying
+
+ @NonNull
+ public List<Feature> queryRenderedFeatures(PointF coordinates, String... layerIds) {
+ Feature[] features = nativeQueryRenderedFeaturesForPoint(mNativeMapViewPtr, coordinates.x / pixelRatio, coordinates.y / pixelRatio, layerIds);
+ return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
+ }
+
+ @NonNull
+ public List<Feature> queryRenderedFeatures(RectF coordinates, String... layerIds) {
+ Feature[] features = nativeQueryRenderedFeaturesForBox(
+ mNativeMapViewPtr,
+ coordinates.left / pixelRatio,
+ coordinates.top / pixelRatio,
+ coordinates.right / pixelRatio,
+ coordinates.bottom / pixelRatio,
+ layerIds);
+ return features != null ? Arrays.asList(features) : new ArrayList<Feature>();
+ }
+
public void scheduleTakeSnapshot() {
nativeScheduleTakeSnapshot(mNativeMapViewPtr);
}
@@ -696,4 +721,8 @@ final class NativeMapView {
private native long nativeUpdatePolyline(long nativeMapviewPtr, long polylineId, Polyline polyline);
private native void nativeScheduleTakeSnapshot(long nativeMapViewPtr);
+
+ private native Feature[] nativeQueryRenderedFeaturesForPoint(long nativeMapViewPtr, float x, float y, String[] layerIds);
+
+ private native Feature[] nativeQueryRenderedFeaturesForBox(long mNativeMapViewPtr, float left, float top, float right, float bottom, String[] layerIds);
}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
index db1fa025ae..1a46671ade 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/AndroidManifest.xml
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.mapbox.mapboxsdk.testapp">
+<manifest package="com.mapbox.mapboxsdk.testapp"
+ xmlns:android="http://schemas.android.com/apk/res/android">
- <uses-permission android:name="android.permission.INTERNET" />
- <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
- <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.INTERNET"/>
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:name=".MapboxApplication"
@@ -20,8 +20,8 @@
android:name=".activity.FeatureOverviewActivity"
android:label="@string/app_name">
<intent-filter>
- <action android:name="android.intent.action.MAIN" />
- <category android:name="android.intent.category.LAUNCHER" />
+ <action android:name="android.intent.action.MAIN"/>
+ <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
@@ -30,7 +30,7 @@
android:label="@string/activity_info_window">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow" />
+ android:value="@string/category_infowindow"/>
</activity>
<activity
android:name=".activity.infowindow.InfoWindowAdapterActivity"
@@ -38,7 +38,7 @@
android:label="@string/activity_infowindow_adapter">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow" />
+ android:value="@string/category_infowindow"/>
</activity>
<activity
android:name=".activity.infowindow.DynamicInfoWindowAdapterActivity"
@@ -46,7 +46,7 @@
android:label="@string/activity_dynamic_infowindow_adapter">
<meta-data
android:name="@string/category"
- android:value="@string/category_infowindow" />
+ android:value="@string/category_infowindow"/>
</activity>
<activity
android:name=".activity.annotation.BulkMarkerActivity"
@@ -55,7 +55,7 @@
android:label="@string/activity_add_bulk_markers">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.annotation.AnimatedMarkerActivity"
@@ -63,7 +63,7 @@
android:label="@string/activity_animated_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.annotation.DynamicMarkerChangeActivity"
@@ -71,7 +71,7 @@
android:label="@string/activity_dynamic_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.annotation.PressForMarkerActivity"
@@ -79,7 +79,7 @@
android:label="@string/activity_press_for_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.camera.CameraAnimationTypeActivity"
@@ -87,7 +87,7 @@
android:label="@string/activity_camera_animation_types">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.camera.CameraPositionActivity"
@@ -95,7 +95,7 @@
android:label="@string/activity_camera_position">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.camera.LatLngBoundsActivity"
@@ -104,7 +104,7 @@
android:screenOrientation="portrait">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.fragment.MapFragmentActivity"
@@ -112,7 +112,7 @@
android:label="@string/activity_map_fragment">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment" />
+ android:value="@string/category_fragment"/>
</activity>
<activity
android:name=".activity.fragment.SupportMapFragmentActivity"
@@ -120,7 +120,7 @@
android:label="@string/activity_map_fragment_suport">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment" />
+ android:value="@string/category_fragment"/>
</activity>
<activity
android:name=".activity.camera.ManualZoomActivity"
@@ -128,7 +128,7 @@
android:label="@string/activity_camera_zoom">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.camera.MaxMinZoomActivity"
@@ -136,7 +136,7 @@
android:label="@string/activity_minmax_zoom">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.customlayer.CustomLayerActivity"
@@ -144,7 +144,7 @@
android:label="@string/activity_custom_layer">
<meta-data
android:name="@string/category"
- android:value="@string/category_custom_layer" />
+ android:value="@string/category_custom_layer"/>
</activity>
<activity
android:name=".activity.userlocation.MyLocationTrackingModeActivity"
@@ -152,7 +152,7 @@
android:label="@string/activity_user_tracking_mode">
<meta-data
android:name="@string/category"
- android:value="@string/category_userlocation" />
+ android:value="@string/category_userlocation"/>
</activity>
<activity
android:name=".activity.userlocation.MyLocationDrawableActivity"
@@ -160,7 +160,7 @@
android:label="@string/activity_user_tracking_customization">
<meta-data
android:name="@string/category"
- android:value="@string/category_userlocation" />
+ android:value="@string/category_userlocation"/>
</activity>
<activity
android:name=".activity.userlocation.MyLocationTintActivity"
@@ -168,7 +168,7 @@
android:label="@string/activity_user_dot_color">
<meta-data
android:name="@string/category"
- android:value="@string/category_userlocation" />
+ android:value="@string/category_userlocation"/>
</activity>
<activity
android:name=".activity.userlocation.MyLocationToggleActivity"
@@ -176,7 +176,7 @@
android:label="@string/activity_user_location_toggle">
<meta-data
android:name="@string/category"
- android:value="@string/category_userlocation" />
+ android:value="@string/category_userlocation"/>
</activity>
<activity
android:name=".activity.annotation.PolygonActivity"
@@ -184,7 +184,7 @@
android:label="@string/activity_polygon">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.annotation.PolylineActivity"
@@ -192,7 +192,7 @@
android:label="@string/activity_polyline">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.directions.DirectionsActivity"
@@ -200,7 +200,7 @@
android:label="@string/activity_directions">
<meta-data
android:name="@string/category"
- android:value="@string/category_directions" />
+ android:value="@string/category_directions"/>
</activity>
<activity
android:name=".activity.geocoding.GeocoderActivity"
@@ -208,7 +208,7 @@
android:label="@string/activity_geocoder">
<meta-data
android:name="@string/category"
- android:value="@string/category_geocoding" />
+ android:value="@string/category_geocoding"/>
</activity>
<activity
android:name=".activity.camera.ScrollByActivity"
@@ -216,7 +216,7 @@
android:label="@string/activity_scroll_by">
<meta-data
android:name="@string/category"
- android:value="@string/category_camera" />
+ android:value="@string/category_camera"/>
</activity>
<activity
android:name=".activity.maplayout.MapPaddingActivity"
@@ -225,7 +225,7 @@
android:screenOrientation="portrait">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout" />
+ android:value="@string/category_maplayout"/>
</activity>
<activity
android:name=".activity.maplayout.DebugModeActivity"
@@ -233,7 +233,7 @@
android:label="@string/activity_debug_mode">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout" />
+ android:value="@string/category_maplayout"/>
</activity>
<activity
android:name=".activity.offline.OfflineActivity"
@@ -241,7 +241,7 @@
android:label="@string/activity_offline">
<meta-data
android:name="@string/category"
- android:value="@string/category_offline" />
+ android:value="@string/category_offline"/>
</activity>
<activity
android:name=".activity.imagegenerator.SnapshotActivity"
@@ -249,7 +249,7 @@
android:label="@string/activity_snapshot">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator" />
+ android:value="@string/category_imagegenerator"/>
</activity>
<activity
android:name=".activity.maplayout.DoubleMapActivity"
@@ -257,7 +257,7 @@
android:label="@string/activity_double_map">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout" />
+ android:value="@string/category_maplayout"/>
</activity>
<activity
android:name=".activity.annotation.MarkerViewActivity"
@@ -265,7 +265,7 @@
android:label="@string/activity_view_marker">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.annotation.MarkerViewScaleActivity"
@@ -273,7 +273,7 @@
android:label="@string/activity_view_marker_scale">
<meta-data
android:name="@string/category"
- android:value="@string/category_annotation" />
+ android:value="@string/category_annotation"/>
</activity>
<activity
android:name=".activity.navigation.LocationPickerActivity"
@@ -281,7 +281,7 @@
android:label="@string/activity_location_picker">
<meta-data
android:name="@string/category"
- android:value="@string/category_navigation" />
+ android:value="@string/category_navigation"/>
</activity>
<activity
android:name=".activity.fragment.ViewPagerActivity"
@@ -289,7 +289,7 @@
android:label="@string/activity_viewpager">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment" />
+ android:value="@string/category_fragment"/>
</activity>
<activity
android:name=".activity.maplayout.NavigationDrawerActivity"
@@ -298,7 +298,7 @@
android:theme="@style/AppTheme.ActionBar.Transparent">
<meta-data
android:name="@string/category"
- android:value="@string/category_fragment" />
+ android:value="@string/category_fragment"/>
</activity>
<activity
android:name=".activity.style.RuntimeStyleActivity"
@@ -306,7 +306,7 @@
android:label="@string/activity_runtime_style">
<meta-data
android:name="@string/category"
- android:value="@string/category_style" />
+ android:value="@string/category_style"/>
</activity>
<activity
android:name=".activity.style.GeoJsonClusteringActivity"
@@ -323,7 +323,7 @@
android:label="@string/activity_print">
<meta-data
android:name="@string/category"
- android:value="@string/category_imagegenerator" />
+ android:value="@string/category_imagegenerator"/>
</activity>
<activity
android:name=".activity.maplayout.SurfaceViewMediaControlActivity"
@@ -331,27 +331,53 @@
android:label="@string/activity_surfaceview_overlay">
<meta-data
android:name="@string/category"
- android:value="@string/category_maplayout" />
+ android:value="@string/category_maplayout"/>
+ </activity>
+
+ <!-- Features -->
+ <activity
+ android:name=".activity.feature.QueryRenderedFeaturesPropertiesActivity"
+ android:description="@string/description_query_rendered_feature_properties_point"
+ android:label="@string/activity_query_rendered_feature_properties">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_features" />
+ </activity>
+ <activity
+ android:name=".activity.feature.QueryRenderedFeaturesBoxCountActivity"
+ android:description="@string/description_query_rendered_features_box_count"
+ android:label="@string/activity_query_rendered_features_box_count">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_features" />
+ </activity>
+ <activity
+ android:name=".activity.feature.QueryRenderedFeaturesBoxHighlightActivity"
+ android:description="@string/description_query_rendered_features_box_highlight"
+ android:label="@string/activity_query_rendered_features_box_highlight">
+ <meta-data
+ android:name="@string/category"
+ android:value="@string/category_features" />
</activity>
<!-- For Unit tests -->
- <activity android:name=".activity.style.RuntimeStyleTestActivity" />
- <activity android:name=".activity.style.RuntimeStyleTimingTestActivity" />
+ <activity android:name=".activity.style.RuntimeStyleTestActivity"/>
+ <activity android:name=".activity.style.RuntimeStyleTimingTestActivity"/>
<!-- Configuration Settings -->
<meta-data
android:name="com.mapbox.TestEventsServer"
- android:value="https://cloudfront-staging.tilestream.net" />
+ android:value="https://cloudfront-staging.tilestream.net"/>
<meta-data
android:name="com.mapbox.TestEventsAccessToken"
- android:value="sk.eyJ1IjoiYmxlZWdlIiwiYSI6InNpcml1c2x5In0.KyT-boMyC_xZYTYojTc8zg" />
+ android:value="sk.eyJ1IjoiYmxlZWdlIiwiYSI6InNpcml1c2x5In0.KyT-boMyC_xZYTYojTc8zg"/>
<!-- Comment out this setting to switch to external storage (and disable internal) in your app -->
<!-- <meta-data -->
<!-- android:name="com.mapbox.SetStorageExternal" -->
<!-- android:value="true" /> -->
- <service android:name="com.mapbox.mapboxsdk.telemetry.TelemetryService" />
+ <service android:name="com.mapbox.mapboxsdk.telemetry.TelemetryService"/>
</application>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
new file mode 100644
index 0000000000..532bb1c18c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxCountActivity.java
@@ -0,0 +1,157 @@
+package com.mapbox.mapboxsdk.testapp.activity.feature;
+
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.google.gson.JsonElement;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Feature;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Demo's query rendered features
+ */
+public class QueryRenderedFeaturesBoxCountActivity extends AppCompatActivity {
+ private static final String TAG = QueryRenderedFeaturesBoxCountActivity.class.getSimpleName();
+
+ public MapView mapView;
+ private MapboxMap mapboxMap;
+ private Marker marker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_query_features_box);
+ setupActionBar();
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ final View selectionBox = findViewById(R.id.selection_box);
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @SuppressWarnings("ConstantConditions")
+ @Override
+ public void onMapReady(final MapboxMap mapboxMap) {
+ QueryRenderedFeaturesBoxCountActivity.this.mapboxMap = mapboxMap;
+
+
+ selectionBox.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ int left = selectionBox.getLeft() - mapView.getLeft();
+ RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
+ Log.i(TAG, String.format("Querying box %s", box));
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box);
+
+ //Show count
+ Toast.makeText(QueryRenderedFeaturesBoxCountActivity.this, String.format("%s features in box", features.size()), Toast.LENGTH_SHORT).show();
+
+ //Debug output
+ debugOutput(features);
+ }
+ });
+
+ //Little taste of home
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 16));
+ }
+ });
+
+ }
+
+ private void debugOutput(List<Feature> features) {
+ Log.i(TAG, String.format("Got %s features", features.size()));
+ for (Feature feature : features) {
+ if (feature != null) {
+ Log.i(TAG, String.format("Got feature %s with %s properties and Geometry %s",
+ feature.getId(),
+ feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
+ feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>")
+ );
+ if (feature.getProperties() != null) {
+ for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
+ Log.i(TAG, String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ }
+ }
+ } else {
+ Log.i(TAG, "Got NULL feature %s");
+ }
+ }
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
new file mode 100644
index 0000000000..53937b4eca
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesBoxHighlightActivity.java
@@ -0,0 +1,155 @@
+package com.mapbox.mapboxsdk.testapp.activity.feature;
+
+import android.graphics.Color;
+import android.graphics.RectF;
+import android.os.Bundle;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.google.gson.JsonElement;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.annotations.PolygonOptions;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Feature;
+import com.mapbox.services.commons.geojson.Polygon;
+import com.mapbox.services.commons.models.Position;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Demo's query rendered features
+ */
+public class QueryRenderedFeaturesBoxHighlightActivity extends AppCompatActivity {
+ private static final String TAG = QueryRenderedFeaturesBoxHighlightActivity.class.getSimpleName();
+
+ public MapView mapView;
+ private MapboxMap mapboxMap;
+ private Marker marker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_query_features_box);
+ setupActionBar();
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ final View selectionBox = findViewById(R.id.selection_box);
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @SuppressWarnings("ConstantConditions")
+ @Override
+ public void onMapReady(final MapboxMap mapboxMap) {
+ QueryRenderedFeaturesBoxHighlightActivity.this.mapboxMap = mapboxMap;
+
+
+ selectionBox.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ //Query
+ int top = selectionBox.getTop() - mapView.getTop();
+ int left = selectionBox.getLeft() - mapView.getLeft();
+ RectF box = new RectF(left, top, left + selectionBox.getWidth(), top + selectionBox.getHeight());
+ Log.i(TAG, String.format("Querying box %s for buildings", box));
+ List<Feature> features = mapboxMap.queryRenderedFeatures(box, "building");
+
+ //Show count
+ Toast.makeText(QueryRenderedFeaturesBoxHighlightActivity.this, String.format("%s features in box", features.size()), Toast.LENGTH_SHORT).show();
+
+ for (Feature feature : features) {
+ if (feature.getGeometry() instanceof Polygon) {
+ Polygon building = (Polygon) feature.getGeometry();
+
+ //Convert outer ring
+ List<List<Position>> coordinates = building.getCoordinates();
+ List<Position> outerRing = coordinates.get(0);
+ List<LatLng> points = new ArrayList<LatLng>();
+ for (Position position : outerRing) {
+ points.add(new LatLng(position.getLatitude(), position.getLongitude()));
+ }
+
+ mapboxMap.addPolygon(new PolygonOptions().addAll(points).fillColor(Color.RED));
+ }
+ }
+ }
+ });
+
+ //Little taste of home
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 16));
+ }
+ });
+
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
new file mode 100644
index 0000000000..7ce1a68a79
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesPropertiesActivity.java
@@ -0,0 +1,258 @@
+package com.mapbox.mapboxsdk.testapp.activity.feature;
+
+import android.graphics.Color;
+import android.graphics.PointF;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.support.annotation.NonNull;
+import android.support.v7.app.ActionBar;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.google.gson.JsonElement;
+import com.mapbox.mapboxsdk.annotations.BaseMarkerOptions;
+import com.mapbox.mapboxsdk.annotations.Marker;
+import com.mapbox.mapboxsdk.camera.CameraUpdateFactory;
+import com.mapbox.mapboxsdk.geometry.LatLng;
+import com.mapbox.mapboxsdk.maps.MapView;
+import com.mapbox.mapboxsdk.maps.MapboxMap;
+import com.mapbox.mapboxsdk.maps.OnMapReadyCallback;
+import com.mapbox.mapboxsdk.testapp.R;
+import com.mapbox.services.commons.geojson.Feature;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Demo's query rendered features
+ */
+public class QueryRenderedFeaturesPropertiesActivity extends AppCompatActivity {
+ private static final String TAG = QueryRenderedFeaturesPropertiesActivity.class.getSimpleName();
+
+ public MapView mapView;
+ private MapboxMap mapboxMap;
+ private Marker marker;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_query_features_point);
+ setupActionBar();
+
+ final float density = getResources().getDisplayMetrics().density;
+
+ //Initialize map as normal
+ mapView = (MapView) findViewById(R.id.mapView);
+ mapView.onCreate(savedInstanceState);
+ mapView.getMapAsync(new OnMapReadyCallback() {
+ @Override
+ public void onMapReady(final MapboxMap mapboxMap) {
+ QueryRenderedFeaturesPropertiesActivity.this.mapboxMap = mapboxMap;
+
+ //Add custom window adapter
+ addCustomInfoWindowAdapter(mapboxMap);
+
+ //Add a click listener
+ mapboxMap.setOnMapClickListener(new MapboxMap.OnMapClickListener() {
+ @Override
+ public void onMapClick(@NonNull LatLng point) {
+ //Query
+ final PointF pixel = mapboxMap.getProjection().toScreenLocation(point);
+ Log.i(TAG, String.format("Requesting features for %sx%s (%sx%s adjusted for density)", pixel.x, pixel.y, pixel.x / density, pixel.y / density));
+ List<Feature> features = mapboxMap.queryRenderedFeatures(pixel);
+
+ //Debug output
+ debugOutput(features);
+
+ //Remove any previous markers
+ if (marker != null) {
+ mapboxMap.removeMarker(marker);
+ }
+
+ //Add a marker on the clicked point
+ marker = mapboxMap.addMarker(new CustomMarkerOptions().position(point).features(features));
+ mapboxMap.selectMarker(marker);
+ }
+ });
+
+ //Little taste of home
+ mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 16));
+ }
+ });
+
+ }
+
+ private void debugOutput(List<Feature> features) {
+ Log.i(TAG, String.format("Got %s features", features.size()));
+ for (Feature feature : features) {
+ if (feature != null) {
+ Log.i(TAG, String.format("Got feature %s with %s properties and Geometry %s",
+ feature.getId(),
+ feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<null>",
+ feature.getGeometry() != null ? feature.getGeometry().getClass().getSimpleName() : "<null>")
+ );
+ if (feature.getProperties() != null) {
+ for (Map.Entry<String, JsonElement> entry : feature.getProperties().entrySet()) {
+ Log.i(TAG, String.format("Prop %s - %s", entry.getKey(), entry.getValue()));
+ }
+ }
+ } else {
+ Log.i(TAG, "Got NULL feature %s");
+ }
+ }
+ }
+
+ private void addCustomInfoWindowAdapter(MapboxMap mapboxMap) {
+ mapboxMap.setInfoWindowAdapter(new MapboxMap.InfoWindowAdapter() {
+
+ private TextView row(String text) {
+ TextView view = new TextView(QueryRenderedFeaturesPropertiesActivity.this);
+ view.setText(text);
+ return view;
+ }
+
+ private int tenDp = (int) getResources().getDimension(R.dimen.attr_margin);
+
+ @Override
+ public View getInfoWindow(@NonNull Marker marker) {
+ CustomMarker customMarker = (CustomMarker) marker;
+ LinearLayout view = new LinearLayout(QueryRenderedFeaturesPropertiesActivity.this);
+ view.setOrientation(LinearLayout.VERTICAL);
+ view.setBackgroundColor(Color.WHITE);
+
+ if (customMarker.features.size() > 0) {
+ view.addView(row(String.format("Found %s features", customMarker.features.size())));
+ Feature feature = customMarker.features.get(0);
+ for (Map.Entry<String, JsonElement> prop : feature.getProperties().entrySet()) {
+ view.addView(row(String.format("%s: %s", prop.getKey(), prop.getValue())));
+ }
+ } else {
+ view.addView(row("No features here"));
+ }
+
+ return view;
+ }
+ });
+ }
+
+ public MapboxMap getMapboxMap() {
+ return mapboxMap;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mapView.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ mapView.onPause();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ mapView.onSaveInstanceState(outState);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mapView.onDestroy();
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ mapView.onLowMemory();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ private void setupActionBar() {
+ Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ final ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ actionBar.setDisplayShowHomeEnabled(true);
+ }
+ }
+
+ private static class CustomMarker extends Marker {
+
+ private final List<Feature> features;
+
+ public CustomMarker(BaseMarkerOptions baseMarkerOptions, List<Feature> features) {
+ super(baseMarkerOptions);
+ this.features = features;
+ }
+ }
+
+ private static class CustomMarkerOptions extends BaseMarkerOptions<CustomMarker, CustomMarkerOptions> {
+
+
+ private List<Feature> features;
+
+ public CustomMarkerOptions features(List<Feature> features) {
+ this.features = features;
+ return this;
+ }
+
+ public CustomMarkerOptions() {
+ }
+
+ private CustomMarkerOptions(Parcel in) {
+ //Should implement this
+ }
+
+ @Override
+ public CustomMarkerOptions getThis() {
+ return this;
+ }
+
+ @Override
+ public CustomMarker getMarker() {
+ return new CustomMarker(this, features);
+ }
+
+ public static final Parcelable.Creator<CustomMarkerOptions> CREATOR
+ = new Parcelable.Creator<CustomMarkerOptions>() {
+ public CustomMarkerOptions createFromParcel(Parcel in) {
+ return new CustomMarkerOptions(in);
+ }
+
+ public CustomMarkerOptions[] newArray(int size) {
+ return new CustomMarkerOptions[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ //Should implement this
+ }
+ }
+}
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_box.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_box.xml
new file mode 100644
index 0000000000..e1d710880c
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_box.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar" />
+
+ <FrameLayout
+ android:id="@+id/selection_box"
+ android:layout_width="150dp"
+ android:layout_height="150dp"
+ android:layout_centerInParent="true"
+ android:alpha="0.3"
+ android:background="#1d72da" />
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_point.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_point.xml
new file mode 100644
index 0000000000..4c0d067c14
--- /dev/null
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/layout/activity_query_features_point.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <android.support.v7.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="@color/primary"
+ android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"/>
+
+ <com.mapbox.mapboxsdk.maps.MapView
+ android:id="@+id/mapView"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar"/>
+
+</RelativeLayout>
diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
index 1cdf449a6c..4436e40200 100644
--- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
+++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/res/values/strings.xml
@@ -57,6 +57,9 @@
<string name="activity_geojson_clustering">GeoJson Clustering</string>
<string name="activity_print">Print a map</string>
<string name="activity_surfaceview_overlay">SurfaceView MediaOverlay</string>
+ <string name="activity_query_rendered_feature_properties">Query feature properties</string>
+ <string name="activity_query_rendered_features_box_count">Count features in box</string>
+ <string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
<string name="title_activity_navigation_drawer">Android SDK View integration</string>
<!-- Description -->
@@ -100,6 +103,9 @@
<string name="description_print">Shows how to print a map</string>
<string name="description_navigation_drawer">Test animation of Android SDK View components</string>
<string name="description_surfaceview_mediacontrols">Test overlaying SurfaceView</string>
+ <string name="description_query_rendered_feature_properties_point">Query rendered feature properties on click</string>
+ <string name="description_query_rendered_features_box_count">Count all rendered features in box</string>
+ <string name="description_query_rendered_features_box_highlight">Hightligh buildings in box</string>
<string name="menuitem_title_concurrent_infowindow">Concurrent Open InfoWindows</string>
<string name="menuitem_title_deselect_markers_on_tap">Deselect Markers On Tap</string>
@@ -121,6 +127,7 @@
<string name="category_userlocation">User Location</string>
<string name="category_navigation">Navigation</string>
<string name="category_style">Styling</string>
+ <string name="category_features">Features</string>
<string name="action_visible_bounds_explanation">Center map around 2 markers</string>
<string name="action_remove_polylines">Remove polylines</string>
diff --git a/platform/android/src/conversion/collection.hpp b/platform/android/src/conversion/collection.hpp
new file mode 100644
index 0000000000..4256d5f969
--- /dev/null
+++ b/platform/android/src/conversion/collection.hpp
@@ -0,0 +1,42 @@
+#pragma once
+
+#include "conversion.hpp"
+#include "constant.hpp"
+
+#include <mbgl/util/optional.hpp>
+#include <jni/jni.hpp>
+
+#include <vector>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+/**
+ * Convert jarray -> ArrayList
+ */
+template <class T>
+inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray<T>& array) {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/util/Arrays")).release();
+ static jni::jmethodID* asList = &jni::GetStaticMethodID(env, *javaClass, "asList", "([Ljava/lang/Object;)Ljava/util/List;");
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *asList, array));
+}
+
+// Java -> C++
+
+
+inline std::vector<std::string> toVector(JNIEnv& env, jni::jarray<jni::jobject>& array) {
+ std::vector<std::string> vector;
+ std::size_t len = jni::GetArrayLength(env, array);
+
+ for (std::size_t i = 0; i < len; i++) {
+ jni::jstring* jstr = reinterpret_cast<jni::jstring*>(jni::GetObjectArrayElement(env, array, i));
+ vector.push_back(*convert<std::string, jni::String>(env, jni::String(jstr)));
+ }
+
+ return vector;
+}
+
+}
+}
+}
diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp
index 9a570d3717..f1a8171b99 100644
--- a/platform/android/src/conversion/constant.hpp
+++ b/platform/android/src/conversion/constant.hpp
@@ -24,6 +24,13 @@ struct Converter<jni::jobject*, bool> {
};
template <>
+struct Converter<jni::jboolean, bool> {
+ Result<jni::jboolean> operator()(jni::JNIEnv&, const bool& value) const {
+ return {(jni::jboolean) value};
+ }
+};
+
+template <>
struct Converter<jni::jobject*, float> {
Result<jni::jobject*> operator()(jni::JNIEnv& env, const float& value) const {
static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Float")).release();
@@ -33,6 +40,45 @@ struct Converter<jni::jobject*, float> {
};
template <>
+struct Converter<jni::jfloat, float> {
+ Result<jni::jfloat> operator()(jni::JNIEnv&, const float& value) const {
+ return {(jni::jfloat) value};
+ }
+};
+
+
+template <>
+struct Converter<jni::jobject*, double> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const double& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Double")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(D)V");
+ return {&jni::NewObject(env, *javaClass, *constructor, (jfloat) value)};
+ }
+};
+
+template <>
+struct Converter<jni::jdouble, float> {
+ Result<jni::jdouble> operator()(jni::JNIEnv&, const double& value) const {
+ return {(jni::jdouble) value};
+ }
+};
+
+/**
+ * All integrals. java is limited to 64 bit signed, so...
+ * TODO: use BigDecimal for > 64 / unsigned?
+ */
+template<typename T>
+struct Converter<jni::jobject*, T, typename std::enable_if<std::is_integral<T>::value>::type> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const T& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "java/lang/Long")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(J)V");
+ return {&jni::NewObject(env, *javaClass, *constructor, (jlong) value)};
+ }
+};
+
+//TODO: convert integral types to primitive jni types
+
+template <>
struct Converter<jni::jobject*, std::string> {
Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::string& value) const {
return {jni::Make<jni::String>(env, value).Get()};
@@ -40,6 +86,13 @@ struct Converter<jni::jobject*, std::string> {
};
template <>
+struct Converter<jni::jstring*, std::string> {
+ Result<jni::jstring*> operator()(jni::JNIEnv& env, const std::string& value) const {
+ return {jni::Make<jni::String>(env, value).Get()};
+ }
+};
+
+template <>
struct Converter<jni::jobject*, Color> {
Result<jni::jobject*> operator()(jni::JNIEnv& env, const Color& value) const {
std::stringstream sstream;
@@ -90,6 +143,15 @@ struct Converter<jni::jobject*, std::vector<float>> {
}
};
+// Java -> C++
+
+template <>
+struct Converter<std::string, jni::String> {
+ Result<std::string> operator()(jni::JNIEnv& env, const jni::String& value) const {
+ return { jni::Make<std::string>(env, value) };
+ }
+};
+
} // namespace conversion
} // namespace style
} // namespace mbgl
diff --git a/platform/android/src/conversion/conversion.hpp b/platform/android/src/conversion/conversion.hpp
index ea8a31bcf2..1277f3f67e 100644
--- a/platform/android/src/conversion/conversion.hpp
+++ b/platform/android/src/conversion/conversion.hpp
@@ -37,7 +37,7 @@ public:
}
};
-template <class T, class V>
+template <class T, class V, class Enable = void>
struct Converter;
template <class T, typename V, class...Args>
diff --git a/platform/android/src/geometry/conversion/feature.hpp b/platform/android/src/geometry/conversion/feature.hpp
new file mode 100644
index 0000000000..857b76da50
--- /dev/null
+++ b/platform/android/src/geometry/conversion/feature.hpp
@@ -0,0 +1,209 @@
+#pragma once
+
+#include "../../conversion/constant.hpp"
+#include "../../conversion/conversion.hpp"
+#include "geometry.hpp"
+
+#include <mbgl/util/feature.hpp>
+#include <mapbox/variant.hpp>
+#include <mapbox/geometry.hpp>
+
+#include <jni/jni.hpp>
+#include "../../jni/local_object.hpp"
+
+#include <string>
+#include <array>
+#include <vector>
+#include <sstream>
+
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+/**
+ * Turn feature identifier into std::string
+ */
+class FeatureIdVisitor {
+public:
+
+ template<class T>
+ std::string operator()(const T& i) const {
+ return std::to_string(i);
+ }
+
+ std::string operator()(const std::string& i) const {
+ return i;
+ }
+
+ std::string operator()(const std::nullptr_t&) const {
+ return "";
+ }
+
+};
+
+/**
+ * Turn properties into Java GSON JsonObject's
+ */
+class PropertyValueEvaluator {
+public:
+ jni::JNIEnv& env;
+
+ /**
+ * null
+ */
+ jni::jobject* operator()(const mapbox::geometry::null_value_t &) const {
+ return (jni::jobject*) nullptr;
+ }
+
+ /**
+ * Boolean primitive
+ */
+ jni::jobject* operator()(const bool& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Z)V");
+
+ //Create JsonPrimitive
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, bool>(env, value));
+ jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, *converted);
+
+ return object;
+ }
+
+ /**
+ * String primitive
+ */
+ jni::jobject* operator()(const std::string& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/String;)V");
+
+ //Create JsonPrimitive
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, value));
+ jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get());
+
+ return object;
+ }
+
+ /**
+ * Number primitives
+ */
+ template <class Number>
+ jni::jobject* operator()(const Number& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonPrimitive")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "(Ljava/lang/Number;)V");
+
+ //Create JsonPrimitive
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, Number>(env, value));
+ jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get());
+
+ return object;
+ }
+
+
+ /**
+ * Json Array
+ */
+ jni::jobject* operator()(const std::vector<mbgl::Value> &values) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonArray")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");;
+ static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Lcom/google/gson/JsonElement;)V");
+
+ //Create json array
+ jni::jobject* jarray = &jni::NewObject(env, *javaClass, *constructor);
+
+ //Add values
+ for (const auto &v : values) {
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(v, *this));
+ jni::CallMethod<void>(env, jarray, *add, converted.get());
+ }
+
+ return jarray;
+ }
+
+ /**
+ * Json Object
+ */
+ jni::jobject* operator()(const std::unordered_map<std::string, mbgl::Value> &value) const {
+ //TODO: clean up duplication here
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");;
+ static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V");
+
+ //Create json object
+ jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor);
+
+ //Add items
+ for (auto &item : value) {
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, *this));
+ jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first));
+ jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get());
+ }
+
+ return jsonObject;
+ }
+};
+
+template <>
+struct Converter<jni::jobject*, std::unordered_map<std::string, mbgl::Value>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::unordered_map<std::string, mbgl::Value>& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/google/gson/JsonObject")).release();
+ static jni::jmethodID* constructor = &jni::GetMethodID(env, *javaClass, "<init>", "()V");;
+ static jni::jmethodID* add = &jni::GetMethodID(env, *javaClass, "add", "(Ljava/lang/String;Lcom/google/gson/JsonElement;)V");
+
+ //Create json object
+ jni::jobject* jsonObject = &jni::NewObject(env, *javaClass, *constructor);
+
+ //Add items
+ PropertyValueEvaluator evaluator {env};
+ for (auto &item : value) {
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, evaluator));
+ jni::LocalObject<jni::jobject> key = jni::NewLocalObject(env, *convert<jni::jobject*, std::string>(env, item.first));
+ jni::CallMethod<void>(env, jsonObject, *add, key.get(), converted.get());
+ }
+
+ return {jsonObject};
+ }
+};
+
+
+template <>
+struct Converter<jni::jobject*, mbgl::Feature> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release();
+ static jni::jmethodID* fromGeometry = &jni::GetStaticMethodID(env, *javaClass, "fromGeometry", "(Lcom/mapbox/services/commons/geojson/Geometry;Lcom/google/gson/JsonObject;Ljava/lang/String;)Lcom/mapbox/services/commons/geojson/Feature;");
+
+ //Convert Id
+ FeatureIdVisitor idEvaluator;
+ std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : "";
+ jni::LocalObject<jni::jobject> jid = jni::NewLocalObject(env, *convert<jni::jobject*>(env, id));
+
+ //Convert properties
+ jni::LocalObject<jni::jobject> properties = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.properties));
+
+ //Convert geometry
+ jni::LocalObject<jni::jobject> geometry = jni::NewLocalObject(env, *convert<jni::jobject*>(env, value.geometry));
+
+ //Create feature
+ return {reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometry, geometry.get(), properties.get(), jid.get()))};
+ }
+};
+
+template <>
+struct Converter<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>> {
+ Result<jni::jarray<jni::jobject>*> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const {
+ static jni::jclass* featureClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release();
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *featureClass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, *convert<jni::jobject*, mbgl::Feature>(env, value.at(i)));
+ jni::SetObjectArrayElement(env, jarray, i, converted.get());
+ }
+
+ return {&jarray};
+ }
+};
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/geometry/conversion/geometry.hpp b/platform/android/src/geometry/conversion/geometry.hpp
new file mode 100644
index 0000000000..385ba9034e
--- /dev/null
+++ b/platform/android/src/geometry/conversion/geometry.hpp
@@ -0,0 +1,184 @@
+#pragma once
+
+#include "../../conversion/constant.hpp"
+#include "../../conversion/collection.hpp"
+
+#include <mapbox/geometry.hpp>
+#include <jni/jni.hpp>
+#include "../../jni/local_object.hpp"
+
+namespace mbgl {
+namespace android {
+namespace conversion {
+
+/**
+ * Turn mapbox::geometry type into Java GeoJson Geometries
+ */
+template <typename T>
+class GeometryEvaluator {
+public:
+
+ jni::JNIEnv& env;
+
+ /**
+ * Point (double[])
+ */
+ jni::jobject* operator()(const mapbox::geometry::point<T> &geometry) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Point")).release();
+ static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([D)Lcom/mapbox/services/commons/geojson/Point;");
+
+ //Create Point
+ jni::LocalObject<jni::jarray<jni::jdouble>> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, position.get()));
+ }
+
+ /**
+ * LineString (double[][])
+ */
+ jni::jobject* operator()(const mapbox::geometry::line_string<T> &geometry) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/LineString")).release();
+ static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/LineString;");
+
+ //Create
+ jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get()));
+ }
+
+ /**
+ * MultiPoint (double[][])
+ */
+ jni::jobject* operator()(const mapbox::geometry::multi_point<T> &geometry) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPoint")).release();
+ static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[D)Lcom/mapbox/services/commons/geojson/MultiPoint;");
+
+ //Create
+ jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, coordinates.get()));
+ }
+
+ /**
+ * Polygon (double[][][])
+ */
+ jni::jobject* operator()(const mapbox::geometry::polygon<T> &geometry) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Polygon")).release();
+ static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/Polygon;");
+
+ //Create
+ jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get()));
+ }
+
+ /**
+ * MultiLineString (double[][][])
+ */
+ jni::jobject* operator()(const mapbox::geometry::multi_line_string<T> &geometry) const {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiLineString")).release();
+ static jni::jmethodID* fromCoordinates = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[D)Lcom/mapbox/services/commons/geojson/MultiLineString;");
+
+ //Create
+ jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromCoordinates, shape.get()));
+ }
+
+ /**
+ * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D
+ */
+ jni::jobject* operator()(const mapbox::geometry::multi_polygon<T> &geometry) const {
+ static jni::jclass* listClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release();
+ jni::LocalObject<jni::jarray<jni::jobject>> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *listClass));
+
+ for(size_t i = 0; i < geometry.size(); i = i + 1) {
+ jni::LocalObject<jni::jarray<jni::jobject>> shape = jni::NewLocalObject(env, toShape<>(env, geometry.at(i)));
+ jni::SetObjectArrayElement(env, *jarray, i, shape.get());
+ }
+
+ //Create the MultiPolygon
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/MultiPolygon")).release();
+ static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromCoordinates", "([[[[D)Lcom/mapbox/services/commons/geojson/MultiPolygon;");
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, jarray.get()));
+ }
+
+ /**
+ * GeometryCollection
+ */
+ jni::jobject* operator()(const mapbox::geometry::geometry_collection<T> &collection) const {
+ static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Geometry")).release();
+ jni::LocalObject<jni::jarray<jni::jobject>> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, collection.size(), *geometryClass));
+
+ for(size_t i = 0; i < collection.size(); i = i + 1) {
+ auto& geometry = collection.at(i);
+ jni::LocalObject<jni::jobject> converted = jni::NewLocalObject(env, mapbox::geometry::geometry<T>::visit(geometry, *this));
+ jni::SetObjectArrayElement(env, *jarray, i, converted.get());
+ }
+
+ //Turn into array list and create the GeometryCollection
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/GeometryCollection")).release();
+ static jni::jmethodID* fromGeometries = &jni::GetStaticMethodID(env, *javaClass, "fromGeometries", "(Ljava/util/List;)Lcom/mapbox/services/commons/geojson/GeometryCollection;");
+
+ jni::LocalObject<jni::jobject> list = jni::NewLocalObject(env, toArrayList<>(env, *jarray));
+ return reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometries, list.get()));
+ }
+
+private:
+
+ /**
+ * x, y -> jarray<jdouble> ([x,y])
+ */
+ static jni::jarray<jni::jdouble>* toGeoJsonPosition(JNIEnv& env, double x, double y) {
+ jni::jarray<jni::jdouble>& jarray = jni::NewArray<jni::jdouble>(env, 2);
+ jni::jdouble array[] = {x, y};
+ jni::SetArrayRegion(env, jarray, 0, 2, array);
+ return &jarray;
+ }
+
+ /**
+ * vector<point<T>> -> jarray<jobject> (double[][]) -> [D + Object array == [[D
+ */
+ static jni::jarray<jni::jobject>* toGeoJsonCoordinates(JNIEnv& env, std::vector<mapbox::geometry::point<T>> points) {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[D")).release();
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, points.size(), *javaClass);
+
+ for(size_t i = 0; i < points.size(); i = i + 1) {
+ mapbox::geometry::point<T> point = points.at(i);
+ jni::LocalObject<jni::jarray<jni::jdouble>> position = jni::NewLocalObject(env, toGeoJsonPosition(env, point.x, point.y));
+ jni::SetObjectArrayElement(env, jarray, i, position.get());
+ }
+
+ return &jarray;
+ }
+
+ /**
+ * polygon<T>
+ * multi_line_string<T>
+ * -> jarray<jobject> (double[][][]) -> [[D + Object array == [[[D
+ */
+ template <class SHAPE>
+ static jni::jarray<jni::jobject>* toShape(JNIEnv& env, SHAPE value) {
+ static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[D")).release();
+ jni::jarray<jni::jobject>& jarray = jni::NewObjectArray(env, value.size(), *javaClass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ jni::LocalObject<jni::jarray<jni::jobject>> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, value.at(i)));
+ jni::SetObjectArrayElement(env, jarray, i, coordinates.get());
+ }
+
+ return &jarray;
+ }
+};
+
+/**
+ * mapbox::geometry::geometry<T> -> Java GeoJson Geometry<>
+ */
+template <class T>
+struct Converter<jni::jobject*, mapbox::geometry::geometry<T>> {
+ Result<jni::jobject*> operator()(jni::JNIEnv& env, const mapbox::geometry::geometry<T>& value) const {
+ GeometryEvaluator<double> evaluator { env } ;
+ jni::jobject* converted = mapbox::geometry::geometry<double>::visit(value, evaluator);
+ return {converted};
+ }
+};
+
+
+}
+}
+}
diff --git a/platform/android/src/jni.cpp b/platform/android/src/jni.cpp
index 89dbfe1cc2..acaa5e8b42 100755
--- a/platform/android/src/jni.cpp
+++ b/platform/android/src/jni.cpp
@@ -14,6 +14,10 @@
#include "style/layers/layers.hpp"
#include "style/sources/sources.hpp"
+#include "conversion/conversion.hpp"
+#include "conversion/collection.hpp"
+#include "geometry/conversion/feature.hpp"
+
#include <mbgl/map/map.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/annotation/annotation.hpp>
@@ -23,9 +27,12 @@
#include <mbgl/platform/log.hpp>
#include <mbgl/storage/network_status.hpp>
#include <mbgl/util/exception.hpp>
+#include <mbgl/util/optional.hpp>
#include <mbgl/util/string.hpp>
#include <mbgl/util/run_loop.hpp>
+#include <mapbox/geometry.hpp>
+
#include <jni/jni.hpp>
#pragma clang diagnostic ignored "-Wunused-parameter"
@@ -929,6 +936,40 @@ void nativeSetVisibleCoordinateBounds(JNIEnv *env, jni::jobject* obj, jlong nati
nativeMapView->getMap().easeTo(cameraOptions, animationOptions);
}
+jni::jarray<jni::jobject>* nativeQueryRenderedFeaturesForPoint(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat x, jni::jfloat y, jni::jarray<jni::jobject>* layerIds) {
+ using namespace mbgl::android::conversion;
+ using namespace mapbox::geometry;
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeQueryRenderedFeatures for Point");
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ mbgl::optional<std::vector<std::string>> layers;
+ if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) {
+ layers = toVector(*env, *layerIds);
+ }
+ point<double> point = {x, y};
+
+ return *convert<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>>(*env, nativeMapView->getMap().queryRenderedFeatures(point, layers));
+}
+
+jni::jarray<jni::jobject>* nativeQueryRenderedFeaturesForBox(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat left, jni::jfloat top, jni::jfloat right, jni::jfloat bottom, jni::jarray<jni::jobject>* layerIds) {
+ using namespace mbgl::android::conversion;
+ using namespace mapbox::geometry;
+
+ mbgl::Log::Debug(mbgl::Event::JNI, "nativeQueryRenderedFeatures for Box %.2f %.2f %.2f %.2f", left, top, right, bottom);
+ assert(nativeMapViewPtr != 0);
+ NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
+
+ mbgl::optional<std::vector<std::string>> layers;
+ if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) {
+ layers = toVector(*env, *layerIds);
+ }
+ box<double> box = { point<double>{ left, top}, point<double>{ right, bottom } };
+
+ return *convert<jni::jarray<jni::jobject>*, std::vector<mbgl::Feature>>(*env, nativeMapView->getMap().queryRenderedFeatures(box, layers));
+}
+
void nativeOnLowMemory(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeOnLowMemory");
assert(nativeMapViewPtr != 0);
@@ -1800,7 +1841,9 @@ extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
MAKE_NATIVE_METHOD(nativeAddSource, "(JLjava/lang/String;Lcom/mapbox/mapboxsdk/style/sources/Source;)V"),
MAKE_NATIVE_METHOD(nativeRemoveSource, "(JLjava/lang/String;)V"),
MAKE_NATIVE_METHOD(nativeSetContentPadding, "(JDDDD)V"),
- MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V")
+ MAKE_NATIVE_METHOD(nativeScheduleTakeSnapshot, "(J)V"),
+ MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForPoint, "(JFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;"),
+ MAKE_NATIVE_METHOD(nativeQueryRenderedFeaturesForBox, "(JFFFF[Ljava/lang/String;)[Lcom/mapbox/services/commons/geojson/Feature;")
);
// Offline begin
diff --git a/platform/android/src/jni/local_object.hpp b/platform/android/src/jni/local_object.hpp
new file mode 100644
index 0000000000..00fc4a1933
--- /dev/null
+++ b/platform/android/src/jni/local_object.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <jni/jni.hpp>
+
+namespace jni {
+
+ class LocalRefDeleter {
+ private:
+ JNIEnv* env = nullptr;
+
+ public:
+ LocalRefDeleter() = default;
+ LocalRefDeleter(JNIEnv& e) : env(&e) {}
+
+ void operator()(jobject* object) const {
+ if (object) {
+ assert(env);
+ DeleteLocalRef(*env, object);
+ }
+ }
+ };
+
+ template < class T >
+ using LocalObject = std::unique_ptr< T, LocalRefDeleter >;
+
+ /**
+ * Use a LocalObject to discard of local references as soon as possible
+ */
+ template < class T >
+ LocalObject<T> NewLocalObject(JNIEnv& env, T* t) {
+ return LocalObject<T>(t, LocalRefDeleter(env));
+ }
+}