summaryrefslogtreecommitdiff
path: root/platform/android/src/geojson
diff options
context:
space:
mode:
Diffstat (limited to 'platform/android/src/geojson')
-rw-r--r--platform/android/src/geojson/conversion/feature.hpp217
-rw-r--r--platform/android/src/geojson/conversion/geometry.hpp184
-rw-r--r--platform/android/src/geojson/feature.cpp63
-rw-r--r--platform/android/src/geojson/feature.hpp39
-rw-r--r--platform/android/src/geojson/feature_collection.cpp40
-rw-r--r--platform/android/src/geojson/feature_collection.hpp29
-rw-r--r--platform/android/src/geojson/geometry.cpp52
-rw-r--r--platform/android/src/geojson/geometry.hpp27
-rw-r--r--platform/android/src/geojson/line_string.cpp54
-rw-r--r--platform/android/src/geojson/line_string.hpp34
-rw-r--r--platform/android/src/geojson/multi_line_string.cpp56
-rw-r--r--platform/android/src/geojson/multi_line_string.hpp33
-rw-r--r--platform/android/src/geojson/multi_point.cpp35
-rw-r--r--platform/android/src/geojson/multi_point.hpp31
-rw-r--r--platform/android/src/geojson/multi_polygon.cpp46
-rw-r--r--platform/android/src/geojson/multi_polygon.hpp31
-rw-r--r--platform/android/src/geojson/point.cpp28
-rw-r--r--platform/android/src/geojson/point.hpp31
-rw-r--r--platform/android/src/geojson/polygon.cpp48
-rw-r--r--platform/android/src/geojson/polygon.hpp33
-rw-r--r--platform/android/src/geojson/position.cpp27
-rw-r--r--platform/android/src/geojson/position.hpp27
22 files changed, 1165 insertions, 0 deletions
diff --git a/platform/android/src/geojson/conversion/feature.hpp b/platform/android/src/geojson/conversion/feature.hpp
new file mode 100644
index 0000000000..86aa5fc03c
--- /dev/null
+++ b/platform/android/src/geojson/conversion/feature.hpp
@@ -0,0 +1,217 @@
+#pragma once
+
+#include "../../conversion/constant.hpp"
+#include "../../conversion/conversion.hpp"
+#include "geometry.hpp"
+#include "../../gson/json_object.hpp"
+
+#include <mbgl/util/feature.hpp>
+#include <mapbox/variant.hpp>
+#include <mapbox/geometry.hpp>
+
+#include <jni/jni.hpp>
+#include "../../jni/local_object.hpp"
+#include "../feature.hpp"
+
+#include <string>
+#include <array>
+#include <vector>
+#include <sstream>
+
+#include <mbgl/util/logging.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>", "(Ljava/lang/Boolean;)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::Object<android::geojson::Feature>, mbgl::Feature> {
+ Result<jni::Object<android::geojson::Feature>> operator()(jni::JNIEnv& env, const mbgl::Feature& value) const {
+
+ // Convert Id
+ FeatureIdVisitor idEvaluator;
+ std::string id = (value.id) ? mapbox::geometry::identifier::visit(value.id.value(), idEvaluator) : "";
+ auto jid = jni::Make<jni::String>(env, id);
+
+ // Convert properties
+ auto properties = jni::Object<gson::JsonObject>(*convert<jni::jobject*>(env, value.properties));
+
+ // Convert geometry
+ auto geometry = jni::Object<android::geojson::Geometry>(*convert<jni::jobject*>(env, value.geometry));
+
+ // Create feature
+ auto feature = android::geojson::Feature::fromGeometry(env, geometry, properties, jid);
+
+ //Cleanup
+ jni::DeleteLocalRef(env, jid);
+ jni::DeleteLocalRef(env, geometry);
+ jni::DeleteLocalRef(env, properties);
+
+ return feature;
+ }
+};
+
+template <>
+struct Converter<jni::Array<jni::Object<android::geojson::Feature>>, std::vector<mbgl::Feature>> {
+ Result<jni::Array<jni::Object<android::geojson::Feature>>> operator()(jni::JNIEnv& env, const std::vector<mbgl::Feature>& value) const {
+ using namespace mbgl::android::geojson;
+ auto features = jni::Array<jni::Object<Feature>>::New(env, value.size(), Feature::javaClass);
+
+ for(size_t i = 0; i < value.size(); i = i + 1) {
+ auto converted = *convert<jni::Object<android::geojson::Feature>, mbgl::Feature>(env, value.at(i));
+ features.Set(env, i, converted);
+ jni::DeleteLocalRef(env, converted);
+ }
+
+ return {features};
+ }
+};
+
+} // namespace conversion
+} // namespace android
+} // namespace mbgl
diff --git a/platform/android/src/geojson/conversion/geometry.hpp b/platform/android/src/geojson/conversion/geometry.hpp
new file mode 100644
index 0000000000..2ca63e2c11
--- /dev/null
+++ b/platform/android/src/geojson/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/geojson/feature.cpp b/platform/android/src/geojson/feature.cpp
new file mode 100644
index 0000000000..a6b387cd15
--- /dev/null
+++ b/platform/android/src/geojson/feature.cpp
@@ -0,0 +1,63 @@
+#include "feature.hpp"
+
+#include "geometry.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mbgl::Feature Feature::convert(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ // Convert
+ auto jGeometry = getGeometry(env, jFeature);
+ auto jProperties = Feature::getProperties(env, jFeature);
+
+ std::experimental::optional<mapbox::geometry::identifier> id;
+ auto jId = Feature::getId(env, jFeature);
+ if (jId) {
+ id = { jni::Make<std::string>(env, jId) };
+ }
+
+ auto feature = mbgl::Feature {
+ Geometry::convert(env, jGeometry),
+ gson::JsonObject::convert(env, jProperties),
+ id
+ };
+
+ // Cleanup
+ jni::DeleteLocalRef(env, jGeometry);
+ jni::DeleteLocalRef(env, jProperties);
+ jni::DeleteLocalRef(env, jId);
+
+ return feature;
+}
+
+jni::Object<Geometry> Feature::getGeometry(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::Object<Geometry> ()>(env, "getGeometry");
+ return jFeature.Call(env, method);
+}
+
+jni::Object<gson::JsonObject> Feature::getProperties(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::Object<gson::JsonObject> ()>(env, "getProperties");
+ return jFeature.Call(env, method);
+}
+
+jni::String Feature::getId(jni::JNIEnv& env, jni::Object<Feature> jFeature) {
+ static auto method = Feature::javaClass.GetMethod<jni::String ()>(env, "getId");
+ return jFeature.Call(env, method);
+}
+
+jni::Object<Feature> Feature::fromGeometry(jni::JNIEnv& env, jni::Object<Geometry> geometry, jni::Object<gson::JsonObject> properties, jni::String id) {
+ static auto method = Feature::javaClass.GetStaticMethod<jni::Object<Feature> (jni::Object<Geometry>, jni::Object<gson::JsonObject>, jni::String)>(env, "fromGeometry");
+ return Feature::javaClass.Call(env, method, geometry, properties, id);
+}
+
+void Feature::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ Feature::javaClass = *jni::Class<Feature>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Feature> Feature::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/feature.hpp b/platform/android/src/geojson/feature.hpp
new file mode 100644
index 0000000000..b5d856cc42
--- /dev/null
+++ b/platform/android/src/geojson/feature.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include <mbgl/util/feature.hpp>
+#include <mbgl/util/geometry.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "geometry.hpp"
+#include "../gson/json_object.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class Feature : private mbgl::util::noncopyable {
+public:
+
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Feature"; };
+
+ static jni::Object<Feature> fromGeometry(jni::JNIEnv&, jni::Object<Geometry>, jni::Object<gson::JsonObject>, jni::String);
+
+ static mbgl::Feature convert(jni::JNIEnv&, jni::Object<Feature>);
+
+ static jni::Object<Geometry> getGeometry(jni::JNIEnv&, jni::Object<Feature>);
+
+ static jni::String getId(jni::JNIEnv&, jni::Object<Feature>);
+
+ static jni::Object<gson::JsonObject> getProperties(jni::JNIEnv&, jni::Object<Feature>);
+
+ static jni::Class<Feature> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/feature_collection.cpp b/platform/android/src/geojson/feature_collection.cpp
new file mode 100644
index 0000000000..2f156532ae
--- /dev/null
+++ b/platform/android/src/geojson/feature_collection.cpp
@@ -0,0 +1,40 @@
+#include "feature_collection.hpp"
+
+#include "feature.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mbgl::FeatureCollection FeatureCollection::convert(jni::JNIEnv& env, jni::Object<FeatureCollection> jCollection) {
+ auto jFeatureList = FeatureCollection::getFeatures(env, jCollection);
+ auto jFeatures = java::util::List::toArray<Feature>(env, jFeatureList);
+ auto size = size_t(jFeatures.Length(env));
+
+ auto collection = mbgl::FeatureCollection();
+ collection.reserve(size);
+
+ for (size_t i = 0; i < size; i++) {
+ auto jFeature = jFeatures.Get(env, i);
+ collection.push_back(Feature::convert(env, jFeature));
+ jni::DeleteLocalRef(env, jFeature);
+ }
+
+ return collection;
+}
+
+jni::Object<java::util::List> FeatureCollection::getFeatures(jni::JNIEnv& env, jni::Object<FeatureCollection> jCollection) {
+ static auto method = FeatureCollection::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getFeatures");
+ return jCollection.Call(env, method);
+}
+
+void FeatureCollection::registerNative(jni::JNIEnv& env) {
+ // Lookup the class
+ javaClass = *jni::Class<FeatureCollection>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<FeatureCollection> FeatureCollection::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/feature_collection.hpp b/platform/android/src/geojson/feature_collection.hpp
new file mode 100644
index 0000000000..8e9717e82b
--- /dev/null
+++ b/platform/android/src/geojson/feature_collection.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include "../java/util.hpp"
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class FeatureCollection : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/FeatureCollection"; };
+
+ static mbgl::FeatureCollection convert(jni::JNIEnv&, jni::Object<FeatureCollection>);
+
+ static jni::Object<java::util::List> getFeatures(jni::JNIEnv&, jni::Object<FeatureCollection>);
+
+ static jni::Class<FeatureCollection> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/geometry.cpp b/platform/android/src/geojson/geometry.cpp
new file mode 100644
index 0000000000..33bb4ee3db
--- /dev/null
+++ b/platform/android/src/geojson/geometry.cpp
@@ -0,0 +1,52 @@
+#include "geometry.hpp"
+
+#include "point.hpp"
+#include "multi_point.hpp"
+#include "line_string.hpp"
+#include "multi_line_string.hpp"
+#include "polygon.hpp"
+#include "multi_polygon.hpp"
+
+#include <string>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::geometry Geometry::convert(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
+ auto type = Geometry::getType(env, jGeometry);
+ if (type == Point::Type()) {
+ return { Point::convert(env, jni::Object<Point>(jGeometry.Get())) };
+ } else if (type == MultiPoint::Type()) {
+ return { MultiPoint::convert(env, jni::Object<MultiPoint>(jGeometry.Get())) };
+ } else if (type == LineString::Type()) {
+ return { LineString::convert(env, jni::Object<LineString>(jGeometry.Get())) };
+ } else if (type == MultiLineString::Type()) {
+ return { MultiLineString::convert(env, jni::Object<MultiLineString>(jGeometry.Get())) };
+ } else if (type == Polygon::Type()) {
+ return { Polygon::convert(env, jni::Object<Polygon>(jGeometry.Get())) };
+ } else if (type == MultiPolygon::Type()) {
+ return { MultiPolygon::convert(env, jni::Object<MultiPolygon>(jGeometry.Get())) };
+ }
+
+ throw std::runtime_error(std::string {"Unsupported GeoJSON type: " } + type);
+}
+
+std::string Geometry::getType(jni::JNIEnv &env, jni::Object<Geometry> jGeometry) {
+ static auto method = Geometry::javaClass.GetMethod<jni::String ()>(env, "getType");
+ auto jType = jGeometry.Call(env, method);
+ auto type = jni::Make<std::string>(env, jType);
+ jni::DeleteLocalRef(env, jType);
+ return type;
+}
+
+void Geometry::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Geometry>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Geometry> Geometry::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/geometry.hpp b/platform/android/src/geojson/geometry.hpp
new file mode 100644
index 0000000000..bdcff6bb3e
--- /dev/null
+++ b/platform/android/src/geojson/geometry.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class Geometry : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Geometry"; };
+
+ static mapbox::geojson::geometry convert(jni::JNIEnv&, jni::Object<Geometry>);
+
+ static std::string getType(jni::JNIEnv&, jni::Object<Geometry>);
+
+ static jni::Class<Geometry> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/line_string.cpp b/platform/android/src/geojson/line_string.cpp
new file mode 100644
index 0000000000..d0719f2538
--- /dev/null
+++ b/platform/android/src/geojson/line_string.cpp
@@ -0,0 +1,54 @@
+#include "line_string.hpp"
+
+#include "position.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
+ mapbox::geojson::line_string lineString;
+
+ if (jLineString) {
+ auto jPositionList = LineString::getCoordinates(env, jLineString);
+ lineString = LineString::convert(env, jPositionList);
+ jni::DeleteLocalRef(env, jPositionList);
+ }
+
+ return lineString;
+}
+
+mapbox::geojson::line_string LineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<Position>*/> jPositionList) {
+ mapbox::geojson::line_string lineString;
+
+ if (jPositionList) {
+ auto jPositionArray = java::util::List::toArray<Position>(env, jPositionList);
+
+ auto size = jPositionArray.Length(env);
+ for (std::size_t i = 0; i < size; i++) {
+ auto jPosition = jPositionArray.Get(env, i);
+ lineString.push_back(Position::convert(env, jPosition));
+ jni::DeleteLocalRef(env, jPosition);
+ }
+
+ jni::DeleteLocalRef(env, jPositionArray);
+ }
+
+ return lineString;
+}
+
+jni::Object<java::util::List> LineString::getCoordinates(jni::JNIEnv &env, jni::Object<LineString> jLineString) {
+ static auto method = LineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+ return jLineString.Call(env, method);
+}
+
+void LineString::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<LineString>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<LineString> LineString::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/line_string.hpp b/platform/android/src/geojson/line_string.hpp
new file mode 100644
index 0000000000..d3be68d0a5
--- /dev/null
+++ b/platform/android/src/geojson/line_string.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+
+class LineString : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/LineString"; };
+
+ static constexpr auto Type() { return "LineString"; };
+
+ static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<LineString>);
+
+ static mapbox::geojson::line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<Position>*/>);
+
+ static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<LineString>);
+
+ static jni::Class<LineString> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_line_string.cpp b/platform/android/src/geojson/multi_line_string.cpp
new file mode 100644
index 0000000000..b676144bf5
--- /dev/null
+++ b/platform/android/src/geojson/multi_line_string.cpp
@@ -0,0 +1,56 @@
+#include "multi_line_string.hpp"
+
+#include "line_string.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<MultiLineString> jMultiLineString) {
+ mapbox::geojson::multi_line_string multiLineString;
+
+ if (jMultiLineString) {
+ auto jPositionListsList = MultiLineString::getCoordinates(env, jMultiLineString);
+ multiLineString = MultiLineString::convert(env, jPositionListsList);
+ jni::DeleteLocalRef(env, jPositionListsList);
+ }
+
+ return multiLineString;
+}
+
+mapbox::geojson::multi_line_string MultiLineString::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Position>>*/> jPositionListsList) {
+ mapbox::geojson::multi_line_string multiLineString;
+
+ if (jPositionListsList) {
+ auto jPositionListsArray = java::util::List::toArray<java::util::List>(env, jPositionListsList);
+
+ auto size = jPositionListsArray.Length(env);
+ multiLineString.reserve(size);
+
+ for (std::size_t i = 0; i < size; i++) {
+ auto jPositionList = jPositionListsArray.Get(env, i);
+ multiLineString.push_back(LineString::convert(env, jPositionList));
+ jni::DeleteLocalRef(env, jPositionList);
+ }
+
+ jni::DeleteLocalRef(env, jPositionListsArray);
+ }
+
+ return multiLineString;
+}
+
+jni::Object<java::util::List> MultiLineString::getCoordinates(jni::JNIEnv &env, jni::Object<MultiLineString> jLineString) {
+ static auto method = MultiLineString::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+ return jLineString.Call(env, method);
+}
+
+void MultiLineString::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<MultiLineString>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<MultiLineString> MultiLineString::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_line_string.hpp b/platform/android/src/geojson/multi_line_string.hpp
new file mode 100644
index 0000000000..af33fe72d6
--- /dev/null
+++ b/platform/android/src/geojson/multi_line_string.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class MultiLineString : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiLineString"; };
+
+ static constexpr auto Type() { return "MultiLineString"; };
+
+ static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<MultiLineString>);
+
+ static mapbox::geojson::multi_line_string convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Position>>*/>);
+
+ static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiLineString>);
+
+ static jni::Class<MultiLineString> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_point.cpp b/platform/android/src/geojson/multi_point.cpp
new file mode 100644
index 0000000000..df4fdd7dfa
--- /dev/null
+++ b/platform/android/src/geojson/multi_point.cpp
@@ -0,0 +1,35 @@
+#include "multi_point.hpp"
+
+#include "line_string.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::multi_point MultiPoint::convert(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
+ mapbox::geojson::multi_point multiPoint;
+
+ if (jMultiPoint) {
+ auto jPositionListsList = MultiPoint::getCoordinates(env, jMultiPoint);
+ multiPoint = LineString::convert(env, jPositionListsList);
+ jni::DeleteLocalRef(env, jPositionListsList);
+ }
+
+ return multiPoint;
+}
+
+jni::Object<java::util::List> MultiPoint::getCoordinates(jni::JNIEnv &env, jni::Object<MultiPoint> jMultiPoint) {
+ static auto method = MultiPoint::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+ return jMultiPoint.Call(env, method);
+}
+
+void MultiPoint::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<MultiPoint>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<MultiPoint> MultiPoint::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_point.hpp b/platform/android/src/geojson/multi_point.hpp
new file mode 100644
index 0000000000..7a698287eb
--- /dev/null
+++ b/platform/android/src/geojson/multi_point.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class MultiPoint : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPoint"; };
+
+ static constexpr auto Type() { return "MultiPoint"; };
+
+ static mapbox::geojson::multi_point convert(jni::JNIEnv&, jni::Object<MultiPoint>);
+
+ static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiPoint>);
+
+ static jni::Class<MultiPoint> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_polygon.cpp b/platform/android/src/geojson/multi_polygon.cpp
new file mode 100644
index 0000000000..a55884a110
--- /dev/null
+++ b/platform/android/src/geojson/multi_polygon.cpp
@@ -0,0 +1,46 @@
+#include "multi_polygon.hpp"
+
+#include "polygon.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::multi_polygon MultiPolygon::convert(jni::JNIEnv &env, jni::Object<MultiPolygon> jMultiPolygon) {
+ mapbox::geojson::multi_polygon multiPolygon;
+
+ if (jMultiPolygon) {
+ auto jPositionListsListList = MultiPolygon::getCoordinates(env, jMultiPolygon);
+ auto jPositionListsListArray = java::util::List::toArray<java::util::List>(env, jPositionListsListList);
+
+ auto size = jPositionListsListArray.Length(env);
+ multiPolygon.reserve(size);
+
+ for (size_t i = 0; i < size; i++) {
+ auto jPositionListsList = jPositionListsListArray.Get(env, i);
+ multiPolygon.push_back(Polygon::convert(env, jPositionListsList));
+ jni::DeleteLocalRef(env, jPositionListsList);
+ }
+
+ jni::DeleteLocalRef(env, jPositionListsListList);
+ jni::DeleteLocalRef(env, jPositionListsListArray);
+ }
+
+ return multiPolygon;
+}
+
+jni::Object<java::util::List> MultiPolygon::getCoordinates(jni::JNIEnv &env, jni::Object<MultiPolygon> jPolygon) {
+ static auto method = MultiPolygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+ return jPolygon.Call(env, method);
+}
+
+void MultiPolygon::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<MultiPolygon>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<MultiPolygon> MultiPolygon::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/multi_polygon.hpp b/platform/android/src/geojson/multi_polygon.hpp
new file mode 100644
index 0000000000..1f144cffd2
--- /dev/null
+++ b/platform/android/src/geojson/multi_polygon.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class MultiPolygon : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/MultiPolygon"; };
+
+ static constexpr auto Type() { return "MultiPolygon"; };
+
+ static mapbox::geojson::multi_polygon convert(jni::JNIEnv&, jni::Object<MultiPolygon>);
+
+ static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<MultiPolygon>);
+
+ static jni::Class<MultiPolygon> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/point.cpp b/platform/android/src/geojson/point.cpp
new file mode 100644
index 0000000000..3d19a119d7
--- /dev/null
+++ b/platform/android/src/geojson/point.cpp
@@ -0,0 +1,28 @@
+#include "point.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::point Point::convert(jni::JNIEnv &env, jni::Object<Point> jPoint) {
+ auto jPosition = Point::getPosition(env, jPoint);
+ auto point = Position::convert(env, jPosition);
+ jni::DeleteLocalRef(env, jPosition);
+ return point;
+}
+
+jni::Object<Position> Point::getPosition(JNIEnv& env, jni::Object<Point> jPoint) {
+ static auto method = Point::javaClass.GetMethod<jni::Object<Position> ()>(env, "getCoordinates");
+ return jPoint.Call(env, method);
+}
+
+void Point::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Point>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Point> Point::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/point.hpp b/platform/android/src/geojson/point.hpp
new file mode 100644
index 0000000000..64ac0af9cc
--- /dev/null
+++ b/platform/android/src/geojson/point.hpp
@@ -0,0 +1,31 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include "position.hpp"
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class Point : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Point"; };
+
+ static constexpr auto Type() { return "Point"; };
+
+ static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<Point>);
+
+ static jni::Object<Position> getPosition(JNIEnv&, jni::Object<Point>);
+
+ static jni::Class<Point> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/polygon.cpp b/platform/android/src/geojson/polygon.cpp
new file mode 100644
index 0000000000..bb37b8d99e
--- /dev/null
+++ b/platform/android/src/geojson/polygon.cpp
@@ -0,0 +1,48 @@
+#include "polygon.hpp"
+
+#include "multi_line_string.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
+ mapbox::geojson::polygon polygon;
+
+ if (jPolygon) {
+ auto jPositionListsList = Polygon::getCoordinates(env, jPolygon);
+ polygon = Polygon::convert(env, jPositionListsList);
+ jni::DeleteLocalRef(env, jPositionListsList);
+ }
+
+ return polygon;
+}
+
+mapbox::geojson::polygon Polygon::convert(jni::JNIEnv &env, jni::Object<java::util::List/*<java::util::List<Position>>*/> jPositionListsList) {
+ mapbox::geojson::polygon polygon;
+
+ if (jPositionListsList) {
+ auto multiLine = MultiLineString::convert(env, jPositionListsList);
+ polygon.reserve(multiLine.size());
+ polygon.insert(std::end(polygon), std::begin(multiLine), std::end(multiLine));
+ }
+
+ return polygon;
+}
+
+
+jni::Object<java::util::List> Polygon::getCoordinates(jni::JNIEnv &env, jni::Object<Polygon> jPolygon) {
+ static auto method = Polygon::javaClass.GetMethod<jni::Object<java::util::List> ()>(env, "getCoordinates");
+ return jPolygon.Call(env, method);
+}
+
+void Polygon::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Polygon>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Polygon> Polygon::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/polygon.hpp b/platform/android/src/geojson/polygon.hpp
new file mode 100644
index 0000000000..e5362cedf1
--- /dev/null
+++ b/platform/android/src/geojson/polygon.hpp
@@ -0,0 +1,33 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+#include "../java/util.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class Polygon : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/geojson/Polygon"; };
+
+ static constexpr auto Type() { return "Polygon"; };
+
+ static mapbox::geojson::polygon convert(jni::JNIEnv &, jni::Object<Polygon>);
+
+ static mapbox::geojson::polygon convert(jni::JNIEnv&, jni::Object<java::util::List/*<java::util::List<Position>>*/>);
+
+ static jni::Object<java::util::List> getCoordinates(jni::JNIEnv&, jni::Object<Polygon>);
+
+ static jni::Class<Polygon> javaClass;
+
+ static void registerNative(jni::JNIEnv &);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/position.cpp b/platform/android/src/geojson/position.cpp
new file mode 100644
index 0000000000..c0e6da3887
--- /dev/null
+++ b/platform/android/src/geojson/position.cpp
@@ -0,0 +1,27 @@
+#include "position.hpp"
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+mapbox::geojson::point Position::convert(jni::JNIEnv &env, jni::Object<Position> jPosition) {
+ static auto method = Position::javaClass.GetMethod<jni::Array<jdouble> ()>(env, "getCoordinates");
+ // Array with 0: longitude, 1: latitude (and optionally 2: altitude)
+ auto coordinates = jPosition.Call(env, method);
+ jdouble lngLat[2];
+ coordinates.GetRegion(env, 0, lngLat);
+ mapbox::geojson::point point(lngLat[0], lngLat[1]);
+ jni::DeleteLocalRef(env, coordinates);
+ return point;
+}
+
+void Position::registerNative(jni::JNIEnv &env) {
+ // Lookup the class
+ javaClass = *jni::Class<Position>::Find(env).NewGlobalRef(env).release();
+}
+
+jni::Class<Position> Position::javaClass;
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file
diff --git a/platform/android/src/geojson/position.hpp b/platform/android/src/geojson/position.hpp
new file mode 100644
index 0000000000..7017a8172a
--- /dev/null
+++ b/platform/android/src/geojson/position.hpp
@@ -0,0 +1,27 @@
+#pragma once
+
+#include <mbgl/util/geojson.hpp>
+#include <mbgl/util/noncopyable.hpp>
+
+#include <jni/jni.hpp>
+
+namespace mbgl {
+namespace android {
+namespace geojson {
+
+class Position : private mbgl::util::noncopyable {
+public:
+ static constexpr auto Name() { return "com/mapbox/services/commons/models/Position"; };
+
+ static constexpr auto Type() { return "Position"; };
+
+ static mapbox::geojson::point convert(jni::JNIEnv&, jni::Object<Position>);
+
+ static jni::Class<Position> javaClass;
+
+ static void registerNative(jni::JNIEnv&);
+};
+
+} // namespace geojson
+} // namespace android
+} // namespace mbgl \ No newline at end of file