From 4e211548d5029a353aaa8814c40599970a8098bd Mon Sep 17 00:00:00 2001 From: Ivo van Dongen Date: Thu, 4 Aug 2016 14:45:50 -0400 Subject: [android] #5869 - visible feature querying --- platform/android/src/conversion/collection.hpp | 42 +++++ platform/android/src/conversion/constant.hpp | 62 ++++++ platform/android/src/conversion/conversion.hpp | 2 +- .../android/src/geometry/conversion/feature.hpp | 209 +++++++++++++++++++++ .../android/src/geometry/conversion/geometry.hpp | 184 ++++++++++++++++++ platform/android/src/jni.cpp | 45 ++++- platform/android/src/jni/local_object.hpp | 33 ++++ 7 files changed, 575 insertions(+), 2 deletions(-) create mode 100644 platform/android/src/conversion/collection.hpp create mode 100644 platform/android/src/geometry/conversion/feature.hpp create mode 100644 platform/android/src/geometry/conversion/geometry.hpp create mode 100644 platform/android/src/jni/local_object.hpp (limited to 'platform/android/src') 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 +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +/** + * Convert jarray -> ArrayList + */ +template +inline jni::jobject* toArrayList(JNIEnv& env, jni::jarray& 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::CallStaticMethod(env, *javaClass, *asList, array)); +} + +// Java -> C++ + + +inline std::vector toVector(JNIEnv& env, jni::jarray& array) { + std::vector vector; + std::size_t len = jni::GetArrayLength(env, array); + + for (std::size_t i = 0; i < len; i++) { + jni::jstring* jstr = reinterpret_cast(jni::GetObjectArrayElement(env, array, i)); + vector.push_back(*convert(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 @@ -23,6 +23,13 @@ struct Converter { } }; +template <> +struct Converter { + Result operator()(jni::JNIEnv&, const bool& value) const { + return {(jni::jboolean) value}; + } +}; + template <> struct Converter { Result operator()(jni::JNIEnv& env, const float& value) const { @@ -32,6 +39,45 @@ struct Converter { } }; +template <> +struct Converter { + Result operator()(jni::JNIEnv&, const float& value) const { + return {(jni::jfloat) value}; + } +}; + + +template <> +struct Converter { + Result 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, "", "(D)V"); + return {&jni::NewObject(env, *javaClass, *constructor, (jfloat) value)}; + } +}; + +template <> +struct Converter { + Result 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 +struct Converter::value>::type> { + Result 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, "", "(J)V"); + return {&jni::NewObject(env, *javaClass, *constructor, (jlong) value)}; + } +}; + +//TODO: convert integral types to primitive jni types + template <> struct Converter { Result operator()(jni::JNIEnv& env, const std::string& value) const { @@ -39,6 +85,13 @@ struct Converter { } }; +template <> +struct Converter { + Result operator()(jni::JNIEnv& env, const std::string& value) const { + return {jni::Make(env, value).Get()}; + } +}; + template <> struct Converter { Result operator()(jni::JNIEnv& env, const Color& value) const { @@ -90,6 +143,15 @@ struct Converter> { } }; +// Java -> C++ + +template <> +struct Converter { + Result operator()(jni::JNIEnv& env, const jni::String& value) const { + return { jni::Make(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 +template struct Converter; template 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 +#include +#include + +#include +#include "../../jni/local_object.hpp" + +#include +#include +#include +#include + +#include + +namespace mbgl { +namespace android { +namespace conversion { + +/** + * Turn feature identifier into std::string + */ +class FeatureIdVisitor { +public: + + template + 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, "", "(Z)V"); + + //Create JsonPrimitive + jni::LocalObject converted = jni::NewLocalObject(env, *convert(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, "", "(Ljava/lang/String;)V"); + + //Create JsonPrimitive + jni::LocalObject converted = jni::NewLocalObject(env, *convert(env, value)); + jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); + + return object; + } + + /** + * Number primitives + */ + template + 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, "", "(Ljava/lang/Number;)V"); + + //Create JsonPrimitive + jni::LocalObject converted = jni::NewLocalObject(env, *convert(env, value)); + jni::jobject* object = &jni::NewObject(env, *javaClass, *constructor, converted.get()); + + return object; + } + + + /** + * Json Array + */ + jni::jobject* operator()(const std::vector &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, "", "()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 converted = jni::NewLocalObject(env, mbgl::Value::visit(v, *this)); + jni::CallMethod(env, jarray, *add, converted.get()); + } + + return jarray; + } + + /** + * Json Object + */ + jni::jobject* operator()(const std::unordered_map &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, "", "()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 converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, *this)); + jni::LocalObject key = jni::NewLocalObject(env, *convert(env, item.first)); + jni::CallMethod(env, jsonObject, *add, key.get(), converted.get()); + } + + return jsonObject; + } +}; + +template <> +struct Converter> { + Result operator()(jni::JNIEnv& env, const std::unordered_map& 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, "", "()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 converted = jni::NewLocalObject(env, mbgl::Value::visit(item.second, evaluator)); + jni::LocalObject key = jni::NewLocalObject(env, *convert(env, item.first)); + jni::CallMethod(env, jsonObject, *add, key.get(), converted.get()); + } + + return {jsonObject}; + } +}; + + +template <> +struct Converter { + Result 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 jid = jni::NewLocalObject(env, *convert(env, id)); + + //Convert properties + jni::LocalObject properties = jni::NewLocalObject(env, *convert(env, value.properties)); + + //Convert geometry + jni::LocalObject geometry = jni::NewLocalObject(env, *convert(env, value.geometry)); + + //Create feature + return {reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometry, geometry.get(), properties.get(), jid.get()))}; + } +}; + +template <> +struct Converter*, std::vector> { + Result*> operator()(jni::JNIEnv& env, const std::vector& value) const { + static jni::jclass* featureClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Feature")).release(); + jni::jarray& jarray = jni::NewObjectArray(env, value.size(), *featureClass); + + for(size_t i = 0; i < value.size(); i = i + 1) { + jni::LocalObject converted = jni::NewLocalObject(env, *convert(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 +#include +#include "../../jni/local_object.hpp" + +namespace mbgl { +namespace android { +namespace conversion { + +/** + * Turn mapbox::geometry type into Java GeoJson Geometries + */ +template +class GeometryEvaluator { +public: + + jni::JNIEnv& env; + + /** + * Point (double[]) + */ + jni::jobject* operator()(const mapbox::geometry::point &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> position = jni::NewLocalObject(env, toGeoJsonPosition(env, geometry.x, geometry.y)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, position.get())); + } + + /** + * LineString (double[][]) + */ + jni::jobject* operator()(const mapbox::geometry::line_string &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> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + } + + /** + * MultiPoint (double[][]) + */ + jni::jobject* operator()(const mapbox::geometry::multi_point &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> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, coordinates.get())); + } + + /** + * Polygon (double[][][]) + */ + jni::jobject* operator()(const mapbox::geometry::polygon &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> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + } + + /** + * MultiLineString (double[][][]) + */ + jni::jobject* operator()(const mapbox::geometry::multi_line_string &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> shape = jni::NewLocalObject(env, toShape<>(env, geometry)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromCoordinates, shape.get())); + } + + /** + * MultiPolygon (double[][][][]) -> [[[D + Object array == [[[[D + */ + jni::jobject* operator()(const mapbox::geometry::multi_polygon &geometry) const { + static jni::jclass* listClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[[D")).release(); + jni::LocalObject> jarray = jni::NewLocalObject(env, &jni::NewObjectArray(env, geometry.size(), *listClass)); + + for(size_t i = 0; i < geometry.size(); i = i + 1) { + jni::LocalObject> 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::CallStaticMethod(env, *javaClass, *fromGeometries, jarray.get())); + } + + /** + * GeometryCollection + */ + jni::jobject* operator()(const mapbox::geometry::geometry_collection &collection) const { + static jni::jclass* geometryClass = jni::NewGlobalRef(env, &jni::FindClass(env, "com/mapbox/services/commons/geojson/Geometry")).release(); + jni::LocalObject> 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 converted = jni::NewLocalObject(env, mapbox::geometry::geometry::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 list = jni::NewLocalObject(env, toArrayList<>(env, *jarray)); + return reinterpret_cast(jni::CallStaticMethod(env, *javaClass, *fromGeometries, list.get())); + } + +private: + + /** + * x, y -> jarray ([x,y]) + */ + static jni::jarray* toGeoJsonPosition(JNIEnv& env, double x, double y) { + jni::jarray& jarray = jni::NewArray(env, 2); + jni::jdouble array[] = {x, y}; + jni::SetArrayRegion(env, jarray, 0, 2, array); + return &jarray; + } + + /** + * vector> -> jarray (double[][]) -> [D + Object array == [[D + */ + static jni::jarray* toGeoJsonCoordinates(JNIEnv& env, std::vector> points) { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[D")).release(); + jni::jarray& jarray = jni::NewObjectArray(env, points.size(), *javaClass); + + for(size_t i = 0; i < points.size(); i = i + 1) { + mapbox::geometry::point point = points.at(i); + jni::LocalObject> position = jni::NewLocalObject(env, toGeoJsonPosition(env, point.x, point.y)); + jni::SetObjectArrayElement(env, jarray, i, position.get()); + } + + return &jarray; + } + + /** + * polygon + * multi_line_string + * -> jarray (double[][][]) -> [[D + Object array == [[[D + */ + template + static jni::jarray* toShape(JNIEnv& env, SHAPE value) { + static jni::jclass* javaClass = jni::NewGlobalRef(env, &jni::FindClass(env, "[[D")).release(); + jni::jarray& jarray = jni::NewObjectArray(env, value.size(), *javaClass); + + for(size_t i = 0; i < value.size(); i = i + 1) { + jni::LocalObject> coordinates = jni::NewLocalObject(env, toGeoJsonCoordinates(env, value.at(i))); + jni::SetObjectArrayElement(env, jarray, i, coordinates.get()); + } + + return &jarray; + } +}; + +/** + * mapbox::geometry::geometry -> Java GeoJson Geometry<> + */ +template +struct Converter> { + Result operator()(jni::JNIEnv& env, const mapbox::geometry::geometry& value) const { + GeometryEvaluator evaluator { env } ; + jni::jobject* converted = mapbox::geometry::geometry::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 #include #include @@ -23,9 +27,12 @@ #include #include #include +#include #include #include +#include + #include #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* nativeQueryRenderedFeaturesForPoint(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat x, jni::jfloat y, jni::jarray* 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(nativeMapViewPtr); + + mbgl::optional> layers; + if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { + layers = toVector(*env, *layerIds); + } + point point = {x, y}; + + return *convert*, std::vector>(*env, nativeMapView->getMap().queryRenderedFeatures(point, layers)); +} + +jni::jarray* nativeQueryRenderedFeaturesForBox(JNIEnv *env, jni::jobject* obj, jlong nativeMapViewPtr, jni::jfloat left, jni::jfloat top, jni::jfloat right, jni::jfloat bottom, jni::jarray* 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(nativeMapViewPtr); + + mbgl::optional> layers; + if (layerIds != nullptr && jni::GetArrayLength(*env, *layerIds) > 0) { + layers = toVector(*env, *layerIds); + } + box box = { point{ left, top}, point{ right, bottom } }; + + return *convert*, std::vector>(*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 + +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 NewLocalObject(JNIEnv& env, T* t) { + return LocalObject(t, LocalRefDeleter(env)); + } +} -- cgit v1.2.1