diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesActivity.java b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesActivity.java index 2183bf4401a..9d36ea141d6 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesActivity.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/feature/QueryRenderedFeaturesActivity.java @@ -9,6 +9,7 @@ import android.util.Log; import android.view.MenuItem; +import com.google.gson.JsonElement; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; import com.mapbox.mapboxsdk.maps.MapView; @@ -18,6 +19,7 @@ import com.mapbox.services.commons.geojson.Feature; import java.util.List; +import java.util.Map; /** * Demo's query rendered features @@ -54,7 +56,12 @@ public void onMapClick(@NonNull LatLng point) { for (Feature feature : features) { if (feature != null) { - Log.i(TAG, String.format("Got feature %s", feature.getId())); + Log.i(TAG, String.format("Got feature %s. With %s properties", feature.getId(), feature.getProperties() != null ? feature.getProperties().entrySet().size() : "<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"); } @@ -63,7 +70,7 @@ public void onMapClick(@NonNull LatLng point) { }); //Little taste of home - mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 12)); + mapboxMap.animateCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(52.0907, 5.1214), 16)); } }); } diff --git a/platform/android/src/conversion/constant.hpp b/platform/android/src/conversion/constant.hpp index 9a570d37174..ceda4859582 100644 --- a/platform/android/src/conversion/constant.hpp +++ b/platform/android/src/conversion/constant.hpp @@ -32,6 +32,27 @@ struct Converter<jni::jobject*, float> { } }; +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)}; + } +}; + +/** + * All integrals. java is limited to 64 bit signed, so... + */ +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)}; + } +}; + template <> struct Converter<jni::jobject*, std::string> { Result<jni::jobject*> operator()(jni::JNIEnv& env, const std::string& value) const { diff --git a/platform/android/src/conversion/conversion.hpp b/platform/android/src/conversion/conversion.hpp index ea8a31bcf29..1277f3f67e0 100644 --- a/platform/android/src/conversion/conversion.hpp +++ b/platform/android/src/conversion/conversion.hpp @@ -37,7 +37,7 @@ class Result : private variant<T, Error> { } }; -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 index c91be1637d5..7f57a1ffb7e 100644 --- a/platform/android/src/geometry/conversion/feature.hpp +++ b/platform/android/src/geometry/conversion/feature.hpp @@ -18,6 +18,9 @@ namespace mbgl { namespace android { namespace conversion { +/** + * Turn feature identifier into std::string + */ class FeatureIdVisitor { public: @@ -30,6 +33,112 @@ class FeatureIdVisitor { 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");; + return &jni::NewObject(env, *javaClass, *constructor, *convert<jni::jobject*, bool>(env, value)); + } + + /** + * 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");; + return &jni::NewObject(env, *javaClass, *constructor, *convert<jni::jobject*, std::string>(env, value)); + } + + /** + * 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"); + return &jni::NewObject(env, *javaClass, *constructor, *convert<jni::jobject*, Number>(env, value)); + } + + + /** + * 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::CallMethod<void>(env, jarray, *add, mbgl::Value::visit(v, *this)); + } + + return jarray; + } + + /** + * Json Object + */ + jni::jobject* operator()(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 + for (auto &item : value) { + jni::CallMethod<void>(env, jsonObject, *add, *convert<jni::jobject*, std::string>(env, item.first), mbgl::Value::visit(item.second, *this)); + } + + 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::CallMethod<void>(env, jsonObject, *add, *convert<jni::jobject*, std::string>(env, item.first), mbgl::Value::visit(item.second, evaluator)); + } + + return {jsonObject}; + } }; @@ -39,13 +148,16 @@ struct Converter<jni::jobject*, mbgl::Feature> { 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;"); - //Get Id + //Convert Id FeatureIdVisitor idEvaluator; std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : ""; Result<jni::jobject*> jid = convert<jni::jobject*>(env, id); + //Convert properties + Result<jni::jobject*> properties = convert<jni::jobject*>(env, value.properties); + /**, TODO: converted geometry**/ - return {reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometry, (jni::jobject*) nullptr, (jni::jobject*) nullptr, *jid))}; + return {reinterpret_cast<jni::jobject*>(jni::CallStaticMethod<jni::jobject*>(env, *javaClass, *fromGeometry, (jni::jobject*) nullptr, *properties, *jid))}; } };