Rename Gradle targets Android (#2226)
This commit is contained in:
@@ -0,0 +1,213 @@
|
||||
#include "custom_geometry_source.hpp"
|
||||
#include "attach_env.hpp"
|
||||
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include "../android_conversion.hpp"
|
||||
#include "../conversion/filter.hpp"
|
||||
|
||||
// C++ -> Java conversion
|
||||
#include "../../conversion/conversion.hpp"
|
||||
#include "../../conversion/collection.hpp"
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include <mbgl/style/conversion/custom_geometry_source_options.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
// This conversion is expected not to fail because it's used only in contexts
|
||||
// where the value was originally a CustomGeometrySourceOptions object on the
|
||||
// Java side. If it fails to convert, it's a bug in our serialization or
|
||||
// Java-side static typing.
|
||||
static style::CustomGeometrySource::Options convertCustomGeometrySourceOptions(jni::JNIEnv& env,
|
||||
const jni::Object<>& options,
|
||||
style::TileFunction fetchFn,
|
||||
style::TileFunction cancelFn) {
|
||||
using namespace mbgl::style::conversion;
|
||||
if (!options) {
|
||||
return style::CustomGeometrySource::Options();
|
||||
}
|
||||
Error error;
|
||||
std::optional<style::CustomGeometrySource::Options> result = convert<style::CustomGeometrySource::Options>(
|
||||
Value(env, options), error);
|
||||
if (!result) {
|
||||
throw std::logic_error(error.message);
|
||||
}
|
||||
result->fetchTileFunction = fetchFn;
|
||||
result->cancelTileFunction = cancelFn;
|
||||
return *result;
|
||||
}
|
||||
|
||||
CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& options)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::CustomGeometrySource>(
|
||||
jni::Make<std::string>(env, sourceId),
|
||||
convertCustomGeometrySourceOptions(
|
||||
env,
|
||||
options,
|
||||
std::bind(&CustomGeometrySource::fetchTile, this, std::placeholders::_1),
|
||||
std::bind(&CustomGeometrySource::cancelTile, this, std::placeholders::_1)))) {}
|
||||
|
||||
CustomGeometrySource::CustomGeometrySource(jni::JNIEnv& env,
|
||||
mbgl::style::Source& coreSource,
|
||||
AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
CustomGeometrySource::~CustomGeometrySource() {
|
||||
releaseThreads();
|
||||
}
|
||||
|
||||
void CustomGeometrySource::fetchTile(const mbgl::CanonicalTileID& tileID) {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
|
||||
static auto fetchTile = javaClass.GetMethod<void(jni::jint, jni::jint, jni::jint)>(*_env, "fetchTile");
|
||||
|
||||
// The source is removed on the main thread, but it still exists on the Render
|
||||
// thread until the frame is complete. This might cause fetchTile/cancelTile
|
||||
// invocations when the Java thread is shutting down and the peer has already
|
||||
// been released. See https://github.com/mapbox/mapbox-gl-native/issues/12551.
|
||||
if (!javaPeer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto peer = jni::Cast(*_env, javaClass, javaPeer);
|
||||
peer.Call(*_env, fetchTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
|
||||
};
|
||||
|
||||
void CustomGeometrySource::cancelTile(const mbgl::CanonicalTileID& tileID) {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
|
||||
static auto cancelTile = javaClass.GetMethod<void(jni::jint, jni::jint, jni::jint)>(*_env, "cancelTile");
|
||||
|
||||
// The source is removed on the main thread, but it still exists on the Render
|
||||
// thread until the frame is complete. This might cause fetchTile/cancelTile
|
||||
// invocations when the Java thread is shutting down and the peer has already
|
||||
// been released. See https://github.com/mapbox/mapbox-gl-native/issues/12551.
|
||||
if (!javaPeer) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto peer = jni::Cast(*_env, javaClass, javaPeer);
|
||||
peer.Call(*_env, cancelTile, (int)tileID.z, (int)tileID.x, (int)tileID.y);
|
||||
};
|
||||
|
||||
void CustomGeometrySource::startThreads() {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
|
||||
static auto startThreads = javaClass.GetMethod<void()>(*_env, "startThreads");
|
||||
|
||||
assert(javaPeer);
|
||||
|
||||
auto peer = jni::Cast(*_env, javaClass, javaPeer);
|
||||
peer.Call(*_env, startThreads);
|
||||
}
|
||||
|
||||
void CustomGeometrySource::releaseThreads() {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
|
||||
static auto releaseThreads = javaClass.GetMethod<void()>(*_env, "releaseThreads");
|
||||
|
||||
if (javaPeer) {
|
||||
auto peer = jni::Cast(*_env, javaClass, javaPeer);
|
||||
peer.Call(*_env, releaseThreads);
|
||||
}
|
||||
};
|
||||
|
||||
bool CustomGeometrySource::isCancelled(jni::jint z, jni::jint x, jni::jint y) {
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(*_env);
|
||||
static auto isCancelled = javaClass.GetMethod<jboolean(jni::jint, jni::jint, jni::jint)>(*_env, "isCancelled");
|
||||
|
||||
assert(javaPeer);
|
||||
|
||||
auto peer = jni::Cast(*_env, javaClass, javaPeer);
|
||||
return peer.Call(*_env, isCancelled, z, x, y);
|
||||
};
|
||||
|
||||
void CustomGeometrySource::setTileData(
|
||||
jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y, const jni::Object<geojson::FeatureCollection>& jFeatures) {
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
// Convert the jni object
|
||||
auto geometry = geojson::FeatureCollection::convert(env, jFeatures);
|
||||
|
||||
// Update the core source if not cancelled
|
||||
if (!isCancelled(z, x, y)) {
|
||||
source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::setTileData(CanonicalTileID(z, x, y),
|
||||
GeoJSON(geometry));
|
||||
}
|
||||
}
|
||||
|
||||
void CustomGeometrySource::invalidateTile(jni::JNIEnv&, jni::jint z, jni::jint x, jni::jint y) {
|
||||
source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::invalidateTile(CanonicalTileID(z, x, y));
|
||||
}
|
||||
|
||||
void CustomGeometrySource::invalidateBounds(jni::JNIEnv& env, const jni::Object<LatLngBounds>& jBounds) {
|
||||
auto bounds = LatLngBounds::getLatLngBounds(env, jBounds);
|
||||
source.as<mbgl::style::CustomGeometrySource>()->CustomGeometrySource::invalidateRegion(bounds);
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> CustomGeometrySource::querySourceFeatures(
|
||||
jni::JNIEnv& env, const jni::Array<jni::Object<>>& jfilter) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
std::vector<mbgl::Feature> features;
|
||||
if (rendererFrontend) {
|
||||
features = rendererFrontend->querySourceFeatures(source.getID(), {{}, toFilter(env, jfilter)});
|
||||
}
|
||||
return Feature::convert(env, features);
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> CustomGeometrySource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void CustomGeometrySource::addToMap(JNIEnv& env,
|
||||
const jni::Object<Source>& obj,
|
||||
mbgl::Map& map,
|
||||
AndroidRendererFrontend& frontend) {
|
||||
Source::addToMap(env, obj, map, frontend);
|
||||
startThreads();
|
||||
}
|
||||
|
||||
bool CustomGeometrySource::removeFromMap(JNIEnv& env, const jni::Object<Source>& source, mbgl::Map& map) {
|
||||
bool successfullyRemoved = Source::removeFromMap(env, source, map);
|
||||
if (successfullyRemoved) {
|
||||
releaseThreads();
|
||||
}
|
||||
return successfullyRemoved;
|
||||
}
|
||||
|
||||
void CustomGeometrySource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<CustomGeometrySource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<CustomGeometrySource>(
|
||||
env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<CustomGeometrySource, const jni::String&, const jni::Object<>&>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&CustomGeometrySource::querySourceFeatures, "querySourceFeatures"),
|
||||
METHOD(&CustomGeometrySource::setTileData, "nativeSetTileData"),
|
||||
METHOD(&CustomGeometrySource::invalidateTile, "nativeInvalidateTile"),
|
||||
METHOD(&CustomGeometrySource::invalidateBounds, "nativeInvalidateBounds"));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/sources/custom_geometry_source.hpp>
|
||||
#include <mbgl/util/geojson.hpp>
|
||||
#include <mbgl/tile/tile_id.hpp>
|
||||
#include "../../geojson/geometry.hpp"
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include "../../geojson/feature_collection.hpp"
|
||||
#include "../../geometry/lat_lng_bounds.hpp"
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class CustomGeometrySource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/CustomGeometrySource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
CustomGeometrySource(jni::JNIEnv&, const jni::String&, const jni::Object<>&);
|
||||
CustomGeometrySource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
~CustomGeometrySource();
|
||||
|
||||
bool removeFromMap(JNIEnv&, const jni::Object<Source>&, mbgl::Map&) override;
|
||||
void addToMap(JNIEnv&, const jni::Object<Source>&, mbgl::Map&, AndroidRendererFrontend&) override;
|
||||
|
||||
void fetchTile(const mbgl::CanonicalTileID& tileID);
|
||||
void cancelTile(const mbgl::CanonicalTileID& tileID);
|
||||
bool isCancelled(jni::jint z, jni::jint x, jni::jint y);
|
||||
void startThreads();
|
||||
void releaseThreads();
|
||||
|
||||
private:
|
||||
void setTileData(
|
||||
jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y, const jni::Object<geojson::FeatureCollection>& jf);
|
||||
|
||||
void invalidateTile(jni::JNIEnv& env, jni::jint z, jni::jint x, jni::jint y);
|
||||
void invalidateBounds(jni::JNIEnv& env, const jni::Object<LatLngBounds>& bounds);
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> querySourceFeatures(jni::JNIEnv&,
|
||||
const jni::Array<jni::Object<>>&);
|
||||
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class CustomGeometrySource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,270 @@
|
||||
#include "geojson_source.hpp"
|
||||
#include <mbgl/style/sources/geojson_source_impl.hpp>
|
||||
#include "attach_env.hpp"
|
||||
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include "../android_conversion.hpp"
|
||||
#include "../conversion/filter.hpp"
|
||||
#include <mbgl/style/conversion/geojson.hpp>
|
||||
#include <mbgl/style/conversion/geojson_options.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
|
||||
// C++ -> Java conversion
|
||||
#include "../../conversion/conversion.hpp"
|
||||
#include "../../conversion/collection.hpp"
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include "../conversion/url_or_tileset.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
// GeoJSONSource uses a "coalescing" model for high frequency asynchronous data
|
||||
// update calls, which in practice means, that any update that started
|
||||
// processing is going to finish and the last scheduled update is going to
|
||||
// finish as well. Any updates scheduled during processing can be canceled. Conversion
|
||||
// from Java features to core ones is done on a worker thread and once finished,
|
||||
// the ownership of the converted features is returned to the calling thread.
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
// This conversion is expected not to fail because it's used only in contexts where
|
||||
// the value was originally a GeoJsonOptions object on the Java side. If it fails
|
||||
// to convert, it's a bug in our serialization or Java-side static typing.
|
||||
static Immutable<style::GeoJSONOptions> convertGeoJSONOptions(jni::JNIEnv& env, const jni::Object<>& options) {
|
||||
using namespace mbgl::style::conversion;
|
||||
if (!options) {
|
||||
return style::GeoJSONOptions::defaultOptions();
|
||||
}
|
||||
Error error;
|
||||
std::optional<style::GeoJSONOptions> result = convert<style::GeoJSONOptions>(mbgl::android::Value(env, options),
|
||||
error);
|
||||
if (!result) {
|
||||
throw std::logic_error(error.message);
|
||||
}
|
||||
return makeMutable<style::GeoJSONOptions>(std::move(*result));
|
||||
}
|
||||
|
||||
GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& options)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::GeoJSONSource>(jni::Make<std::string>(env, sourceId),
|
||||
convertGeoJSONOptions(env, options))),
|
||||
converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground(),
|
||||
source.as<style::GeoJSONSource>()->impl().getOptions())) {}
|
||||
|
||||
GeoJSONSource::GeoJSONSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend),
|
||||
converter(std::make_unique<Actor<FeatureConverter>>(Scheduler::GetBackground(),
|
||||
source.as<style::GeoJSONSource>()->impl().getOptions())) {}
|
||||
|
||||
GeoJSONSource::~GeoJSONSource() = default;
|
||||
|
||||
void GeoJSONSource::setGeoJSONString(jni::JNIEnv& env, const jni::String& jString) {
|
||||
std::shared_ptr<std::string> json = std::make_shared<std::string>(jni::Make<std::string>(env, jString));
|
||||
|
||||
Update::Converter converterFn = [this, json](ActorRef<GeoJSONDataCallback> _callback) {
|
||||
converter->self().invoke(&FeatureConverter::convertJson, json, _callback);
|
||||
};
|
||||
|
||||
setAsync(converterFn);
|
||||
}
|
||||
|
||||
void GeoJSONSource::setFeatureCollection(jni::JNIEnv& env, const jni::Object<geojson::FeatureCollection>& jFeatures) {
|
||||
setCollectionAsync(env, jFeatures);
|
||||
}
|
||||
|
||||
void GeoJSONSource::setFeature(jni::JNIEnv& env, const jni::Object<geojson::Feature>& jFeature) {
|
||||
setCollectionAsync(env, jFeature);
|
||||
}
|
||||
|
||||
void GeoJSONSource::setGeometry(jni::JNIEnv& env, const jni::Object<geojson::Geometry>& jGeometry) {
|
||||
setCollectionAsync(env, jGeometry);
|
||||
}
|
||||
|
||||
void GeoJSONSource::setURL(jni::JNIEnv& env, const jni::String& url) {
|
||||
// Update the core source
|
||||
source.as<style::GeoJSONSource>()->setURL(jni::Make<std::string>(env, url));
|
||||
}
|
||||
|
||||
jni::Local<jni::String> GeoJSONSource::getURL(jni::JNIEnv& env) {
|
||||
std::optional<std::string> url = source.as<style::GeoJSONSource>()->getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> GeoJSONSource::querySourceFeatures(
|
||||
jni::JNIEnv& env, const jni::Array<jni::Object<>>& jfilter) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
std::vector<mbgl::Feature> features;
|
||||
if (rendererFrontend) {
|
||||
features = rendererFrontend->querySourceFeatures(source.getID(), {{}, toFilter(env, jfilter)});
|
||||
}
|
||||
return Feature::convert(env, features);
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> GeoJSONSource::getClusterChildren(
|
||||
jni::JNIEnv& env, const jni::Object<geojson::Feature>& feature) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
if (rendererFrontend) {
|
||||
mbgl::Feature _feature = Feature::convert(env, feature);
|
||||
_feature.properties["cluster_id"] = static_cast<uint64_t>(_feature.properties["cluster_id"].get<double>());
|
||||
const auto featureExtension = rendererFrontend->queryFeatureExtensions(
|
||||
source.getID(), _feature, "supercluster", "children", {});
|
||||
if (featureExtension.is<mbgl::FeatureCollection>()) {
|
||||
return Feature::convert(env, featureExtension.get<mbgl::FeatureCollection>());
|
||||
}
|
||||
}
|
||||
return jni::Array<jni::Object<Feature>>::New(env, 0);
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> GeoJSONSource::getClusterLeaves(
|
||||
jni::JNIEnv& env, const jni::Object<geojson::Feature>& feature, jni::jlong limit, jni::jlong offset) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
if (rendererFrontend) {
|
||||
mbgl::Feature _feature = Feature::convert(env, feature);
|
||||
_feature.properties["cluster_id"] = static_cast<uint64_t>(_feature.properties["cluster_id"].get<double>());
|
||||
const std::map<std::string, mbgl::Value> options = {{"limit", static_cast<uint64_t>(limit)},
|
||||
{"offset", static_cast<uint64_t>(offset)}};
|
||||
auto featureExtension = rendererFrontend->queryFeatureExtensions(
|
||||
source.getID(), _feature, "supercluster", "leaves", options);
|
||||
if (featureExtension.is<mbgl::FeatureCollection>()) {
|
||||
return Feature::convert(env, featureExtension.get<mbgl::FeatureCollection>());
|
||||
}
|
||||
}
|
||||
return jni::Array<jni::Object<Feature>>::New(env, 0);
|
||||
;
|
||||
}
|
||||
|
||||
jint GeoJSONSource::getClusterExpansionZoom(jni::JNIEnv& env, const jni::Object<geojson::Feature>& feature) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
if (rendererFrontend) {
|
||||
mbgl::Feature _feature = Feature::convert(env, feature);
|
||||
_feature.properties["cluster_id"] = static_cast<uint64_t>(_feature.properties["cluster_id"].get<double>());
|
||||
auto featureExtension = rendererFrontend->queryFeatureExtensions(
|
||||
source.getID(), _feature, "supercluster", "expansion-zoom", {});
|
||||
if (featureExtension.is<mbgl::Value>()) {
|
||||
auto value = featureExtension.get<mbgl::Value>();
|
||||
if (value.is<uint64_t>()) {
|
||||
return value.get<uint64_t>();
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> GeoJSONSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<GeoJSONSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
template <class JNIType>
|
||||
void GeoJSONSource::setCollectionAsync(jni::JNIEnv& env, const jni::Object<JNIType>& jObject) {
|
||||
auto global = jni::NewGlobal<jni::EnvAttachingDeleter>(env, jObject);
|
||||
auto object = std::make_shared<decltype(global)>(std::move(global));
|
||||
|
||||
Update::Converter converterFn = [this, object](ActorRef<GeoJSONDataCallback> _callback) {
|
||||
converter->self().invoke(&FeatureConverter::convertObject<JNIType>, object, _callback);
|
||||
};
|
||||
|
||||
setAsync(converterFn);
|
||||
}
|
||||
|
||||
void GeoJSONSource::setAsync(Update::Converter converterFn) {
|
||||
awaitingUpdate = std::make_unique<Update>(
|
||||
std::move(converterFn),
|
||||
std::make_unique<Actor<GeoJSONDataCallback>>(
|
||||
*Scheduler::GetCurrent(), [this](std::shared_ptr<style::GeoJSONData> geoJSONData) {
|
||||
// conversion from Java features to core ones finished
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
|
||||
// Update the core source
|
||||
source.as<mbgl::style::GeoJSONSource>()->setGeoJSONData(std::move(geoJSONData));
|
||||
|
||||
// if there is an awaiting update, execute it, otherwise, release resources
|
||||
if (awaitingUpdate) {
|
||||
update = std::move(awaitingUpdate);
|
||||
update->converterFn(update->callback->self());
|
||||
} else {
|
||||
update.reset();
|
||||
}
|
||||
}));
|
||||
|
||||
// If another update is running, wait
|
||||
if (update) {
|
||||
return;
|
||||
}
|
||||
|
||||
// no updates are being processed, execute this one
|
||||
update = std::move(awaitingUpdate);
|
||||
update->converterFn(update->callback->self());
|
||||
}
|
||||
|
||||
void GeoJSONSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<GeoJSONSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<GeoJSONSource>(
|
||||
env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<GeoJSONSource, const jni::String&, const jni::Object<>&>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&GeoJSONSource::setGeoJSONString, "nativeSetGeoJsonString"),
|
||||
METHOD(&GeoJSONSource::setFeatureCollection, "nativeSetFeatureCollection"),
|
||||
METHOD(&GeoJSONSource::setFeature, "nativeSetFeature"),
|
||||
METHOD(&GeoJSONSource::setGeometry, "nativeSetGeometry"),
|
||||
METHOD(&GeoJSONSource::setURL, "nativeSetUrl"),
|
||||
METHOD(&GeoJSONSource::getURL, "nativeGetUrl"),
|
||||
METHOD(&GeoJSONSource::querySourceFeatures, "querySourceFeatures"),
|
||||
METHOD(&GeoJSONSource::getClusterChildren, "nativeGetClusterChildren"),
|
||||
METHOD(&GeoJSONSource::getClusterLeaves, "nativeGetClusterLeaves"),
|
||||
METHOD(&GeoJSONSource::getClusterExpansionZoom, "nativeGetClusterExpansionZoom"));
|
||||
}
|
||||
|
||||
void FeatureConverter::convertJson(std::shared_ptr<std::string> json, ActorRef<GeoJSONDataCallback> callback) {
|
||||
using namespace mbgl::style::conversion;
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
// Convert the jni object
|
||||
Error error;
|
||||
std::optional<GeoJSON> converted = parseGeoJSON(*json, error);
|
||||
if (!converted) {
|
||||
mbgl::Log::Error(mbgl::Event::JNI, "Error setting geo json: " + error.message);
|
||||
return;
|
||||
}
|
||||
|
||||
callback.invoke(&GeoJSONDataCallback::operator(),
|
||||
style::GeoJSONData::create(*converted, sequencedScheduler, options));
|
||||
}
|
||||
|
||||
template <class JNIType>
|
||||
void FeatureConverter::convertObject(
|
||||
std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>> jObject,
|
||||
ActorRef<GeoJSONDataCallback> callback) {
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
android::UniqueEnv _env = android::AttachEnv();
|
||||
// Convert the jni object
|
||||
auto geometry = JNIType::convert(*_env, *jObject);
|
||||
callback.invoke(&GeoJSONDataCallback::operator(),
|
||||
style::GeoJSONData::create(geometry, sequencedScheduler, options));
|
||||
}
|
||||
|
||||
Update::Update(Converter _converterFn, std::unique_ptr<Actor<GeoJSONDataCallback>> _callback)
|
||||
: converterFn(std::move(_converterFn)),
|
||||
callback(std::move(_callback)) {}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include "../../geojson/geometry.hpp"
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include "../../geojson/feature_collection.hpp"
|
||||
#include "../../android_renderer_frontend.hpp"
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
using GeoJSONDataCallback = std::function<void(std::shared_ptr<style::GeoJSONData>)>;
|
||||
|
||||
class FeatureConverter {
|
||||
public:
|
||||
explicit FeatureConverter(Immutable<style::GeoJSONOptions> options_)
|
||||
: options(std::move(options_)),
|
||||
sequencedScheduler(Scheduler::GetSequenced()) {}
|
||||
void convertJson(std::shared_ptr<std::string>, ActorRef<GeoJSONDataCallback>);
|
||||
|
||||
template <class JNIType>
|
||||
void convertObject(std::shared_ptr<jni::Global<jni::Object<JNIType>, jni::EnvAttachingDeleter>>,
|
||||
ActorRef<GeoJSONDataCallback>);
|
||||
|
||||
private:
|
||||
Immutable<style::GeoJSONOptions> options;
|
||||
std::shared_ptr<Scheduler> sequencedScheduler;
|
||||
};
|
||||
|
||||
struct Update {
|
||||
using Converter = std::function<void(ActorRef<GeoJSONDataCallback>)>;
|
||||
Converter converterFn;
|
||||
|
||||
std::unique_ptr<Actor<GeoJSONDataCallback>> callback;
|
||||
|
||||
Update(Converter, std::unique_ptr<Actor<GeoJSONDataCallback>>);
|
||||
};
|
||||
|
||||
class GeoJSONSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/GeoJsonSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
GeoJSONSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&);
|
||||
GeoJSONSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
~GeoJSONSource();
|
||||
|
||||
private:
|
||||
void setGeoJSONString(jni::JNIEnv&, const jni::String&);
|
||||
void setFeatureCollection(jni::JNIEnv&, const jni::Object<geojson::FeatureCollection>&);
|
||||
void setFeature(jni::JNIEnv&, const jni::Object<geojson::Feature>&);
|
||||
void setGeometry(jni::JNIEnv&, const jni::Object<geojson::Geometry>&);
|
||||
void setURL(jni::JNIEnv&, const jni::String&);
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> querySourceFeatures(jni::JNIEnv&,
|
||||
const jni::Array<jni::Object<>>&);
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> getClusterChildren(jni::JNIEnv&,
|
||||
const jni::Object<geojson::Feature>&);
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> getClusterLeaves(jni::JNIEnv&,
|
||||
const jni::Object<geojson::Feature>&,
|
||||
jni::jlong,
|
||||
jni::jlong);
|
||||
jint getClusterExpansionZoom(jni::JNIEnv&, const jni::Object<geojson::Feature>&);
|
||||
|
||||
jni::Local<jni::String> getURL(jni::JNIEnv&);
|
||||
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
std::unique_ptr<Update> awaitingUpdate;
|
||||
std::unique_ptr<Update> update;
|
||||
std::shared_ptr<ThreadPool> threadPool;
|
||||
std::unique_ptr<Actor<FeatureConverter>> converter;
|
||||
|
||||
template <class JNIType>
|
||||
void setCollectionAsync(jni::JNIEnv&, const jni::Object<JNIType>&);
|
||||
|
||||
void setAsync(Update::Converter);
|
||||
|
||||
}; // class GeoJSONSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,74 @@
|
||||
#include "image_source.hpp"
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include "../android_conversion.hpp"
|
||||
|
||||
// C++ -> Java conversion
|
||||
#include "../../conversion/conversion.hpp"
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
#include <mbgl/util/premultiply.hpp>
|
||||
|
||||
#include "bitmap.hpp"
|
||||
#include <string>
|
||||
#include <array>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
ImageSource::ImageSource(jni::JNIEnv& env,
|
||||
const jni::String& sourceId,
|
||||
const jni::Object<LatLngQuad>& coordinatesObject)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::ImageSource>(jni::Make<std::string>(env, sourceId),
|
||||
LatLngQuad::getLatLngArray(env, coordinatesObject))) {}
|
||||
|
||||
ImageSource::ImageSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
ImageSource::~ImageSource() = default;
|
||||
|
||||
void ImageSource::setURL(jni::JNIEnv& env, const jni::String& url) {
|
||||
// Update the core source
|
||||
source.as<mbgl::style::ImageSource>()->ImageSource::setURL(jni::Make<std::string>(env, url));
|
||||
}
|
||||
|
||||
jni::Local<jni::String> ImageSource::getURL(jni::JNIEnv& env) {
|
||||
std::optional<std::string> url = source.as<mbgl::style::ImageSource>()->ImageSource::getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
void ImageSource::setImage(jni::JNIEnv& env, const jni::Object<Bitmap>& bitmap) {
|
||||
source.as<mbgl::style::ImageSource>()->setImage(Bitmap::GetImage(env, bitmap));
|
||||
}
|
||||
|
||||
void ImageSource::setCoordinates(jni::JNIEnv& env, const jni::Object<LatLngQuad>& coordinatesObject) {
|
||||
source.as<mbgl::style::ImageSource>()->setCoordinates(LatLngQuad::getLatLngArray(env, coordinatesObject));
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> ImageSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<ImageSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void ImageSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<ImageSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<ImageSource>(env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<ImageSource, const jni::String&, const jni::Object<LatLngQuad>&>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&ImageSource::setURL, "nativeSetUrl"),
|
||||
METHOD(&ImageSource::getURL, "nativeGetUrl"),
|
||||
METHOD(&ImageSource::setImage, "nativeSetImage"),
|
||||
METHOD(&ImageSource::setCoordinates, "nativeSetCoordinates"));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include "../../geometry/lat_lng_quad.hpp"
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class Bitmap;
|
||||
|
||||
class ImageSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/ImageSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
ImageSource(jni::JNIEnv&, const jni::String&, const jni::Object<LatLngQuad>&);
|
||||
|
||||
ImageSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
|
||||
~ImageSource();
|
||||
|
||||
void setURL(jni::JNIEnv&, const jni::String&);
|
||||
jni::Local<jni::String> getURL(jni::JNIEnv&);
|
||||
|
||||
void setImage(jni::JNIEnv&, const jni::Object<Bitmap>&);
|
||||
|
||||
void setCoordinates(jni::JNIEnv&, const jni::Object<LatLngQuad>&);
|
||||
|
||||
private:
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class ImageSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "raster_dem_source.hpp"
|
||||
|
||||
#include "../android_conversion.hpp"
|
||||
#include "../value.hpp"
|
||||
#include "../conversion/url_or_tileset.hpp"
|
||||
#include "source.hpp"
|
||||
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
RasterDEMSource::RasterDEMSource(jni::JNIEnv& env,
|
||||
const jni::String& sourceId,
|
||||
const jni::Object<>& urlOrTileSet,
|
||||
jni::jint tileSize)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::RasterDEMSource>(
|
||||
jni::Make<std::string>(env, sourceId), convertURLOrTileset(Value(env, urlOrTileSet)), tileSize)) {}
|
||||
|
||||
RasterDEMSource::RasterDEMSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
RasterDEMSource::~RasterDEMSource() = default;
|
||||
|
||||
jni::Local<jni::String> RasterDEMSource::getURL(jni::JNIEnv& env) {
|
||||
std::optional<std::string> url = source.as<mbgl::style::RasterDEMSource>()->RasterDEMSource::getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> RasterDEMSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<RasterDEMSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void RasterDEMSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<RasterDEMSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<RasterDEMSource>(
|
||||
env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<RasterDEMSource, const jni::String&, const jni::Object<>&, jni::jint>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&RasterDEMSource::getURL, "nativeGetUrl"));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/sources/raster_dem_source.hpp>
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class RasterDEMSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/RasterDemSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
RasterDEMSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&, jni::jint);
|
||||
RasterDEMSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
~RasterDEMSource();
|
||||
|
||||
jni::Local<jni::String> getURL(jni::JNIEnv&);
|
||||
|
||||
private:
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class RasterDEMSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "raster_source.hpp"
|
||||
|
||||
#include "../android_conversion.hpp"
|
||||
#include "../value.hpp"
|
||||
#include "../conversion/url_or_tileset.hpp"
|
||||
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
RasterSource::RasterSource(jni::JNIEnv& env,
|
||||
const jni::String& sourceId,
|
||||
const jni::Object<>& urlOrTileSet,
|
||||
jni::jint tileSize)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::RasterSource>(
|
||||
jni::Make<std::string>(env, sourceId), convertURLOrTileset(Value(env, urlOrTileSet)), tileSize)) {}
|
||||
|
||||
RasterSource::RasterSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
RasterSource::~RasterSource() = default;
|
||||
|
||||
jni::Local<jni::String> RasterSource::getURL(jni::JNIEnv& env) {
|
||||
std::optional<std::string> url = source.as<mbgl::style::RasterSource>()->RasterSource::getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> RasterSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<RasterSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void RasterSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<RasterSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<RasterSource>(
|
||||
env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<RasterSource, const jni::String&, const jni::Object<>&, jni::jint>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&RasterSource::getURL, "nativeGetUrl"));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class RasterSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/RasterSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
RasterSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&, jni::jint);
|
||||
RasterSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
~RasterSource();
|
||||
|
||||
jni::Local<jni::String> getURL(jni::JNIEnv&);
|
||||
|
||||
private:
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class RasterSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,257 @@
|
||||
#include "source.hpp"
|
||||
#include "../android_conversion.hpp"
|
||||
#include "attach_env.hpp"
|
||||
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
#include <mbgl/style/style.hpp>
|
||||
#include <mbgl/util/logging.hpp>
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include <mbgl/style/conversion/source.hpp>
|
||||
#include <mbgl/style/conversion_impl.hpp>
|
||||
|
||||
// C++ -> Java conversion
|
||||
#include "../conversion/property_value.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
// Core Sources
|
||||
#include <mbgl/style/sources/geojson_source.hpp>
|
||||
#include <mbgl/style/sources/image_source.hpp>
|
||||
#include <mbgl/style/sources/raster_source.hpp>
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
|
||||
// Android Source peers
|
||||
#include "geojson_source.hpp"
|
||||
#include "image_source.hpp"
|
||||
#include "raster_source.hpp"
|
||||
#include "unknown_source.hpp"
|
||||
#include "vector_source.hpp"
|
||||
#include "custom_geometry_source.hpp"
|
||||
#include "raster_dem_source.hpp"
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
static std::unique_ptr<Source> createSourcePeer(jni::JNIEnv& env,
|
||||
mbgl::style::Source& coreSource,
|
||||
AndroidRendererFrontend* frontend) {
|
||||
if (coreSource.is<mbgl::style::VectorSource>()) {
|
||||
return std::make_unique<VectorSource>(env, *coreSource.as<mbgl::style::VectorSource>(), frontend);
|
||||
} else if (coreSource.is<mbgl::style::RasterSource>()) {
|
||||
return std::make_unique<RasterSource>(env, *coreSource.as<mbgl::style::RasterSource>(), frontend);
|
||||
} else if (coreSource.is<mbgl::style::GeoJSONSource>()) {
|
||||
return std::make_unique<GeoJSONSource>(env, *coreSource.as<mbgl::style::GeoJSONSource>(), frontend);
|
||||
} else if (coreSource.is<mbgl::style::ImageSource>()) {
|
||||
return std::make_unique<ImageSource>(env, *coreSource.as<mbgl::style::ImageSource>(), frontend);
|
||||
} else {
|
||||
return std::make_unique<UnknownSource>(env, coreSource, frontend);
|
||||
}
|
||||
}
|
||||
|
||||
const jni::Object<Source>& Source::peerForCoreSource(jni::JNIEnv& env,
|
||||
mbgl::style::Source& coreSource,
|
||||
AndroidRendererFrontend& frontend) {
|
||||
if (!coreSource.peer.has_value()) {
|
||||
coreSource.peer = createSourcePeer(env, coreSource, &frontend);
|
||||
}
|
||||
return coreSource.peer.get<std::unique_ptr<Source>>()->javaPeer;
|
||||
}
|
||||
|
||||
const jni::Object<Source>& Source::peerForCoreSource(jni::JNIEnv& env, mbgl::style::Source& coreSource) {
|
||||
if (!coreSource.peer.has_value()) {
|
||||
coreSource.peer = createSourcePeer(env, coreSource, nullptr);
|
||||
}
|
||||
return coreSource.peer.get<std::unique_ptr<Source>>()->javaPeer;
|
||||
}
|
||||
|
||||
Source::Source(jni::JNIEnv& env,
|
||||
mbgl::style::Source& coreSource,
|
||||
const jni::Object<Source>& obj,
|
||||
AndroidRendererFrontend* frontend)
|
||||
: source(coreSource),
|
||||
javaPeer(jni::NewGlobal(env, obj)),
|
||||
rendererFrontend(frontend) {}
|
||||
|
||||
Source::Source(jni::JNIEnv&, std::unique_ptr<mbgl::style::Source> coreSource)
|
||||
: ownedSource(std::move(coreSource)),
|
||||
source(*ownedSource) {}
|
||||
|
||||
Source::~Source() {
|
||||
if (ownedSource) {
|
||||
ownedSource.reset();
|
||||
ownedSource.release();
|
||||
}
|
||||
// Before being added to a map, the Java peer owns this C++ peer and cleans
|
||||
// up after itself correctly through the jni native peer bindings.
|
||||
// After being added to the map, the ownership is flipped and the C++ peer
|
||||
// has a strong reference
|
||||
// to it's Java peer, preventing the Java peer from being GC'ed.
|
||||
// In this case, the core source initiates the destruction, which requires
|
||||
// releasing the Java peer, while also resetting it's nativePtr to 0 to
|
||||
// prevent the subsequent GC of the Java peer from re-entering this dtor.
|
||||
if (ownedSource.get() == nullptr && javaPeer.get() != nullptr) {
|
||||
// Manually clear the java peer
|
||||
android::UniqueEnv env = android::AttachEnv();
|
||||
static auto& javaClass = jni::Class<Source>::Singleton(*env);
|
||||
static auto nativePtrField = javaClass.GetField<jlong>(*env, "nativePtr");
|
||||
javaPeer.Set(*env, nativePtrField, (jlong)0);
|
||||
javaPeer.reset();
|
||||
}
|
||||
}
|
||||
|
||||
jni::Local<jni::String> Source::getId(jni::JNIEnv& env) {
|
||||
return jni::Make<jni::String>(env, source.getID());
|
||||
}
|
||||
|
||||
jni::Local<jni::String> Source::getAttribution(jni::JNIEnv& env) {
|
||||
auto attribution = source.getAttribution();
|
||||
return attribution ? jni::Make<jni::String>(env, attribution.value()) : jni::Make<jni::String>(env, "");
|
||||
}
|
||||
|
||||
void Source::setPrefetchZoomDelta(jni::JNIEnv& env, jni::Integer& delta) {
|
||||
if (!delta) {
|
||||
source.setPrefetchZoomDelta(std::nullopt);
|
||||
} else {
|
||||
source.setPrefetchZoomDelta(jni::Unbox(env, delta));
|
||||
}
|
||||
}
|
||||
|
||||
jni::Local<jni::Integer> Source::getPrefetchZoomDelta(jni::JNIEnv& env) {
|
||||
auto delta = source.getPrefetchZoomDelta();
|
||||
if (delta.has_value()) {
|
||||
return jni::Box(env, jni::jint(delta.value()));
|
||||
}
|
||||
return jni::Local<jni::Integer>(env, nullptr);
|
||||
}
|
||||
|
||||
void Source::setMaxOverscaleFactorForParentTiles(jni::JNIEnv& env, jni::Integer& maxOverscaleFactor) {
|
||||
if (!maxOverscaleFactor) {
|
||||
source.setMaxOverscaleFactorForParentTiles(std::nullopt);
|
||||
} else {
|
||||
source.setMaxOverscaleFactorForParentTiles(jni::Unbox(env, maxOverscaleFactor));
|
||||
}
|
||||
}
|
||||
|
||||
jni::Local<jni::Integer> Source::getMaxOverscaleFactorForParentTiles(jni::JNIEnv& env) {
|
||||
auto maxOverscaleFactor = source.getMaxOverscaleFactorForParentTiles();
|
||||
if (maxOverscaleFactor) {
|
||||
return jni::Box(env, jni::jint(*maxOverscaleFactor));
|
||||
}
|
||||
return jni::Local<jni::Integer>(env, nullptr);
|
||||
}
|
||||
|
||||
void Source::addToStyle(JNIEnv& env, const jni::Object<Source>& obj, mbgl::style::Style& style) {
|
||||
if (!ownedSource) {
|
||||
throw std::runtime_error("Cannot add source twice");
|
||||
}
|
||||
|
||||
// Add source to style and release ownership
|
||||
style.addSource(std::move(ownedSource));
|
||||
|
||||
// Add peer to core source
|
||||
source.peer = std::unique_ptr<Source>(this);
|
||||
|
||||
// Add strong reference to java source
|
||||
javaPeer = jni::NewGlobal(env, obj);
|
||||
}
|
||||
|
||||
void Source::addToMap(JNIEnv& env, const jni::Object<Source>& obj, mbgl::Map& map, AndroidRendererFrontend& frontend) {
|
||||
// Check to see if we own the source first
|
||||
if (!ownedSource) {
|
||||
throw std::runtime_error("Cannot add source twice");
|
||||
}
|
||||
|
||||
// Add source to map and release ownership
|
||||
map.getStyle().addSource(std::move(ownedSource));
|
||||
|
||||
// Add peer to core source
|
||||
source.peer = std::unique_ptr<Source>(this);
|
||||
|
||||
// Add strong reference to java source
|
||||
javaPeer = jni::NewGlobal(env, obj);
|
||||
|
||||
rendererFrontend = &frontend;
|
||||
}
|
||||
|
||||
bool Source::removeFromMap(JNIEnv&, const jni::Object<Source>&, mbgl::Map& map) {
|
||||
// Cannot remove if not attached yet
|
||||
if (ownedSource) {
|
||||
throw std::runtime_error("Cannot remove detached source");
|
||||
}
|
||||
|
||||
// Remove the source from the map and take ownership
|
||||
ownedSource = map.getStyle().removeSource(source.getID());
|
||||
|
||||
// The source may not be removed if any layers still reference it
|
||||
return ownedSource != nullptr;
|
||||
}
|
||||
|
||||
jni::Local<jni::Boolean> Source::isVolatile(jni::JNIEnv& env) {
|
||||
return jni::Box(env, jni::jboolean(source.isVolatile()));
|
||||
}
|
||||
|
||||
void Source::setVolatile(JNIEnv& env, jni::Boolean& value) {
|
||||
source.setVolatile(jni::Unbox(env, value));
|
||||
}
|
||||
|
||||
void Source::setMinimumTileUpdateInterval(JNIEnv& env, jni::Long& interval) {
|
||||
source.setMinimumTileUpdateInterval(Milliseconds(jni::Unbox(env, interval)));
|
||||
}
|
||||
|
||||
jni::Local<jni::Long> Source::getMinimumTileUpdateInterval(JNIEnv& env) {
|
||||
return jni::Box(env, jni::jlong(source.getMinimumTileUpdateInterval().count() / 1000000));
|
||||
}
|
||||
|
||||
void Source::releaseJavaPeer() {
|
||||
// We can't release the peer if the source was not removed from the map
|
||||
if (!ownedSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Release the peer relationships. These will be re-established when the source is added to a map
|
||||
assert(ownedSource->peer.has_value());
|
||||
ownedSource->peer.get<std::unique_ptr<Source>>().release();
|
||||
ownedSource->peer = mapbox::base::TypeWrapper();
|
||||
|
||||
// Release the strong reference to the java peer
|
||||
assert(javaPeer);
|
||||
javaPeer.reset();
|
||||
|
||||
rendererFrontend = nullptr;
|
||||
}
|
||||
|
||||
void Source::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<Source>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<Source>(
|
||||
env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
METHOD(&Source::getId, "nativeGetId"),
|
||||
METHOD(&Source::getAttribution, "nativeGetAttribution"),
|
||||
METHOD(&Source::setPrefetchZoomDelta, "nativeSetPrefetchZoomDelta"),
|
||||
METHOD(&Source::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"),
|
||||
METHOD(&Source::setMaxOverscaleFactorForParentTiles, "nativeSetMaxOverscaleFactorForParentTiles"),
|
||||
METHOD(&Source::getMaxOverscaleFactorForParentTiles, "nativeGetMaxOverscaleFactorForParentTiles"),
|
||||
METHOD(&Source::isVolatile, "nativeIsVolatile"),
|
||||
METHOD(&Source::setVolatile, "nativeSetVolatile"),
|
||||
METHOD(&Source::setMinimumTileUpdateInterval, "nativeSetMinimumTileUpdateInterval"),
|
||||
METHOD(&Source::getMinimumTileUpdateInterval, "nativeGetMinimumTileUpdateInterval"));
|
||||
|
||||
// Register subclasses
|
||||
GeoJSONSource::registerNative(env);
|
||||
ImageSource::registerNative(env);
|
||||
RasterSource::registerNative(env);
|
||||
UnknownSource::registerNative(env);
|
||||
VectorSource::registerNative(env);
|
||||
CustomGeometrySource::registerNative(env);
|
||||
RasterDEMSource::registerNative(env);
|
||||
}
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,80 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/util/noncopyable.hpp>
|
||||
#include <mbgl/map/map.hpp>
|
||||
#include <mbgl/style/source.hpp>
|
||||
|
||||
#include "../value.hpp"
|
||||
#include "../../android_renderer_frontend.hpp"
|
||||
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class Source : private mbgl::util::noncopyable {
|
||||
public:
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/Source"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
static const jni::Object<Source>& peerForCoreSource(jni::JNIEnv&, mbgl::style::Source&);
|
||||
static const jni::Object<Source>& peerForCoreSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend&);
|
||||
|
||||
/*
|
||||
* Called when a Java object is created for a core source that belongs to a map.
|
||||
*/
|
||||
Source(jni::JNIEnv&, mbgl::style::Source&, const jni::Object<Source>&, AndroidRendererFrontend*);
|
||||
|
||||
/*
|
||||
* Called when a Java object is created for a new core source that does not
|
||||
* belong to a map.
|
||||
*/
|
||||
Source(jni::JNIEnv&, std::unique_ptr<mbgl::style::Source>);
|
||||
|
||||
virtual ~Source();
|
||||
|
||||
virtual void addToMap(JNIEnv&, const jni::Object<Source>&, mbgl::Map&, AndroidRendererFrontend&);
|
||||
|
||||
virtual bool removeFromMap(JNIEnv&, const jni::Object<Source>&, mbgl::Map&);
|
||||
|
||||
void releaseJavaPeer();
|
||||
|
||||
jni::Local<jni::String> getId(jni::JNIEnv&);
|
||||
|
||||
jni::Local<jni::String> getAttribution(jni::JNIEnv&);
|
||||
|
||||
void setPrefetchZoomDelta(jni::JNIEnv& env, jni::Integer& delta);
|
||||
|
||||
jni::Local<jni::Integer> getPrefetchZoomDelta(jni::JNIEnv&);
|
||||
|
||||
void setMaxOverscaleFactorForParentTiles(jni::JNIEnv& env, jni::Integer& delta);
|
||||
|
||||
jni::Local<jni::Integer> getMaxOverscaleFactorForParentTiles(jni::JNIEnv&);
|
||||
|
||||
void addToStyle(JNIEnv& env, const jni::Object<Source>& obj, mbgl::style::Style& style);
|
||||
|
||||
jni::Local<jni::Boolean> isVolatile(JNIEnv&);
|
||||
|
||||
void setVolatile(JNIEnv&, jni::Boolean&);
|
||||
|
||||
void setMinimumTileUpdateInterval(JNIEnv&, jni::Long&);
|
||||
|
||||
jni::Local<jni::Long> getMinimumTileUpdateInterval(JNIEnv&);
|
||||
|
||||
protected:
|
||||
// Set on newly created sources until added to the map.
|
||||
std::unique_ptr<mbgl::style::Source> ownedSource;
|
||||
|
||||
// Raw pointer that is valid at all times.
|
||||
mbgl::style::Source& source;
|
||||
|
||||
// Set when the source is added to a map.
|
||||
jni::Global<jni::Object<Source>> javaPeer;
|
||||
|
||||
// RendererFrontend pointer is valid only when added to the map.
|
||||
AndroidRendererFrontend* rendererFrontend{nullptr};
|
||||
};
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,35 @@
|
||||
#include "unknown_source.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
// Dummy initializer (We don't support initializing this from the JVM)
|
||||
std::unique_ptr<mbgl::android::UnknownSource> init(jni::JNIEnv&) {
|
||||
throw std::runtime_error("UnknownSource should not be initialized from the JVM");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
UnknownSource::UnknownSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
jni::Local<jni::Object<Source>> UnknownSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<UnknownSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void UnknownSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<UnknownSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<UnknownSource>(env, javaClass, "nativePtr", init, "initialize", "finalize");
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/source.hpp>
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class UnknownSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/UnknownSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
UnknownSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
|
||||
~UnknownSource() = default;
|
||||
|
||||
private:
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class UnknownSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "vector_source.hpp"
|
||||
|
||||
#include <mbgl/renderer/query.hpp>
|
||||
|
||||
// Java -> C++ conversion
|
||||
#include "../android_conversion.hpp"
|
||||
#include "../conversion/filter.hpp"
|
||||
|
||||
// C++ -> Java conversion
|
||||
#include "../../conversion/conversion.hpp"
|
||||
#include "../../conversion/collection.hpp"
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include "../conversion/url_or_tileset.hpp"
|
||||
|
||||
#include <mbgl/util/variant.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
VectorSource::VectorSource(jni::JNIEnv& env, const jni::String& sourceId, const jni::Object<>& urlOrTileSet)
|
||||
: Source(env,
|
||||
std::make_unique<mbgl::style::VectorSource>(jni::Make<std::string>(env, sourceId),
|
||||
convertURLOrTileset(Value(env, urlOrTileSet)))) {}
|
||||
|
||||
VectorSource::VectorSource(jni::JNIEnv& env, mbgl::style::Source& coreSource, AndroidRendererFrontend* frontend)
|
||||
: Source(env, coreSource, createJavaPeer(env), frontend) {}
|
||||
|
||||
VectorSource::~VectorSource() = default;
|
||||
|
||||
jni::Local<jni::String> VectorSource::getURL(jni::JNIEnv& env) {
|
||||
std::optional<std::string> url = source.as<mbgl::style::VectorSource>()->VectorSource::getURL();
|
||||
return url ? jni::Make<jni::String>(env, *url) : jni::Local<jni::String>();
|
||||
}
|
||||
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> VectorSource::querySourceFeatures(
|
||||
jni::JNIEnv& env, const jni::Array<jni::String>& jSourceLayerIds, const jni::Array<jni::Object<>>& jfilter) {
|
||||
using namespace mbgl::android::conversion;
|
||||
using namespace mbgl::android::geojson;
|
||||
|
||||
std::vector<mbgl::Feature> features;
|
||||
if (rendererFrontend) {
|
||||
features = rendererFrontend->querySourceFeatures(source.getID(),
|
||||
{toVector(env, jSourceLayerIds), toFilter(env, jfilter)});
|
||||
}
|
||||
return Feature::convert(env, features);
|
||||
}
|
||||
|
||||
jni::Local<jni::Object<Source>> VectorSource::createJavaPeer(jni::JNIEnv& env) {
|
||||
static auto& javaClass = jni::Class<VectorSource>::Singleton(env);
|
||||
static auto constructor = javaClass.GetConstructor<jni::jlong>(env);
|
||||
return javaClass.New(env, constructor, reinterpret_cast<jni::jlong>(this));
|
||||
}
|
||||
|
||||
void VectorSource::registerNative(jni::JNIEnv& env) {
|
||||
// Lookup the class
|
||||
static auto& javaClass = jni::Class<VectorSource>::Singleton(env);
|
||||
|
||||
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
||||
|
||||
// Register the peer
|
||||
jni::RegisterNativePeer<VectorSource>(env,
|
||||
javaClass,
|
||||
"nativePtr",
|
||||
jni::MakePeer<VectorSource, const jni::String&, const jni::Object<>&>,
|
||||
"initialize",
|
||||
"finalize",
|
||||
METHOD(&VectorSource::querySourceFeatures, "querySourceFeatures"),
|
||||
METHOD(&VectorSource::getURL, "nativeGetUrl"));
|
||||
}
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "source.hpp"
|
||||
#include <mbgl/style/sources/vector_source.hpp>
|
||||
#include "../../geojson/feature.hpp"
|
||||
#include <jni/jni.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace android {
|
||||
|
||||
class VectorSource : public Source {
|
||||
public:
|
||||
using SuperTag = Source;
|
||||
static constexpr auto Name() { return "org/maplibre/android/style/sources/VectorSource"; };
|
||||
|
||||
static void registerNative(jni::JNIEnv&);
|
||||
|
||||
VectorSource(jni::JNIEnv&, const jni::String&, const jni::Object<>&);
|
||||
VectorSource(jni::JNIEnv&, mbgl::style::Source&, AndroidRendererFrontend*);
|
||||
~VectorSource();
|
||||
|
||||
private:
|
||||
jni::Local<jni::Array<jni::Object<geojson::Feature>>> querySourceFeatures(jni::JNIEnv&,
|
||||
const jni::Array<jni::String>&,
|
||||
const jni::Array<jni::Object<>>& jfilter);
|
||||
|
||||
jni::Local<jni::String> getURL(jni::JNIEnv&);
|
||||
|
||||
jni::Local<jni::Object<Source>> createJavaPeer(jni::JNIEnv&);
|
||||
|
||||
}; // class VectorSource
|
||||
|
||||
} // namespace android
|
||||
} // namespace mbgl
|
||||
Reference in New Issue
Block a user