2205 lines
99 KiB
C++
2205 lines
99 KiB
C++
#include "native_map_view.hpp"
|
|
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <cassert>
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <cmath>
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <list>
|
|
#include <tuple>
|
|
|
|
#include <sys/system_properties.h>
|
|
|
|
#include <android/native_window_jni.h>
|
|
|
|
#include <jni/jni.hpp>
|
|
|
|
#include <mln/map/map.hpp>
|
|
#include <mln/map/map_options.hpp>
|
|
#include <mln/math/minmax.hpp>
|
|
#include <mln/util/action_journal.hpp>
|
|
#include <mln/util/constants.hpp>
|
|
#include <mln/util/event.hpp>
|
|
#include <mln/util/exception.hpp>
|
|
#include <mln/util/geo.hpp>
|
|
#include <mln/util/image.hpp>
|
|
#include <mln/util/logging.hpp>
|
|
#include <mln/util/platform.hpp>
|
|
#include <mln/util/projection.hpp>
|
|
#include <mln/style/style.hpp>
|
|
#include <mln/style/layers/raster_particle_layer.hpp>
|
|
#include <mln/style/layers/weather_color_layer.hpp>
|
|
#include <mln/style/sources/raster_array_source.hpp>
|
|
#include <mln/style/image.hpp>
|
|
#include <mln/style/filter.hpp>
|
|
#include <mln/renderer/query.hpp>
|
|
#include <mln/renderer/render_segment_json.hpp>
|
|
#include <mln/particle/field/field_profile_contract_json.hpp>
|
|
#include <mln/particle/ingress/encoded_field_transport.hpp>
|
|
|
|
// Java -> C++ conversion
|
|
#include "style/android_conversion.hpp"
|
|
#include <mln/style/conversion/filter.hpp>
|
|
#include <mln/style/conversion_impl.hpp>
|
|
|
|
// C++ -> Java conversion
|
|
#include "conversion/conversion.hpp"
|
|
#include "conversion/collection.hpp"
|
|
#include "style/conversion/filter.hpp"
|
|
#include "geojson/feature.hpp"
|
|
#include "gson/json_object.hpp"
|
|
#include "rendering_stats.hpp"
|
|
|
|
#include "android_renderer_frontend.hpp"
|
|
#include "attach_env.hpp"
|
|
#include "bitmap.hpp"
|
|
#include "bitmap_factory.hpp"
|
|
#include "file_source.hpp"
|
|
#include "geometry/lat_lng_bounds.hpp"
|
|
#include "java/util.hpp"
|
|
#include "jni.hpp"
|
|
#include "map/camera_position.hpp"
|
|
#include "map/image.hpp"
|
|
#include "map_renderer.hpp"
|
|
#include "run_loop_impl.hpp"
|
|
#include "style/light.hpp"
|
|
#include "tile/tile_operation.hpp"
|
|
|
|
namespace mln {
|
|
namespace android {
|
|
|
|
namespace {}
|
|
|
|
NativeMapView::NativeMapView(jni::JNIEnv& _env,
|
|
const jni::Object<NativeMapView>& _obj,
|
|
const jni::Object<FileSource>& jFileSource,
|
|
const jni::Object<MapRenderer>& jMapRenderer,
|
|
const jni::Object<NativeMapOptions>& jNativeMapOptions)
|
|
: javaPeer(_env, _obj),
|
|
mapRenderer(MapRenderer::getNativePeer(_env, jMapRenderer)),
|
|
pixelRatio(NativeMapOptions::pixelRatio(_env, jNativeMapOptions)) {
|
|
// Get a reference to the JavaVM for callbacks
|
|
if (_env.GetJavaVM(&vm) < 0) {
|
|
_env.ExceptionDescribe();
|
|
return;
|
|
}
|
|
|
|
mapRenderer.SetAsyncRendererCleanup(NativeMapOptions::asyncRendererCleanup(_env, jNativeMapOptions));
|
|
|
|
// Create a renderer frontend
|
|
rendererFrontend = AndroidRendererFrontend::create(_env, jMapRenderer);
|
|
|
|
// Create Map options
|
|
MapOptions options;
|
|
options.withMapMode(MapMode::Continuous)
|
|
.withSize(mln::Size{static_cast<uint32_t>(width), static_cast<uint32_t>(height)})
|
|
.withPixelRatio(pixelRatio)
|
|
.withConstrainMode(ConstrainMode::HeightOnly)
|
|
.withViewportMode(ViewportMode::Default)
|
|
.withCrossSourceCollisions(NativeMapOptions::crossSourceCollisions(_env, jNativeMapOptions))
|
|
.withFastPFOREnabled(NativeMapOptions::fastPFOREnabled(_env, jNativeMapOptions));
|
|
|
|
// Create the core map
|
|
map = std::make_unique<mln::Map>(*rendererFrontend,
|
|
*this,
|
|
options,
|
|
mln::android::FileSource::getSharedResourceOptions(_env, jFileSource),
|
|
mln::android::FileSource::getSharedClientOptions(_env, jFileSource),
|
|
mln::android::NativeMapOptions::getActionJournalOptions(_env, jNativeMapOptions));
|
|
}
|
|
|
|
/**
|
|
* Called through NativeMapView#destroy()
|
|
*/
|
|
NativeMapView::~NativeMapView() {
|
|
map.reset();
|
|
vm = nullptr;
|
|
}
|
|
|
|
void NativeMapView::onCameraWillChange(MapObserver::CameraChangeMode mode) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onCameraWillChange = javaClass.GetMethod<void(jboolean)>(*_env, "onCameraWillChange");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onCameraWillChange, (jboolean)(mode != MapObserver::CameraChangeMode::Immediate));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onCameraIsChanging() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onCameraIsChanging = javaClass.GetMethod<void()>(*_env, "onCameraIsChanging");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onCameraIsChanging);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onCameraDidChange(MapObserver::CameraChangeMode mode) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onCameraDidChange = javaClass.GetMethod<void(jboolean)>(*_env, "onCameraDidChange");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onCameraDidChange, (jboolean)(mode != MapObserver::CameraChangeMode::Immediate));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onWillStartLoadingMap() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onWillStartLoadingMap = javaClass.GetMethod<void()>(*_env, "onWillStartLoadingMap");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onWillStartLoadingMap);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidFinishLoadingMap() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidFinishLoadingMap = javaClass.GetMethod<void()>(*_env, "onDidFinishLoadingMap");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidFinishLoadingMap);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidFailLoadingMap(MapLoadError, const std::string& error) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidFailLoadingMap = javaClass.GetMethod<void(jni::String)>(*_env, "onDidFailLoadingMap");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidFailLoadingMap, jni::Make<jni::String>(*_env, error));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onWillStartRenderingFrame() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onWillStartRenderingFrame = javaClass.GetMethod<void()>(*_env, "onWillStartRenderingFrame");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onWillStartRenderingFrame);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidFinishRenderingFrame(const MapObserver::RenderFrameStatus& status) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidFinishRenderingFrame = javaClass.GetMethod<void(jboolean, jni::Object<RenderingStats>)>(
|
|
*_env, "onDidFinishRenderingFrame");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
if (!renderingStats) {
|
|
renderingStats = jni::NewGlobal(*_env, RenderingStats::Create(*_env));
|
|
}
|
|
|
|
RenderingStats::Update(*_env, renderingStats, status.renderingStats);
|
|
|
|
weakReference.Call(*_env,
|
|
onDidFinishRenderingFrame,
|
|
(jboolean)(status.mode != MapObserver::RenderMode::Partial),
|
|
renderingStats);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onWillStartRenderingMap() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onWillStartRenderingMap = javaClass.GetMethod<void()>(*_env, "onWillStartRenderingMap");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onWillStartRenderingMap);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidFinishRenderingMap(MapObserver::RenderMode mode) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidFinishRenderingMap = javaClass.GetMethod<void(jboolean)>(*_env, "onDidFinishRenderingMap");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidFinishRenderingMap, (jboolean)(mode != MapObserver::RenderMode::Partial));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidBecomeIdle() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidBecomeIdle = javaClass.GetMethod<void()>(*_env, "onDidBecomeIdle");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidBecomeIdle);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onDidFinishLoadingStyle() {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onDidFinishLoadingStyle = javaClass.GetMethod<void()>(*_env, "onDidFinishLoadingStyle");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onDidFinishLoadingStyle);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onSourceChanged(mln::style::Source& source) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSourceChanged = javaClass.GetMethod<void(jni::String)>(*_env, "onSourceChanged");
|
|
auto sourceId = jni::Make<jni::String>(*_env, source.getID());
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onSourceChanged, sourceId);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onStyleImageMissing(const std::string& imageId) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onStyleImageMissing = javaClass.GetMethod<void(jni::String)>(*_env, "onStyleImageMissing");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onStyleImageMissing, jni::Make<jni::String>(*_env, imageId));
|
|
}
|
|
}
|
|
|
|
bool NativeMapView::onCanRemoveUnusedStyleImage(const std::string& imageId) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onCanRemoveUnusedStyleImage = javaClass.GetMethod<jboolean(jni::String)>(*_env,
|
|
"onCanRemoveUnusedStyleImage");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
return weakReference.Call(*_env, onCanRemoveUnusedStyleImage, jni::Make<jni::String>(*_env, imageId));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// JNI Methods //
|
|
|
|
void NativeMapView::resizeView(jni::JNIEnv&, int w, int h) {
|
|
width = util::max(64, w);
|
|
height = util::max(64, h);
|
|
map->setSize({static_cast<uint32_t>(width), static_cast<uint32_t>(height)});
|
|
}
|
|
|
|
jni::Local<jni::String> NativeMapView::getStyleUrl(jni::JNIEnv& env) {
|
|
return jni::Make<jni::String>(env, map->getStyle().getURL());
|
|
}
|
|
|
|
void NativeMapView::setStyleUrl(jni::JNIEnv& env, const jni::String& url) {
|
|
map->getStyle().loadURL(jni::Make<std::string>(env, url));
|
|
}
|
|
|
|
jni::Local<jni::String> NativeMapView::getStyleJson(jni::JNIEnv& env) {
|
|
return jni::Make<jni::String>(env, map->getStyle().getJSON());
|
|
}
|
|
|
|
void NativeMapView::setStyleJson(jni::JNIEnv& env, const jni::String& json) {
|
|
map->getStyle().loadJSON(jni::Make<std::string>(env, json));
|
|
}
|
|
|
|
void NativeMapView::setLatLngBounds(jni::JNIEnv& env, const jni::Object<mln::android::LatLngBounds>& jBounds) {
|
|
mln::BoundOptions bounds;
|
|
if (jBounds) {
|
|
bounds.withLatLngBounds(mln::android::LatLngBounds::getLatLngBounds(env, jBounds));
|
|
} else {
|
|
bounds.withLatLngBounds(mln::LatLngBounds());
|
|
}
|
|
map->setBounds(bounds);
|
|
}
|
|
|
|
void NativeMapView::cancelTransitions(jni::JNIEnv&) {
|
|
map->cancelTransitions();
|
|
}
|
|
|
|
void NativeMapView::setGestureInProgress(jni::JNIEnv&, jni::jboolean inProgress) {
|
|
map->setGestureInProgress(inProgress);
|
|
}
|
|
|
|
void NativeMapView::moveBy(jni::JNIEnv&, jni::jdouble dx, jni::jdouble dy, jni::jlong duration) {
|
|
mln::AnimationOptions animationOptions;
|
|
if (duration > 0) {
|
|
animationOptions.duration.emplace(mln::Milliseconds(duration));
|
|
animationOptions.easing.emplace(mln::util::UnitBezier{0.1, 0.4, 0.35, 1.0});
|
|
}
|
|
map->moveBy({dx, dy}, animationOptions);
|
|
}
|
|
|
|
void NativeMapView::jumpTo(jni::JNIEnv& env,
|
|
jni::jdouble bearing,
|
|
jni::jdouble latitude,
|
|
jni::jdouble longitude,
|
|
jni::jdouble pitch,
|
|
jni::jdouble zoom,
|
|
const jni::Array<jni::jdouble>& padding) {
|
|
mln::CameraOptions options;
|
|
if (bearing != -1) {
|
|
options.bearing = bearing;
|
|
}
|
|
options.center = mln::LatLng(latitude, longitude);
|
|
if (padding) {
|
|
assert(padding.Length(env) == 4);
|
|
options.padding = mln::EdgeInsets{
|
|
padding.Get(env, 0), padding.Get(env, 1), padding.Get(env, 2), padding.Get(env, 3)};
|
|
}
|
|
if (pitch != -1) {
|
|
options.pitch = pitch;
|
|
}
|
|
if (zoom != -1) {
|
|
options.zoom = zoom;
|
|
}
|
|
|
|
map->jumpTo(options);
|
|
}
|
|
|
|
void NativeMapView::easeTo(jni::JNIEnv& env,
|
|
jni::jdouble bearing,
|
|
jni::jdouble latitude,
|
|
jni::jdouble longitude,
|
|
jni::jlong duration,
|
|
jni::jdouble pitch,
|
|
jni::jdouble zoom,
|
|
const jni::Array<jni::jdouble>& padding,
|
|
jni::jboolean easing) {
|
|
mln::CameraOptions cameraOptions;
|
|
if (bearing != -1) {
|
|
cameraOptions.bearing = bearing;
|
|
}
|
|
cameraOptions.center = mln::LatLng(latitude, longitude);
|
|
if (padding) {
|
|
assert(padding.Length(env) == 4);
|
|
cameraOptions.padding = mln::EdgeInsets{
|
|
padding.Get(env, 0), padding.Get(env, 1), padding.Get(env, 2), padding.Get(env, 3)};
|
|
}
|
|
if (pitch != -1) {
|
|
cameraOptions.pitch = pitch;
|
|
}
|
|
if (zoom != -1) {
|
|
cameraOptions.zoom = zoom;
|
|
}
|
|
|
|
mln::AnimationOptions animationOptions;
|
|
animationOptions.duration.emplace(mln::Milliseconds(duration));
|
|
if (!easing) {
|
|
// add a linear interpolator instead of easing
|
|
animationOptions.easing.emplace(mln::util::UnitBezier{0, 0, 1, 1});
|
|
}
|
|
|
|
map->easeTo(cameraOptions, animationOptions);
|
|
}
|
|
|
|
void NativeMapView::flyTo(jni::JNIEnv& env,
|
|
jni::jdouble bearing,
|
|
jni::jdouble latitude,
|
|
jni::jdouble longitude,
|
|
jni::jlong duration,
|
|
jni::jdouble pitch,
|
|
jni::jdouble zoom,
|
|
const jni::Array<jni::jdouble>& padding) {
|
|
mln::CameraOptions cameraOptions;
|
|
if (bearing != -1) {
|
|
cameraOptions.bearing = bearing;
|
|
}
|
|
cameraOptions.center = mln::LatLng(latitude, longitude);
|
|
if (padding) {
|
|
assert(padding.Length(env) == 4);
|
|
cameraOptions.padding = mln::EdgeInsets{
|
|
padding.Get(env, 0), padding.Get(env, 1), padding.Get(env, 2), padding.Get(env, 3)};
|
|
}
|
|
if (pitch != -1) {
|
|
cameraOptions.pitch = pitch;
|
|
}
|
|
if (zoom != -1) {
|
|
cameraOptions.zoom = zoom;
|
|
}
|
|
|
|
mln::AnimationOptions animationOptions;
|
|
animationOptions.duration.emplace(mln::Milliseconds(duration));
|
|
map->flyTo(cameraOptions, animationOptions);
|
|
}
|
|
|
|
jni::Local<jni::Object<LatLng>> NativeMapView::getLatLng(JNIEnv& env) {
|
|
return LatLng::New(env, *map->getCameraOptions(std::nullopt).center);
|
|
}
|
|
|
|
void NativeMapView::setLatLng(jni::JNIEnv& env,
|
|
jni::jdouble latitude,
|
|
jni::jdouble longitude,
|
|
const jni::Array<jni::jdouble>& padding,
|
|
jni::jlong duration) {
|
|
mln::CameraOptions cameraOptions;
|
|
cameraOptions.center = mln::LatLng(latitude, longitude);
|
|
if (padding) {
|
|
assert(padding.Length(env) == 4);
|
|
cameraOptions.padding = mln::EdgeInsets{
|
|
padding.Get(env, 0), padding.Get(env, 1), padding.Get(env, 2), padding.Get(env, 3)};
|
|
}
|
|
map->easeTo(cameraOptions, mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraForLatLngBounds(
|
|
jni::JNIEnv& env,
|
|
const jni::Object<LatLngBounds>& jBounds,
|
|
double top,
|
|
double left,
|
|
double bottom,
|
|
double right,
|
|
double bearing,
|
|
double tilt) {
|
|
mln::EdgeInsets padding = {top, left, bottom, right};
|
|
return CameraPosition::New(
|
|
env,
|
|
map->cameraForLatLngBounds(mln::android::LatLngBounds::getLatLngBounds(env, jBounds), padding, bearing, tilt),
|
|
pixelRatio);
|
|
}
|
|
|
|
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraForGeometry(
|
|
jni::JNIEnv& env,
|
|
const jni::Object<geojson::Geometry>& jGeometry,
|
|
double top,
|
|
double left,
|
|
double bottom,
|
|
double right,
|
|
double bearing,
|
|
double tilt) {
|
|
auto geometry = geojson::Geometry::convert(env, jGeometry);
|
|
mln::EdgeInsets padding = {top, left, bottom, right};
|
|
return CameraPosition::New(env, map->cameraForGeometry(geometry, padding, bearing, tilt), pixelRatio);
|
|
}
|
|
|
|
void NativeMapView::setReachability(jni::JNIEnv&, jni::jboolean reachable) {
|
|
if (reachable) {
|
|
mln::NetworkStatus::Reachable();
|
|
}
|
|
}
|
|
|
|
void NativeMapView::resetPosition(jni::JNIEnv&) {
|
|
map->jumpTo(mln::CameraOptions().withCenter(mln::LatLng{}).withZoom(0.0).withBearing(0.0).withPitch(0.0));
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getPitch(jni::JNIEnv&) {
|
|
return *map->getCameraOptions().pitch;
|
|
}
|
|
|
|
void NativeMapView::setPitch(jni::JNIEnv&, jni::jdouble pitch, jni::jlong duration) {
|
|
map->easeTo(mln::CameraOptions().withPitch(pitch), mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
void NativeMapView::setZoom(jni::JNIEnv&, jni::jdouble zoom, jni::jdouble x, jni::jdouble y, jni::jlong duration) {
|
|
map->easeTo(mln::CameraOptions().withZoom(zoom).withAnchor(mln::ScreenCoordinate{x, y}),
|
|
mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getZoom(jni::JNIEnv&) {
|
|
return *map->getCameraOptions().zoom;
|
|
}
|
|
|
|
void NativeMapView::resetZoom(jni::JNIEnv&) {
|
|
map->jumpTo(mln::CameraOptions().withZoom(0.0));
|
|
}
|
|
|
|
void NativeMapView::setMinZoom(jni::JNIEnv&, jni::jdouble zoom) {
|
|
map->setBounds(BoundOptions().withMinZoom(zoom));
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getMinZoom(jni::JNIEnv&) {
|
|
return *map->getBounds().minZoom;
|
|
}
|
|
|
|
void NativeMapView::setMaxZoom(jni::JNIEnv&, jni::jdouble zoom) {
|
|
map->setBounds(BoundOptions().withMaxZoom(zoom));
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getMaxZoom(jni::JNIEnv&) {
|
|
return *map->getBounds().maxZoom;
|
|
}
|
|
|
|
void NativeMapView::setMinPitch(jni::JNIEnv&, jni::jdouble pitch) {
|
|
map->setBounds(BoundOptions().withMinPitch(pitch));
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getMinPitch(jni::JNIEnv&) {
|
|
return *map->getBounds().minPitch;
|
|
}
|
|
|
|
void NativeMapView::setMaxPitch(jni::JNIEnv&, jni::jdouble pitch) {
|
|
map->setBounds(BoundOptions().withMaxPitch(pitch));
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getMaxPitch(jni::JNIEnv&) {
|
|
return *map->getBounds().maxPitch;
|
|
}
|
|
|
|
void NativeMapView::rotateBy(
|
|
jni::JNIEnv&, jni::jdouble sx, jni::jdouble sy, jni::jdouble ex, jni::jdouble ey, jni::jlong duration) {
|
|
mln::ScreenCoordinate first(sx, sy);
|
|
mln::ScreenCoordinate second(ex, ey);
|
|
map->rotateBy(first, second, mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
void NativeMapView::setBearing(jni::JNIEnv&, jni::jdouble degrees, jni::jlong duration) {
|
|
map->easeTo(mln::CameraOptions().withBearing(degrees), mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
void NativeMapView::setBearingXY(
|
|
jni::JNIEnv&, jni::jdouble degrees, jni::jdouble cx, jni::jdouble cy, jni::jlong duration) {
|
|
mln::ScreenCoordinate anchor(cx, cy);
|
|
map->easeTo(mln::CameraOptions().withBearing(degrees).withAnchor(anchor),
|
|
mln::AnimationOptions{mln::Milliseconds(duration)});
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getBearing(jni::JNIEnv&) {
|
|
return *map->getCameraOptions().bearing;
|
|
}
|
|
|
|
void NativeMapView::resetNorth(jni::JNIEnv&) {
|
|
map->easeTo(mln::CameraOptions().withBearing(0.0), mln::AnimationOptions{{mln::Milliseconds(500)}});
|
|
}
|
|
|
|
void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env,
|
|
const jni::Array<jni::Object<LatLng>>& coordinates,
|
|
const jni::Object<RectF>& padding,
|
|
jdouble direction,
|
|
jni::jlong duration) {
|
|
NullCheck(env, &coordinates);
|
|
std::size_t count = coordinates.Length(env);
|
|
|
|
std::vector<mln::LatLng> latLngs;
|
|
latLngs.reserve(count);
|
|
|
|
for (std::size_t i = 0; i < count; i++) {
|
|
latLngs.push_back(LatLng::getLatLng(env, coordinates.Get(env, i)));
|
|
}
|
|
|
|
mln::EdgeInsets mbglInsets = {RectF::getTop(env, padding),
|
|
RectF::getLeft(env, padding),
|
|
RectF::getBottom(env, padding),
|
|
RectF::getRight(env, padding)};
|
|
mln::CameraOptions cameraOptions = map->cameraForLatLngs(latLngs, mbglInsets);
|
|
if (direction >= 0) {
|
|
cameraOptions.bearing = direction;
|
|
}
|
|
|
|
mln::AnimationOptions animationOptions;
|
|
if (duration > 0) {
|
|
animationOptions.duration.emplace(mln::Milliseconds(duration));
|
|
// equivalent to kCAMediaTimingFunctionDefault in iOS
|
|
animationOptions.easing.emplace(mln::util::UnitBezier{0.25, 0.1, 0.25, 0.1});
|
|
}
|
|
|
|
map->easeTo(cameraOptions, animationOptions);
|
|
}
|
|
|
|
void NativeMapView::getVisibleCoordinateBounds(JNIEnv& env, jni::Array<jdouble>& output) {
|
|
auto latlngBounds = map->latLngBoundsForCameraUnwrapped(map->getCameraOptions(std::nullopt));
|
|
|
|
double latNorth = latlngBounds.north();
|
|
double lonEast = latlngBounds.east();
|
|
double latSouth = latlngBounds.south();
|
|
double lonWest = latlngBounds.west();
|
|
|
|
std::vector<jdouble> buffer;
|
|
buffer.reserve(4);
|
|
|
|
// Order of the LatLngBounds: double latNorth, double lonEast, double latSouth, double lonWest
|
|
buffer.push_back(latNorth);
|
|
buffer.push_back(lonEast);
|
|
buffer.push_back(latSouth);
|
|
buffer.push_back(lonWest);
|
|
|
|
output.SetRegion<std::vector<jdouble>>(env, 0, buffer);
|
|
}
|
|
|
|
void NativeMapView::scheduleSnapshot(jni::JNIEnv&) {
|
|
mapRenderer.requestSnapshot([&](PremultipliedImage image) {
|
|
auto _env = android::AttachEnv();
|
|
// Convert image to bitmap
|
|
auto bitmap = Bitmap::CreateBitmap(*_env, std::move(image));
|
|
|
|
// invoke Mapview#OnSnapshotReady
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSnapshotReady = javaClass.GetMethod<void(jni::Object<Bitmap>)>(*_env, "onSnapshotReady");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onSnapshotReady, bitmap);
|
|
}
|
|
});
|
|
}
|
|
|
|
jni::Local<jni::Object<CameraPosition>> NativeMapView::getCameraPosition(jni::JNIEnv& env) {
|
|
return CameraPosition::New(env, map->getCameraOptions(std::nullopt), pixelRatio);
|
|
}
|
|
|
|
void NativeMapView::updateMarker(
|
|
jni::JNIEnv& env, jni::jlong markerId, jni::jdouble lat, jni::jdouble lon, const jni::String& jid) {
|
|
if (markerId == -1) {
|
|
return;
|
|
}
|
|
|
|
std::string iconId = jni::Make<std::string>(env, jid);
|
|
// Because Java only has int, not unsigned int, we need to bump the annotation id up to a long.
|
|
map->updateAnnotation(markerId, mln::SymbolAnnotation{mln::Point<double>(lon, lat), iconId});
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::jlong>> NativeMapView::addMarkers(jni::JNIEnv& env,
|
|
const jni::Array<jni::Object<Marker>>& jmarkers) {
|
|
jni::NullCheck(env, &jmarkers);
|
|
std::size_t len = jmarkers.Length(env);
|
|
|
|
std::vector<jni::jlong> ids;
|
|
ids.reserve(len);
|
|
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
auto marker = jmarkers.Get(env, i);
|
|
ids.push_back(map->addAnnotation(
|
|
mln::SymbolAnnotation{Marker::getPosition(env, marker), Marker::getIconId(env, marker)}));
|
|
}
|
|
|
|
auto result = jni::Array<jni::jlong>::New(env, len);
|
|
result.SetRegion<std::vector<jni::jlong>>(env, 0, ids);
|
|
|
|
return result;
|
|
}
|
|
|
|
void NativeMapView::onLowMemory(JNIEnv&) {
|
|
rendererFrontend->reduceMemoryUse();
|
|
}
|
|
|
|
using DebugOptions = mln::MapDebugOptions;
|
|
|
|
void NativeMapView::setDebug(JNIEnv&, jni::jboolean debug) {
|
|
DebugOptions debugOptions = debug ? DebugOptions::TileBorders | DebugOptions::ParseStatus | DebugOptions::Collision
|
|
: DebugOptions::NoDebug;
|
|
map->setDebug(debugOptions);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::getDebug(JNIEnv&) {
|
|
return map->getDebug() != DebugOptions::NoDebug;
|
|
}
|
|
|
|
void NativeMapView::setRetainedUnderlayEnabled(JNIEnv&, jni::jboolean enabled) {
|
|
map->setRetainedUnderlayEnabled(enabled);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setContinuousLayerRange(JNIEnv& env,
|
|
const jni::String& startLayerId,
|
|
const jni::String& endLayerId) {
|
|
return map->setContinuousLayerRange(jni::Make<std::string>(env, startLayerId),
|
|
jni::Make<std::string>(env, endLayerId));
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setRenderSegmentPlan(JNIEnv& env, const jni::String& planJson) {
|
|
auto plan = renderSegmentPlanFromJSONOrInvalid(jni::Make<std::string>(env, planJson));
|
|
return map->setRenderSegmentPlan(std::move(plan));
|
|
}
|
|
|
|
jni::Local<jni::String> NativeMapView::getRenderSegmentDiagnostics(JNIEnv& env) {
|
|
return jni::Make<jni::String>(env, renderSegmentDiagnosticsToJSON(map->getRenderSegmentDiagnostics()));
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::String>> NativeMapView::getRetainedUnderlayDiagnostics(JNIEnv& env) {
|
|
const auto snapshot = map->getRetainedUnderlayDiagnostics();
|
|
const std::array<std::string, 39> values = {
|
|
snapshot.enabled ? "true" : "false",
|
|
std::to_string(snapshot.build),
|
|
std::to_string(snapshot.reuse),
|
|
std::to_string(snapshot.fallback),
|
|
std::to_string(snapshot.invalidation),
|
|
snapshot.lastReason,
|
|
snapshot.rangeConfigured ? "true" : "false",
|
|
snapshot.rangeValid ? "true" : "false",
|
|
snapshot.startLayerId,
|
|
snapshot.endLayerId,
|
|
std::to_string(snapshot.startIndex),
|
|
std::to_string(snapshot.endIndex),
|
|
std::to_string(snapshot.continuousBegin),
|
|
std::to_string(snapshot.continuousEndExclusive),
|
|
snapshot.bottomPresent ? "true" : "false",
|
|
snapshot.bottomCacheState,
|
|
std::to_string(snapshot.bottomBuild),
|
|
std::to_string(snapshot.bottomReuse),
|
|
std::to_string(snapshot.bottomInvalidation),
|
|
snapshot.bottomLastInvalidationReason,
|
|
std::to_string(snapshot.bottomTextureCreate),
|
|
std::to_string(snapshot.bottomCompositeCreate),
|
|
std::to_string(snapshot.bottomResourceRelease),
|
|
snapshot.topPresent ? "true" : "false",
|
|
snapshot.topCacheState,
|
|
std::to_string(snapshot.topBuild),
|
|
std::to_string(snapshot.topReuse),
|
|
std::to_string(snapshot.topInvalidation),
|
|
snapshot.topLastInvalidationReason,
|
|
std::to_string(snapshot.topTextureCreate),
|
|
std::to_string(snapshot.topCompositeCreate),
|
|
std::to_string(snapshot.topResourceRelease),
|
|
snapshot.rangeFallback ? "true" : "false",
|
|
snapshot.fastPathEligible ? "true" : "false",
|
|
snapshot.fastPathActive ? "true" : "false",
|
|
snapshot.fastPathFallbackReason,
|
|
std::to_string(snapshot.fastPathHit),
|
|
std::to_string(snapshot.fastPathMiss),
|
|
std::to_string(snapshot.fullCpuFrame),
|
|
};
|
|
auto result = jni::Array<jni::String>::New(env, values.size());
|
|
for (std::size_t index = 0; index < values.size(); ++index) {
|
|
result.Set(env, index, jni::Make<jni::String>(env, values[index]));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::String>> NativeMapView::getActionJournalLogFiles(JNIEnv& env) {
|
|
const auto& actionJournal = map->getActionJournal();
|
|
if (!actionJournal) {
|
|
return jni::Local<jni::Array<jni::String>>();
|
|
}
|
|
|
|
const auto& files = actionJournal->getLogFiles();
|
|
auto jFiles = jni::Array<jni::String>::New(env, files.size());
|
|
|
|
uint32_t index = 0;
|
|
for (const auto& file : files) {
|
|
jFiles.Set(env, index++, jni::Make<jni::String>(env, file));
|
|
}
|
|
|
|
return jFiles;
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::String>> NativeMapView::getActionJournalLog(JNIEnv& env) {
|
|
const auto& actionJournal = map->getActionJournal();
|
|
if (!actionJournal) {
|
|
return jni::Local<jni::Array<jni::String>>();
|
|
}
|
|
|
|
const auto& events = actionJournal->getLog();
|
|
auto jEvents = jni::Array<jni::String>::New(env, events.size());
|
|
|
|
uint32_t index = 0;
|
|
for (const auto& event : events) {
|
|
jEvents.Set(env, index++, jni::Make<jni::String>(env, event));
|
|
}
|
|
|
|
return jEvents;
|
|
}
|
|
|
|
void NativeMapView::clearActionJournalLog(JNIEnv&) {
|
|
const auto& actionJournal = map->getActionJournal();
|
|
if (actionJournal) {
|
|
actionJournal->clearLog();
|
|
}
|
|
}
|
|
|
|
jni::jboolean NativeMapView::isFullyLoaded(JNIEnv&) {
|
|
return map->isFullyLoaded();
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getMetersPerPixelAtLatitude(JNIEnv&, jni::jdouble lat, jni::jdouble zoom) {
|
|
return mln::Projection::getMetersPerPixelAtLatitude(lat, zoom);
|
|
}
|
|
|
|
jni::Local<jni::Object<ProjectedMeters>> NativeMapView::projectedMetersForLatLng(JNIEnv& env,
|
|
jni::jdouble latitude,
|
|
jni::jdouble longitude) {
|
|
mln::ProjectedMeters projectedMeters = mln::Projection::projectedMetersForLatLng(mln::LatLng(latitude, longitude));
|
|
return ProjectedMeters::New(env, projectedMeters.northing(), projectedMeters.easting());
|
|
}
|
|
|
|
jni::Local<jni::Object<LatLng>> NativeMapView::latLngForProjectedMeters(JNIEnv& env,
|
|
jdouble northing,
|
|
jdouble easting) {
|
|
return LatLng::New(env, mln::Projection::latLngForProjectedMeters(mln::ProjectedMeters(northing, easting)));
|
|
}
|
|
|
|
jni::Local<jni::Object<PointF>> NativeMapView::pixelForLatLng(JNIEnv& env, jdouble latitude, jdouble longitude) {
|
|
mln::ScreenCoordinate pixel = map->pixelForLatLng(mln::LatLng(latitude, longitude));
|
|
return PointF::New(env, static_cast<float>(pixel.x), static_cast<float>(pixel.y));
|
|
}
|
|
|
|
void NativeMapView::pixelsForLatLngs(JNIEnv& env,
|
|
const jni::Array<jdouble>& input,
|
|
jni::Array<jdouble>& output,
|
|
jfloat pixelRatio_) {
|
|
jni::NullCheck(env, &input);
|
|
std::size_t len = input.Length(env);
|
|
|
|
std::vector<mln::LatLng> latLngs;
|
|
latLngs.reserve(len);
|
|
|
|
for (std::size_t i = 0; i < len; i += 2) {
|
|
auto latLng = mln::LatLng(input.Get(env, i), input.Get(env, i + 1));
|
|
latLngs.push_back(latLng);
|
|
}
|
|
|
|
std::vector<jdouble> buffer;
|
|
buffer.reserve(len);
|
|
std::vector<ScreenCoordinate> coordinates = map->pixelsForLatLngs(latLngs);
|
|
for (std::size_t i = 0; i < len / 2; i++) {
|
|
buffer.push_back(coordinates[i].x * pixelRatio_);
|
|
buffer.push_back(coordinates[i].y * pixelRatio_);
|
|
}
|
|
|
|
output.SetRegion<std::vector<jdouble>>(env, 0, buffer);
|
|
}
|
|
|
|
jni::Local<jni::Object<LatLng>> NativeMapView::latLngForPixel(JNIEnv& env, jfloat x, jfloat y) {
|
|
return LatLng::New(env, map->latLngForPixel(mln::ScreenCoordinate(x, y)));
|
|
}
|
|
|
|
void NativeMapView::latLngsForPixels(JNIEnv& env,
|
|
const jni::Array<jdouble>& input,
|
|
jni::Array<jdouble>& output,
|
|
jfloat pixelRatio_) {
|
|
jni::NullCheck(env, &input);
|
|
std::size_t len = input.Length(env);
|
|
|
|
std::vector<mln::ScreenCoordinate> coordinates;
|
|
coordinates.reserve(len);
|
|
|
|
for (std::size_t i = 0; i < len; i += 2) {
|
|
auto coordinate = mln::ScreenCoordinate(input.Get(env, i) / pixelRatio_, input.Get(env, i + 1) / pixelRatio_);
|
|
coordinates.push_back(coordinate);
|
|
}
|
|
|
|
std::vector<jdouble> buffer;
|
|
buffer.reserve(len);
|
|
std::vector<mln::LatLng> latLngs = map->latLngsForPixels(coordinates);
|
|
for (std::size_t i = 0; i < len / 2; i++) {
|
|
buffer.push_back(latLngs[i].latitude());
|
|
buffer.push_back(latLngs[i].longitude());
|
|
}
|
|
|
|
output.SetRegion<std::vector<jdouble>>(env, 0, buffer);
|
|
}
|
|
|
|
jni::Local<jni::Array<jlong>> NativeMapView::addPolylines(JNIEnv& env,
|
|
const jni::Array<jni::Object<Polyline>>& polylines) {
|
|
NullCheck(env, &polylines);
|
|
std::size_t len = polylines.Length(env);
|
|
|
|
std::vector<jni::jlong> ids;
|
|
ids.reserve(len);
|
|
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
mln::LineAnnotation annotation = Polyline::toAnnotation(env, polylines.Get(env, i));
|
|
ids.push_back(map->addAnnotation(annotation));
|
|
}
|
|
|
|
auto result = jni::Array<jni::jlong>::New(env, len);
|
|
result.SetRegion<std::vector<jni::jlong>>(env, 0, ids);
|
|
|
|
return result;
|
|
}
|
|
|
|
jni::Local<jni::Array<jlong>> NativeMapView::addPolygons(JNIEnv& env,
|
|
const jni::Array<jni::Object<Polygon>>& polygons) {
|
|
NullCheck(env, &polygons);
|
|
std::size_t len = polygons.Length(env);
|
|
|
|
std::vector<jni::jlong> ids;
|
|
ids.reserve(len);
|
|
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
mln::FillAnnotation annotation = Polygon::toAnnotation(env, polygons.Get(env, i));
|
|
ids.push_back(map->addAnnotation(annotation));
|
|
}
|
|
|
|
auto result = jni::Array<jni::jlong>::New(env, len);
|
|
result.SetRegion<std::vector<jni::jlong>>(env, 0, ids);
|
|
|
|
return result;
|
|
}
|
|
|
|
void NativeMapView::updatePolyline(JNIEnv& env, jlong polylineId, const jni::Object<Polyline>& polyline) {
|
|
mln::LineAnnotation annotation = Polyline::toAnnotation(env, polyline);
|
|
map->updateAnnotation(polylineId, annotation);
|
|
}
|
|
|
|
void NativeMapView::updatePolygon(JNIEnv& env, jlong polygonId, const jni::Object<Polygon>& polygon) {
|
|
mln::FillAnnotation annotation = Polygon::toAnnotation(env, polygon);
|
|
map->updateAnnotation(polygonId, annotation);
|
|
}
|
|
|
|
void NativeMapView::removeAnnotations(JNIEnv& env, const jni::Array<jlong>& ids) {
|
|
NullCheck(env, &ids);
|
|
std::size_t len = ids.Length(env);
|
|
auto elements = jni::GetArrayElements(env, *ids);
|
|
jlong* jids = std::get<0>(elements).get();
|
|
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
if (jids[i] == -1L) {
|
|
continue;
|
|
}
|
|
map->removeAnnotation(jids[i]);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::addAnnotationIcon(
|
|
JNIEnv& env, const jni::String& symbol, jint w, jint h, jfloat scale, const jni::Array<jbyte>& jpixels) {
|
|
const std::string symbolName = jni::Make<std::string>(env, symbol);
|
|
|
|
NullCheck(env, &jpixels);
|
|
std::size_t size = jpixels.Length(env);
|
|
|
|
mln::PremultipliedImage premultipliedImage({static_cast<uint32_t>(w), static_cast<uint32_t>(h)});
|
|
if (premultipliedImage.bytes() != uint32_t(size)) {
|
|
throw mln::util::StyleImageException("Annotation icon image pixel count mismatch");
|
|
}
|
|
|
|
jni::GetArrayRegion(env, *jpixels, 0, size, reinterpret_cast<jbyte*>(premultipliedImage.data.get()));
|
|
map->addAnnotationImage(
|
|
std::make_unique<mln::style::Image>(symbolName, std::move(premultipliedImage), static_cast<float>(scale)));
|
|
}
|
|
|
|
void NativeMapView::removeAnnotationIcon(JNIEnv& env, const jni::String& symbol) {
|
|
const std::string symbolName = jni::Make<std::string>(env, symbol);
|
|
map->removeAnnotationImage(symbolName);
|
|
}
|
|
|
|
jdouble NativeMapView::getTopOffsetPixelsForAnnotationSymbol(JNIEnv& env, const jni::String& symbolName) {
|
|
return map->getTopOffsetPixelsForAnnotationImage(jni::Make<std::string>(env, symbolName));
|
|
}
|
|
|
|
jni::Local<jni::Object<TransitionOptions>> NativeMapView::getTransitionOptions(JNIEnv& env) {
|
|
const auto transitionOptions = map->getStyle().getTransitionOptions();
|
|
const auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
transitionOptions.duration.value_or(mln::Duration::zero()))
|
|
.count();
|
|
const auto delay = std::chrono::duration_cast<std::chrono::milliseconds>(
|
|
transitionOptions.delay.value_or(mln::Duration::zero()))
|
|
.count();
|
|
const auto enablePlacementTransitions = (jboolean)transitionOptions.enablePlacementTransitions;
|
|
return TransitionOptions::fromTransitionOptions(env, duration, delay, enablePlacementTransitions);
|
|
}
|
|
|
|
void NativeMapView::setTransitionOptions(JNIEnv& env, const jni::Object<TransitionOptions>& options) {
|
|
const mln::style::TransitionOptions transitionOptions(
|
|
Duration(mln::Milliseconds(TransitionOptions::getDuration(env, options))),
|
|
Duration(mln::Milliseconds(TransitionOptions::getDelay(env, options))),
|
|
TransitionOptions::isEnablePlacementTransitions(env, options));
|
|
map->getStyle().setTransitionOptions(transitionOptions);
|
|
}
|
|
|
|
jni::Local<jni::Array<jlong>> NativeMapView::queryPointAnnotations(JNIEnv& env, const jni::Object<RectF>& rect) {
|
|
using namespace mln::style;
|
|
using namespace mln::style::conversion;
|
|
|
|
// Convert input
|
|
mln::ScreenBox box = {
|
|
{RectF::getLeft(env, rect), RectF::getTop(env, rect)},
|
|
{RectF::getRight(env, rect), RectF::getBottom(env, rect)},
|
|
};
|
|
|
|
// Assume only points for now
|
|
mln::AnnotationIDs ids = rendererFrontend->queryPointAnnotations(box, annotationRequestTimeout);
|
|
|
|
// Convert result
|
|
std::vector<jlong> longIds(ids.begin(), ids.end());
|
|
auto result = jni::Array<jni::jlong>::New(env, ids.size());
|
|
result.SetRegion<std::vector<jni::jlong>>(env, 0, longIds);
|
|
|
|
return result;
|
|
}
|
|
|
|
jni::Local<jni::Array<jlong>> NativeMapView::queryShapeAnnotations(JNIEnv& env, const jni::Object<RectF>& rect) {
|
|
using namespace mln::style;
|
|
using namespace mln::style::conversion;
|
|
|
|
// Convert input
|
|
mln::ScreenBox box = {
|
|
{RectF::getLeft(env, rect), RectF::getTop(env, rect)},
|
|
{RectF::getRight(env, rect), RectF::getBottom(env, rect)},
|
|
};
|
|
|
|
mln::AnnotationIDs ids = rendererFrontend->queryShapeAnnotations(box, annotationRequestTimeout);
|
|
|
|
// Convert result
|
|
std::vector<jlong> longIds(ids.begin(), ids.end());
|
|
auto result = jni::Array<jni::jlong>::New(env, ids.size());
|
|
result.SetRegion<std::vector<jni::jlong>>(env, 0, longIds);
|
|
|
|
return result;
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::Object<geojson::Feature>>> NativeMapView::queryRenderedFeaturesForPoint(
|
|
JNIEnv& env,
|
|
jni::jfloat x,
|
|
jni::jfloat y,
|
|
const jni::Array<jni::String>& layerIds,
|
|
const jni::Array<jni::Object<>>& jfilter) {
|
|
using namespace mln::android::conversion;
|
|
using namespace mln::android::geojson;
|
|
|
|
std::optional<std::vector<std::string>> layers;
|
|
if (layerIds && layerIds.Length(env) > 0) {
|
|
layers = android::conversion::toVector(env, layerIds);
|
|
}
|
|
mapbox::geometry::point<double> point = {x, y};
|
|
|
|
return Feature::convert(env, rendererFrontend->queryRenderedFeatures(point, {layers, toFilter(env, jfilter)}));
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::Object<geojson::Feature>>> NativeMapView::queryRenderedFeaturesForBox(
|
|
JNIEnv& env,
|
|
jni::jfloat left,
|
|
jni::jfloat top,
|
|
jni::jfloat right,
|
|
jni::jfloat bottom,
|
|
const jni::Array<jni::String>& layerIds,
|
|
const jni::Array<jni::Object<>>& jfilter) {
|
|
using namespace mln::android::conversion;
|
|
using namespace mln::android::geojson;
|
|
|
|
std::optional<std::vector<std::string>> layers;
|
|
if (layerIds && layerIds.Length(env) > 0) {
|
|
layers = toVector(env, layerIds);
|
|
}
|
|
mapbox::geometry::box<double> box = {mapbox::geometry::point<double>{left, top},
|
|
mapbox::geometry::point<double>{right, bottom}};
|
|
|
|
return Feature::convert(env, rendererFrontend->queryRenderedFeatures(box, {layers, toFilter(env, jfilter)}));
|
|
}
|
|
|
|
void NativeMapView::setFeatureState(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::String& sourceLayerId,
|
|
const jni::String& featureId,
|
|
const jni::Object<gson::JsonObject>& state) {
|
|
if (!sourceId || !featureId || !state) {
|
|
return;
|
|
}
|
|
|
|
rendererFrontend->setFeatureState(
|
|
jni::Make<std::string>(env, sourceId),
|
|
sourceLayerId ? std::optional<std::string>(jni::Make<std::string>(env, sourceLayerId)) : std::nullopt,
|
|
jni::Make<std::string>(env, featureId),
|
|
gson::JsonObject::convert(env, state));
|
|
map->triggerRepaint();
|
|
}
|
|
|
|
jni::Local<jni::Object<gson::JsonObject>> NativeMapView::getFeatureState(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::String& sourceLayerId,
|
|
const jni::String& featureId) {
|
|
if (!sourceId || !featureId) {
|
|
return jni::Local<jni::Object<gson::JsonObject>>();
|
|
}
|
|
|
|
const auto state = rendererFrontend->getFeatureState(
|
|
jni::Make<std::string>(env, sourceId),
|
|
sourceLayerId ? std::optional<std::string>(jni::Make<std::string>(env, sourceLayerId)) : std::nullopt,
|
|
jni::Make<std::string>(env, featureId));
|
|
if (state.empty()) {
|
|
return jni::Local<jni::Object<gson::JsonObject>>();
|
|
}
|
|
|
|
return gson::JsonObject::New(env, state);
|
|
}
|
|
|
|
void NativeMapView::removeFeatureState(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::String& sourceLayerId,
|
|
const jni::String& featureId,
|
|
const jni::String& stateKey) {
|
|
if (!sourceId) {
|
|
return;
|
|
}
|
|
|
|
rendererFrontend->removeFeatureState(
|
|
jni::Make<std::string>(env, sourceId),
|
|
sourceLayerId ? std::optional<std::string>(jni::Make<std::string>(env, sourceLayerId)) : std::nullopt,
|
|
featureId ? std::optional<std::string>(jni::Make<std::string>(env, featureId)) : std::nullopt,
|
|
stateKey ? std::optional<std::string>(jni::Make<std::string>(env, stateKey)) : std::nullopt);
|
|
map->triggerRepaint();
|
|
}
|
|
|
|
jni::Local<jni::Object<Light>> NativeMapView::getLight(JNIEnv& env) {
|
|
mln::style::Light* light = map->getStyle().getLight();
|
|
if (light) {
|
|
return Light::createJavaLightPeer(env, *map, *light);
|
|
} else {
|
|
return jni::Local<jni::Object<Light>>();
|
|
}
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::Object<Layer>>> NativeMapView::getLayers(JNIEnv& env) {
|
|
// Get the core layers
|
|
std::vector<style::Layer*> layers = map->getStyle().getLayers();
|
|
|
|
// Convert
|
|
auto jLayers = jni::Array<jni::Object<Layer>>::New(env, layers.size());
|
|
int index = 0;
|
|
for (auto layer : layers) {
|
|
jLayers.Set(env, index, LayerManagerAndroid::get()->createJavaLayerPeer(env, *layer));
|
|
index++;
|
|
}
|
|
|
|
return jLayers;
|
|
}
|
|
|
|
jni::Local<jni::Object<Layer>> NativeMapView::getLayer(JNIEnv& env, const jni::String& layerId) {
|
|
// Find the layer
|
|
mln::style::Layer* coreLayer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId));
|
|
if (!coreLayer) {
|
|
mln::Log::Debug(mln::Event::JNI, "No layer found");
|
|
return jni::Local<jni::Object<Layer>>();
|
|
}
|
|
|
|
// Create and return the layer's native peer
|
|
return LayerManagerAndroid::get()->createJavaLayerPeer(env, *coreLayer);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::hasStyleLayer(JNIEnv& env, const jni::String& layerId) {
|
|
return map->getStyle().getLayer(jni::Make<std::string>(env, layerId)) != nullptr;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setStyleLayerFloatProperty(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::String& propertyName,
|
|
const jni::jfloat value) {
|
|
auto* layer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId));
|
|
if (!layer) return false;
|
|
const auto property = jni::Make<std::string>(env, propertyName);
|
|
if (property == "weather-color-field-value-source") {
|
|
auto* weatherColorLayer = dynamic_cast<mln::style::WeatherColorLayer*>(layer);
|
|
if (!weatherColorLayer || (value != 0.0f && value != 1.0f)) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleField: event=FIELD_VALUE_SOURCE_SET result=REJECTED");
|
|
return false;
|
|
}
|
|
weatherColorLayer->setFieldValueSource(value == 0.0f ? mln::style::FieldValueSource::Scalar
|
|
: mln::style::FieldValueSource::VectorMagnitude);
|
|
return true;
|
|
}
|
|
if (property != "raster-particle-time-mix" && property != "raster-particle-particle-count" &&
|
|
property != "raster-particle-line-width" && property != "raster-particle-particle-length" &&
|
|
property != "raster-particle-particle-opacity" && property != "raster-particle-trail-opacity" &&
|
|
property != "raster-particle-trail-fade" && property != "raster-particle-speed-color-enabled" &&
|
|
property != "raster-particle-advection-scale") {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=UNSUPPORTED_PROPERTY");
|
|
return false;
|
|
}
|
|
auto* particleLayer = dynamic_cast<mln::style::RasterParticleLayer*>(layer);
|
|
if (!particleLayer) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=UNSUPPORTED_LAYER");
|
|
return false;
|
|
}
|
|
if (!std::isfinite(value)) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=INVALID_FLOAT");
|
|
return false;
|
|
}
|
|
if (property == "raster-particle-time-mix") {
|
|
particleLayer->setTimeMix(value);
|
|
} else if (property == "raster-particle-particle-count") {
|
|
if (value < 0.0f || std::floor(value) != value) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=INVALID_PARTICLE_COUNT");
|
|
return false;
|
|
}
|
|
const auto requestedCount = static_cast<uint32_t>(value);
|
|
particleLayer->setParticleCount(requestedCount);
|
|
if (particleLayer->getParticleCount() != requestedCount) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=INVALID_PARTICLE_COUNT");
|
|
return false;
|
|
}
|
|
} else if (property == "raster-particle-line-width") {
|
|
particleLayer->setLineWidth(value);
|
|
} else if (property == "raster-particle-particle-length") {
|
|
particleLayer->setParticleLength(value);
|
|
} else if (property == "raster-particle-particle-opacity") {
|
|
particleLayer->setParticleOpacity(value);
|
|
} else if (property == "raster-particle-trail-opacity") {
|
|
particleLayer->setTrailOpacity(value);
|
|
} else if (property == "raster-particle-trail-fade") {
|
|
particleLayer->setTrailFade(value);
|
|
} else if (property == "raster-particle-advection-scale") {
|
|
if (value <= 0.0f) return false;
|
|
particleLayer->setAdvectionScale(value);
|
|
} else {
|
|
if (value != 0.0f && value != 1.0f) {
|
|
mln::Log::Error(mln::Event::JNI, "ParticleGPU: event=STYLE_PROPERTY_SET result=INVALID_COLOR_MODE");
|
|
return false;
|
|
}
|
|
particleLayer->setSpeedColorEnabled(value > 0.5f);
|
|
}
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleGPU: event=STYLE_PROPERTY_SET layerId=" + jni::Make<std::string>(env, layerId) +
|
|
" property=" + property + " value=" + std::to_string(value) + " result=SUCCESS");
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setWeatherColorOpacity(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::jfloat opacity) {
|
|
if (!std::isfinite(opacity)) return false;
|
|
auto* layer = dynamic_cast<mln::style::WeatherColorLayer*>(
|
|
map->getStyle().getLayer(jni::Make<std::string>(env, layerId)));
|
|
if (!layer) return false;
|
|
layer->setOpacity(opacity);
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setStyleLayerVisibility(JNIEnv& env, const jni::String& layerId, const jni::jboolean visible) {
|
|
auto* layer = map->getStyle().getLayer(jni::Make<std::string>(env, layerId));
|
|
if (!layer) return false;
|
|
layer->setVisibility(visible ? mln::style::VisibilityType::Visible : mln::style::VisibilityType::None);
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setRasterParticleColors(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::Array<jni::jfloat>& particle,
|
|
const jni::Array<jni::jfloat>& trail) {
|
|
auto* layer = dynamic_cast<mln::style::RasterParticleLayer*>(map->getStyle().getLayer(jni::Make<std::string>(env, layerId)));
|
|
if (!layer || particle.Length(env) != 4 || trail.Length(env) != 4) return false;
|
|
std::array<float, 4> particleColor, trailColor;
|
|
particle.GetRegion(env, 0, particleColor);
|
|
trail.GetRegion(env, 0, trailColor);
|
|
if (!mln::style::validWeatherRGBA(particleColor) || !mln::style::validWeatherRGBA(trailColor)) return false;
|
|
layer->setParticleColor(particleColor);
|
|
layer->setTrailColor(trailColor);
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setRasterParticleColor(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::Array<jni::jfloat>& particle) {
|
|
auto* layer = dynamic_cast<mln::style::RasterParticleLayer*>(map->getStyle().getLayer(jni::Make<std::string>(env, layerId)));
|
|
if (!layer || particle.Length(env) != 4) return false;
|
|
std::array<float, 4> color;
|
|
particle.GetRegion(env, 0, color);
|
|
if (!mln::style::validWeatherRGBA(color)) return false;
|
|
layer->setParticleColor(color);
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setRasterParticleTrailColor(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::Array<jni::jfloat>& trail) {
|
|
auto* layer = dynamic_cast<mln::style::RasterParticleLayer*>(map->getStyle().getLayer(jni::Make<std::string>(env, layerId)));
|
|
if (!layer || trail.Length(env) != 4) return false;
|
|
std::array<float, 4> color;
|
|
trail.GetRegion(env, 0, color);
|
|
if (!mln::style::validWeatherRGBA(color)) return false;
|
|
layer->setTrailColor(color);
|
|
return true;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setRasterArraySourceTargetFps(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::jint targetFps) {
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env, sourceId)));
|
|
return source && source->setRuntimeTargetFps(static_cast<uint8_t>(targetFps));
|
|
}
|
|
|
|
jni::jint NativeMapView::getRasterArraySourceTargetFps(JNIEnv& env, const jni::String& sourceId) {
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env, sourceId)));
|
|
return source ? source->runtimeTargetFps() : 0;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::setWeatherColorStops(JNIEnv& env,
|
|
const jni::String& layerId,
|
|
const jni::Array<jni::jfloat>& speeds,
|
|
const jni::Array<jni::jfloat>& rgba) {
|
|
auto* layer = dynamic_cast<mln::style::WeatherColorLayer*>(map->getStyle().getLayer(jni::Make<std::string>(env, layerId)));
|
|
const auto count = speeds.Length(env);
|
|
if (!layer || count < 2 || count > static_cast<jni::jsize>(mln::style::MaxWeatherColorStops) || rgba.Length(env) != count * 4) return false;
|
|
std::vector<float> speedValues(count), rgbaValues(static_cast<size_t>(count) * 4);
|
|
speeds.GetRegion(env, 0, speedValues);
|
|
rgba.GetRegion(env, 0, rgbaValues);
|
|
std::vector<mln::style::WeatherColorStop> stops;
|
|
stops.reserve(count);
|
|
for (jni::jsize i = 0; i < count; ++i) {
|
|
std::array<float, 4> color;
|
|
std::copy_n(rgbaValues.data() + i * 4, 4, color.data());
|
|
stops.push_back({speedValues[i], color});
|
|
}
|
|
return layer->setColorStops(std::move(stops));
|
|
}
|
|
|
|
jni::jint NativeMapView::getWeatherColorStopCapacity(JNIEnv&) {
|
|
return static_cast<jni::jint>(mln::style::MaxWeatherColorStops);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::advanceRasterArraySourceFieldWindow(JNIEnv& env, const jni::String& sourceId) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = map->getStyle().getSource(id);
|
|
auto* rasterArraySource = dynamic_cast<mln::style::RasterArraySource*>(source);
|
|
if (!rasterArraySource) {
|
|
mln::Log::Error(mln::Event::JNI,
|
|
"ParticleField: event=FIELD_WINDOW_UPDATE_REJECTED reason=SOURCE_NOT_FOUND sourceId=" + id);
|
|
return false;
|
|
}
|
|
const auto result = rasterArraySource->advanceRuntimeFieldWindow();
|
|
const bool success = result == mln::style::RasterArrayRuntimeFieldUpdateResult::Applied ||
|
|
result == mln::style::RasterArrayRuntimeFieldUpdateResult::Noop;
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleField: event=FIELD_WINDOW_UPDATE_BRIDGE sourceId=" + id +
|
|
" result=" + (success ? "SUCCESS" : "REJECTED"));
|
|
return success;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::repeatRasterArraySourceFieldWindow(JNIEnv& env, const jni::String& sourceId) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = map->getStyle().getSource(id);
|
|
auto* rasterArraySource = dynamic_cast<mln::style::RasterArraySource*>(source);
|
|
if (!rasterArraySource) {
|
|
mln::Log::Error(mln::Event::JNI,
|
|
"ParticleField: event=FIELD_WINDOW_UPDATE_REJECTED reason=SOURCE_NOT_FOUND sourceId=" + id);
|
|
return false;
|
|
}
|
|
const auto result = rasterArraySource->repeatRuntimeFieldWindow();
|
|
const bool success = result == mln::style::RasterArrayRuntimeFieldUpdateResult::Noop;
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleField: event=FIELD_WINDOW_UPDATE_DUPLICATE_BRIDGE sourceId=" + id +
|
|
" result=" + (success ? "NOOP" : "REJECTED"));
|
|
return success;
|
|
}
|
|
|
|
jni::jint NativeMapView::submitRasterArraySourceHostField(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::String& metadata,
|
|
const jni::Array<jbyte>& payload) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if (!source) {
|
|
mln::Log::Error(mln::Event::JNI,
|
|
"ParticleField: event=HOST_FIELD_SUBMIT_REJECTED reason=SOURCE_NOT_FOUND sourceId=" + id);
|
|
return static_cast<jni::jint>(mln::style::RasterArrayHostFieldSubmitResult::RejectedSourceMode);
|
|
}
|
|
NullCheck(env, &payload);
|
|
std::vector<char> bytes(payload.Length(env));
|
|
if (!bytes.empty()) {
|
|
jni::GetArrayRegion(env, *payload, 0, bytes.size(), reinterpret_cast<jbyte*>(bytes.data()));
|
|
}
|
|
const auto result = source->submitHostField(jni::Make<std::string>(env, metadata), bytes);
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleField: event=HOST_FIELD_SUBMIT_BRIDGE sourceId=" + id +
|
|
" result=" + mln::style::rasterArrayHostFieldSubmitResultName(result));
|
|
return static_cast<jni::jint>(result);
|
|
}
|
|
|
|
jni::jint NativeMapView::submitRasterArraySourceHostFieldV2(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::String& metadata,
|
|
const jni::Array<jbyte>& packedPlanes,
|
|
const jni::jboolean scalarPresent,
|
|
const jni::jint scalarLength,
|
|
const jni::jboolean vectorPresent,
|
|
const jni::jint vectorLength,
|
|
const jni::jint validityLength) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if (!source) return static_cast<jni::jint>(mln::style::RasterArrayHostFieldSubmitResult::RejectedSourceMode);
|
|
NullCheck(env, &packedPlanes);
|
|
if (scalarLength < 0 || vectorLength < 0 || validityLength <= 0 || (!scalarPresent && scalarLength != 0) ||
|
|
(!vectorPresent && vectorLength != 0) || (scalarPresent && scalarLength == 0) ||
|
|
(vectorPresent && vectorLength == 0) || (!scalarPresent && !vectorPresent)) {
|
|
return static_cast<jni::jint>(mln::style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
|
|
}
|
|
const uint64_t total = static_cast<uint64_t>(scalarLength) + static_cast<uint64_t>(vectorLength) +
|
|
static_cast<uint64_t>(validityLength);
|
|
if (total != packedPlanes.Length(env)) {
|
|
return static_cast<jni::jint>(mln::style::RasterArrayHostFieldSubmitResult::RejectedAdapter);
|
|
}
|
|
std::vector<char> packed(total);
|
|
if (!packed.empty()) jni::GetArrayRegion(env, *packedPlanes, 0, packed.size(), reinterpret_cast<jbyte*>(packed.data()));
|
|
size_t offset = 0;
|
|
const auto take = [&packed, &offset](const size_t length) {
|
|
std::vector<char> result(packed.begin() + static_cast<std::ptrdiff_t>(offset),
|
|
packed.begin() + static_cast<std::ptrdiff_t>(offset + length));
|
|
offset += length;
|
|
return result;
|
|
};
|
|
mln::particle::ParticleFieldPayloadBundle payloads{
|
|
.scalar = scalarPresent ? std::optional<std::vector<char>>(take(static_cast<size_t>(scalarLength))) : std::nullopt,
|
|
.vector = vectorPresent ? std::optional<std::vector<char>>(take(static_cast<size_t>(vectorLength))) : std::nullopt,
|
|
.validity = take(static_cast<size_t>(validityLength)),
|
|
};
|
|
const auto result = source->submitHostField(jni::Make<std::string>(env, metadata), payloads);
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleField: event=HOST_FIELD_V2_SUBMIT_BRIDGE sourceId=" + id + " scalar=" +
|
|
(scalarPresent ? "present" : "absent") + " vector=" + (vectorPresent ? "present" : "absent") +
|
|
" result=" + mln::style::rasterArrayHostFieldSubmitResultName(result));
|
|
return static_cast<jni::jint>(result);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::clearRasterArraySourceHostFields(JNIEnv& env, const jni::String& sourceId) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if (!source) return false;
|
|
const bool cleared = source->clearHostFields();
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"WeatherRuntime: event=CLEAR_FIELDS sourceId=" + id + " result=" +
|
|
(cleared ? "SUCCESS" : "REJECTED"));
|
|
return cleared;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::resetRasterArraySourceWeatherRuntime(JNIEnv& env, const jni::String& sourceId) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if (!source) return false;
|
|
const bool reset = source->resetWeatherRuntimeState();
|
|
mln::Log::Info(mln::Event::JNI, "WeatherRuntime: event=TERMINAL_RESET sourceId=" + id +
|
|
" result=" + (reset ? "SUCCESS" : "REJECTED"));
|
|
return reset;
|
|
}
|
|
|
|
jni::jboolean NativeMapView::seekRasterArraySourceTimelineToTimestamp(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::jlong timestamp) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
return source && timestamp >= 0 && source->seekRuntimeTimelineToTimestamp(static_cast<uint64_t>(timestamp));
|
|
}
|
|
|
|
jni::jboolean NativeMapView::controlRasterArraySourceTimeline(JNIEnv& env,
|
|
const jni::String& sourceId,
|
|
const jni::jint command,
|
|
const jni::jfloat value) {
|
|
const auto id = jni::Make<std::string>(env, sourceId);
|
|
auto* source = dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if (!source) return false;
|
|
const bool success = source->controlRuntimeTimeline(command, value);
|
|
mln::Log::Info(mln::Event::JNI,
|
|
"ParticleTimeline: event=COMMAND sourceId=" + id + " command=" + std::to_string(command) +
|
|
" result=" + (success ? "SUCCESS" : "REJECTED"));
|
|
return success;
|
|
}
|
|
|
|
jni::jint NativeMapView::submitRasterArraySourceEncodedField(JNIEnv& env, const jni::String& sourceId, const jni::String& fieldKey, const jni::String& fieldId, jni::jlong generation, jni::jlong dataVersion, const jni::String& contractJson, const jni::Array<jbyte>& qbin) {
|
|
const auto id=jni::Make<std::string>(env,sourceId); auto* source=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(id));
|
|
if(!source) return 0; // bridge-layer SOURCE_MISSING sentinel, never a Core result
|
|
if(generation<=0 || dataVersion<0 || !qbin || !mln::particle::ingress::isValidEncodedFieldTransportSize(qbin.Length(env))) return static_cast<jni::jint>(mln::style::RasterArrayEncodedFieldSubmitResult::RejectedDecode);
|
|
const auto contract=mln::particle::parseResolvedFieldProfileContractJson(jni::Make<std::string>(env,contractJson));
|
|
if(!contract) return static_cast<jni::jint>(mln::style::RasterArrayEncodedFieldSubmitResult::RejectedContract);
|
|
std::vector<uint8_t> bytes(qbin.Length(env)); if(!bytes.empty()) jni::GetArrayRegion(env,*qbin,0,bytes.size(),reinterpret_cast<jbyte*>(bytes.data()));
|
|
const auto result=source->submitEncodedField(jni::Make<std::string>(env,fieldKey), {jni::Make<std::string>(env,fieldId),static_cast<uint64_t>(generation),static_cast<uint64_t>(dataVersion)}, *contract, bytes);
|
|
return static_cast<jni::jint>(result);
|
|
}
|
|
jni::jint NativeMapView::clearRasterArraySourceEncodedField(JNIEnv& env,const jni::String& sourceId,const jni::String& fieldKey) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; return s->clearEncodedField(jni::Make<std::string>(env,fieldKey)) ? 1 : -1; }
|
|
jni::jint NativeMapView::clearAllRasterArraySourceEncodedFields(JNIEnv& env,const jni::String& sourceId) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; s->clearAllEncodedFields(); return 1; }
|
|
jni::jint NativeMapView::setRasterArraySourceEncodedColorBinding(JNIEnv& env, const jni::String& sourceId, const jni::String& fieldKey, jni::jint semanticID, jni::jint groupID, jni::jint valueClass, jni::jint unitID) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; if(semanticID<=0||semanticID>UINT16_MAX||groupID<0||groupID>UINT16_MAX||unitID<0||unitID>UINT16_MAX||(valueClass<0||valueClass>2))return -1; s->setColorBinding({jni::Make<std::string>(env,fieldKey),{static_cast<uint16_t>(semanticID),static_cast<uint16_t>(groupID)},static_cast<mln::particle::FieldValueClass>(valueClass),static_cast<uint16_t>(unitID)}); return 1; }
|
|
jni::jint NativeMapView::setRasterArraySourceEncodedParticleBinding(JNIEnv& env, const jni::String& sourceId, const jni::String& fieldKey, jni::jint mode, jni::jint uSemanticID, jni::jint uGroupID, jni::jint vSemanticID, jni::jint vGroupID, jni::jint directionSemanticID, jni::jint directionGroupID, jni::jint unitID) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; const auto valid=[](jni::jint semantic,jni::jint group){return semantic>0&&semantic<=UINT16_MAX&&group>=0&&group<=UINT16_MAX;}; const auto inactive=[](jni::jint semantic,jni::jint group){return semantic==0&&group==0;}; if(unitID<0||unitID>UINT16_MAX)return -1; mln::particle::ParticleBinding binding; binding.fieldKey=jni::Make<std::string>(env,fieldKey); binding.expectedUnitID=static_cast<uint16_t>(unitID); if(mode==0&&valid(uSemanticID,uGroupID)&&valid(vSemanticID,vGroupID)&&inactive(directionSemanticID,directionGroupID)){binding.mode=mln::particle::ParticleBindingMode::VectorComponents;binding.uPlane={static_cast<uint16_t>(uSemanticID),static_cast<uint16_t>(uGroupID)};binding.vPlane={static_cast<uint16_t>(vSemanticID),static_cast<uint16_t>(vGroupID)};} else if(mode==1&&inactive(uSemanticID,uGroupID)&&inactive(vSemanticID,vGroupID)&&valid(directionSemanticID,directionGroupID)){binding.mode=mln::particle::ParticleBindingMode::DirectionAngle;binding.directionPlane={static_cast<uint16_t>(directionSemanticID),static_cast<uint16_t>(directionGroupID)};} else return -1; s->setParticleBinding(std::move(binding)); return 1; }
|
|
jni::jint NativeMapView::setRasterArraySourceEncodedForecastTime(JNIEnv& env,const jni::String& sourceId,jni::jlong timestamp) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; return timestamp>=0&&s->setRequestedForecastTime(static_cast<uint64_t>(timestamp))?1:-1; }
|
|
jni::jint NativeMapView::setRasterArraySourceEncodedForecastPlaybackState(JNIEnv& env,const jni::String& sourceId,jni::jboolean playing) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; s->setForecastPlaybackState(playing ? mln::particle::ForecastPlaybackState::Playing : mln::particle::ForecastPlaybackState::Paused); return 1; }
|
|
jni::jint NativeMapView::setRasterArraySourceEncodedForecastPlaybackSpeed(JNIEnv& env,const jni::String& sourceId,jni::jfloat speed) { auto* s=dynamic_cast<mln::style::RasterArraySource*>(map->getStyle().getSource(jni::Make<std::string>(env,sourceId))); if(!s)return 0; return s->setForecastPlaybackSpeed(speed)?1:-1; }
|
|
|
|
void NativeMapView::addLayer(JNIEnv& env, jlong nativeLayerPtr, const jni::String& before) {
|
|
assert(nativeLayerPtr != 0);
|
|
|
|
Layer* layer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
try {
|
|
layer->addToStyle(
|
|
map->getStyle(),
|
|
before ? std::optional<std::string>(jni::Make<std::string>(env, before)) : std::optional<std::string>());
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
void NativeMapView::addLayerAbove(JNIEnv& env, jlong nativeLayerPtr, const jni::String& above) {
|
|
assert(nativeLayerPtr != 0);
|
|
|
|
Layer* layer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
|
|
// Find the sibling
|
|
auto layers = map->getStyle().getLayers();
|
|
auto siblingId = jni::Make<std::string>(env, above);
|
|
|
|
size_t index = 0;
|
|
for (auto l : layers) {
|
|
if (l->getID() == siblingId) {
|
|
break;
|
|
}
|
|
index++;
|
|
}
|
|
|
|
// Check if we found a sibling to place before
|
|
std::optional<std::string> before;
|
|
if (index + 1 > layers.size()) {
|
|
// Not found
|
|
jni::ThrowNew(env,
|
|
jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"),
|
|
std::string("Could not find layer: ").append(siblingId).c_str());
|
|
return;
|
|
} else if (index + 1 < layers.size()) {
|
|
// Place before the sibling
|
|
before = {layers.at(index + 1)->getID()};
|
|
}
|
|
|
|
// Add the layer
|
|
try {
|
|
layer->addToStyle(map->getStyle(), before);
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
void NativeMapView::addLayerAt(JNIEnv& env, jlong nativeLayerPtr, jni::jint index) {
|
|
assert(nativeLayerPtr != 0);
|
|
|
|
Layer* layer = reinterpret_cast<Layer*>(nativeLayerPtr);
|
|
auto layers = map->getStyle().getLayers();
|
|
|
|
// Check index
|
|
int numLayers = layers.size() - 1;
|
|
if (index > numLayers || index < 0) {
|
|
Log::Error(Event::JNI, "Index out of range: " + std::to_string(index));
|
|
jni::ThrowNew(env,
|
|
jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"),
|
|
std::string("Invalid index").c_str());
|
|
return;
|
|
}
|
|
|
|
// Insert it below the current at that index
|
|
try {
|
|
layer->addToStyle(map->getStyle(), layers.at(index)->getID());
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/layers/CannotAddLayerException"), error.what());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove layer at index.
|
|
*/
|
|
jni::jboolean NativeMapView::removeLayerAt(JNIEnv& env, jni::jint index) {
|
|
auto layers = map->getStyle().getLayers();
|
|
|
|
// Check index
|
|
int numLayers = layers.size() - 1;
|
|
if (index > numLayers || index < 0) {
|
|
Log::Warning(Event::JNI, "Index out of range: " + std::to_string(index));
|
|
return jni::jni_false;
|
|
}
|
|
|
|
std::unique_ptr<mln::style::Layer> coreLayer = map->getStyle().removeLayer(layers.at(index)->getID());
|
|
if (coreLayer) {
|
|
jni::Local<jni::Object<Layer>> layerObj = LayerManagerAndroid::get()->createJavaLayerPeer(env,
|
|
std::move(coreLayer));
|
|
return jni::jni_true;
|
|
}
|
|
return jni::jni_false;
|
|
}
|
|
|
|
/**
|
|
* Remove with wrapper object id. Ownership is transferred back to the wrapper
|
|
*/
|
|
jni::jboolean NativeMapView::removeLayer(JNIEnv& env, jlong layerPtr) {
|
|
if (layerPtr == 0) {
|
|
Log::Warning(Event::JNI, "Cannot remove layer: native layer pointer is null");
|
|
return jni::jni_false;
|
|
}
|
|
|
|
mln::android::Layer* layer = reinterpret_cast<mln::android::Layer*>(layerPtr);
|
|
const auto layerId = jni::Make<std::string>(env, layer->getId(env));
|
|
if (layerId.empty()) {
|
|
Log::Warning(Event::JNI, "Cannot remove layer: layer reference is detached or invalid");
|
|
return jni::jni_false;
|
|
}
|
|
|
|
std::unique_ptr<mln::style::Layer> coreLayer = map->getStyle().removeLayer(layerId);
|
|
if (coreLayer) {
|
|
layer->setLayer(std::move(coreLayer));
|
|
return jni::jni_true;
|
|
}
|
|
return jni::jni_false;
|
|
}
|
|
|
|
jni::Local<jni::Array<jni::Object<Source>>> NativeMapView::getSources(JNIEnv& env) {
|
|
// Get the core sources
|
|
std::vector<style::Source*> sources = map->getStyle().getSources();
|
|
|
|
// Convert
|
|
auto jSources = jni::Array<jni::Object<Source>>::New(env, sources.size());
|
|
int index = 0;
|
|
for (auto source : sources) {
|
|
jSources.Set(env, index, Source::peerForCoreSource(env, *source, *rendererFrontend, *map));
|
|
index++;
|
|
}
|
|
|
|
return jSources;
|
|
}
|
|
|
|
jni::Local<jni::Object<Source>> NativeMapView::getSource(JNIEnv& env, const jni::String& sourceId) {
|
|
// Find the source
|
|
mln::style::Source* coreSource = map->getStyle().getSource(jni::Make<std::string>(env, sourceId));
|
|
if (!coreSource) {
|
|
mln::Log::Debug(mln::Event::JNI, "No source found");
|
|
return jni::Local<jni::Object<Source>>();
|
|
}
|
|
|
|
// Create and return the source's native peer
|
|
return jni::NewLocal(env, Source::peerForCoreSource(env, *coreSource, *rendererFrontend, *map));
|
|
}
|
|
|
|
jni::jboolean NativeMapView::hasStyleSource(JNIEnv& env, const jni::String& sourceId) {
|
|
return map->getStyle().getSource(jni::Make<std::string>(env, sourceId)) != nullptr;
|
|
}
|
|
|
|
void NativeMapView::addSource(JNIEnv& env, const jni::Object<Source>& obj, jlong sourcePtr) {
|
|
assert(sourcePtr != 0);
|
|
|
|
Source* source = reinterpret_cast<Source*>(sourcePtr);
|
|
try {
|
|
source->addToMap(env, obj, *map, *rendererFrontend);
|
|
} catch (const std::runtime_error& error) {
|
|
jni::ThrowNew(
|
|
env, jni::FindClass(env, "org/maplibre/android/style/sources/CannotAddSourceException"), error.what());
|
|
}
|
|
}
|
|
|
|
jni::jboolean NativeMapView::removeSource(JNIEnv& env, const jni::Object<Source>& obj, jlong sourcePtr) {
|
|
assert(sourcePtr != 0);
|
|
|
|
mln::android::Source* source = reinterpret_cast<mln::android::Source*>(sourcePtr);
|
|
if (source->removeFromMap(env, obj, *map)) {
|
|
source->releaseJavaPeer();
|
|
return jni::jni_true;
|
|
}
|
|
|
|
return jni::jni_false;
|
|
}
|
|
|
|
void NativeMapView::addImage(
|
|
JNIEnv& env, const jni::String& name, const jni::Object<Bitmap>& bitmap, jni::jfloat scale, jni::jboolean sdf) {
|
|
jni::NullCheck(env, &bitmap);
|
|
mln::PremultipliedImage premultipliedImage = Bitmap::GetImage(env, bitmap);
|
|
|
|
map->getStyle().addImage(std::make_unique<mln::style::Image>(
|
|
jni::Make<std::string>(env, name), std::move(premultipliedImage), static_cast<float>(scale), sdf));
|
|
}
|
|
|
|
void NativeMapView::addImages(JNIEnv& env, const jni::Array<jni::Object<mln::android::Image>>& jimages) {
|
|
jni::NullCheck(env, &jimages);
|
|
std::size_t len = jimages.Length(env);
|
|
|
|
for (std::size_t i = 0; i < len; i++) {
|
|
auto image = mln::android::Image::getImage(env, jimages.Get(env, i));
|
|
map->getStyle().addImage(std::make_unique<mln::style::Image>(image));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::removeImage(JNIEnv& env, const jni::String& name) {
|
|
map->getStyle().removeImage(jni::Make<std::string>(env, name));
|
|
}
|
|
|
|
jni::Local<jni::Object<Bitmap>> NativeMapView::getImage(JNIEnv& env, const jni::String& name) {
|
|
if (auto image = map->getStyle().getImage(jni::Make<std::string>(env, name))) {
|
|
return Bitmap::CreateBitmap(env, image->getImage());
|
|
}
|
|
return jni::Local<jni::Object<Bitmap>>();
|
|
}
|
|
|
|
void NativeMapView::setPrefetchTiles(JNIEnv&, jni::jboolean enable) {
|
|
map->setPrefetchZoomDelta(enable ? util::DEFAULT_PREFETCH_ZOOM_DELTA : uint8_t(0));
|
|
}
|
|
|
|
jni::jboolean NativeMapView::getPrefetchTiles(JNIEnv&) {
|
|
return jni::jboolean(map->getPrefetchZoomDelta() > 0);
|
|
}
|
|
|
|
void NativeMapView::setPrefetchZoomDelta(JNIEnv&, jni::jint delta) {
|
|
map->setPrefetchZoomDelta(uint8_t(delta));
|
|
}
|
|
|
|
jni::jint NativeMapView::getPrefetchZoomDelta(JNIEnv&) {
|
|
return jni::jint(map->getPrefetchZoomDelta());
|
|
}
|
|
|
|
void NativeMapView::setTileCacheEnabled(JNIEnv&, jni::jboolean enabled) {
|
|
rendererFrontend->setTileCacheEnabled(enabled);
|
|
}
|
|
|
|
jni::jboolean NativeMapView::getTileCacheEnabled(JNIEnv&) {
|
|
return jni::jboolean(rendererFrontend->getTileCacheEnabled());
|
|
}
|
|
|
|
void NativeMapView::setTileLodMinRadius(JNIEnv&, jni::jdouble radius) {
|
|
map->setTileLodMinRadius(radius);
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getTileLodMinRadius(JNIEnv&) {
|
|
return jni::jdouble(map->getTileLodMinRadius());
|
|
}
|
|
|
|
void NativeMapView::setTileLodScale(JNIEnv&, jni::jdouble scale) {
|
|
map->setTileLodScale(scale);
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getTileLodScale(JNIEnv&) {
|
|
return jni::jdouble(map->getTileLodScale());
|
|
}
|
|
|
|
void NativeMapView::setTileLodPitchThreshold(JNIEnv&, jni::jdouble threshold) {
|
|
map->setTileLodPitchThreshold(threshold);
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getTileLodPitchThreshold(JNIEnv&) {
|
|
return jni::jdouble(map->getTileLodPitchThreshold());
|
|
}
|
|
|
|
void NativeMapView::setTileLodZoomShift(JNIEnv&, jni::jdouble shift) {
|
|
map->setTileLodZoomShift(shift);
|
|
}
|
|
|
|
jni::jdouble NativeMapView::getTileLodZoomShift(JNIEnv&) {
|
|
return jni::jdouble(map->getTileLodZoomShift());
|
|
}
|
|
|
|
mln::Map& NativeMapView::getMap() {
|
|
return *map;
|
|
}
|
|
|
|
void NativeMapView::triggerRepaint(JNIEnv&) {
|
|
assert(map);
|
|
map->triggerRepaint();
|
|
}
|
|
|
|
jni::jboolean NativeMapView::isRenderingStatsViewEnabled(JNIEnv&) {
|
|
assert(map);
|
|
return jni::jboolean(map->isRenderingStatsViewEnabled());
|
|
}
|
|
|
|
void NativeMapView::enableRenderingStatsView(JNIEnv&, jni::jboolean value) {
|
|
assert(map);
|
|
map->enableRenderingStatsView(value);
|
|
}
|
|
|
|
void NativeMapView::setFrustumOffset(JNIEnv& env, const jni::Object<RectF>& padding) {
|
|
mln::EdgeInsets offset = {RectF::getTop(env, padding),
|
|
RectF::getLeft(env, padding),
|
|
RectF::getBottom(env, padding),
|
|
RectF::getRight(env, padding)};
|
|
map->setFrustumOffset(offset);
|
|
}
|
|
|
|
// Static methods //
|
|
|
|
void NativeMapView::registerNative(jni::JNIEnv& env) {
|
|
// Lookup the class
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(env);
|
|
|
|
#define METHOD(MethodPtr, name) jni::MakeNativePeerMethod<decltype(MethodPtr), (MethodPtr)>(name)
|
|
|
|
// Register the peer
|
|
jni::RegisterNativePeer<NativeMapView>(
|
|
env,
|
|
javaClass,
|
|
"nativePtr",
|
|
jni::MakePeer<NativeMapView,
|
|
const jni::Object<NativeMapView>&,
|
|
const jni::Object<FileSource>&,
|
|
const jni::Object<MapRenderer>&,
|
|
const jni::Object<NativeMapOptions>&>,
|
|
"nativeInitialize",
|
|
"nativeDestroy",
|
|
METHOD(&NativeMapView::resizeView, "nativeResizeView"),
|
|
METHOD(&NativeMapView::getStyleUrl, "nativeGetStyleUrl"),
|
|
METHOD(&NativeMapView::setStyleUrl, "nativeSetStyleUrl"),
|
|
METHOD(&NativeMapView::getStyleJson, "nativeGetStyleJson"),
|
|
METHOD(&NativeMapView::setStyleJson, "nativeSetStyleJson"),
|
|
METHOD(&NativeMapView::cancelTransitions, "nativeCancelTransitions"),
|
|
METHOD(&NativeMapView::setGestureInProgress, "nativeSetGestureInProgress"),
|
|
METHOD(&NativeMapView::moveBy, "nativeMoveBy"),
|
|
METHOD(&NativeMapView::jumpTo, "nativeJumpTo"),
|
|
METHOD(&NativeMapView::easeTo, "nativeEaseTo"),
|
|
METHOD(&NativeMapView::flyTo, "nativeFlyTo"),
|
|
METHOD(&NativeMapView::getLatLng, "nativeGetLatLng"),
|
|
METHOD(&NativeMapView::setLatLng, "nativeSetLatLng"),
|
|
METHOD(&NativeMapView::getCameraForLatLngBounds, "nativeGetCameraForLatLngBounds"),
|
|
METHOD(&NativeMapView::getCameraForGeometry, "nativeGetCameraForGeometry"),
|
|
METHOD(&NativeMapView::setReachability, "nativeSetReachability"),
|
|
METHOD(&NativeMapView::resetPosition, "nativeResetPosition"),
|
|
METHOD(&NativeMapView::getPitch, "nativeGetPitch"),
|
|
METHOD(&NativeMapView::setPitch, "nativeSetPitch"),
|
|
METHOD(&NativeMapView::getZoom, "nativeGetZoom"),
|
|
METHOD(&NativeMapView::setZoom, "nativeSetZoom"),
|
|
METHOD(&NativeMapView::resetZoom, "nativeResetZoom"),
|
|
METHOD(&NativeMapView::setMinZoom, "nativeSetMinZoom"),
|
|
METHOD(&NativeMapView::getMinZoom, "nativeGetMinZoom"),
|
|
METHOD(&NativeMapView::setMaxZoom, "nativeSetMaxZoom"),
|
|
METHOD(&NativeMapView::getMaxZoom, "nativeGetMaxZoom"),
|
|
METHOD(&NativeMapView::setMinPitch, "nativeSetMinPitch"),
|
|
METHOD(&NativeMapView::getMinPitch, "nativeGetMinPitch"),
|
|
METHOD(&NativeMapView::setMaxPitch, "nativeSetMaxPitch"),
|
|
METHOD(&NativeMapView::getMaxPitch, "nativeGetMaxPitch"),
|
|
METHOD(&NativeMapView::rotateBy, "nativeRotateBy"),
|
|
METHOD(&NativeMapView::setBearing, "nativeSetBearing"),
|
|
METHOD(&NativeMapView::setBearingXY, "nativeSetBearingXY"),
|
|
METHOD(&NativeMapView::getBearing, "nativeGetBearing"),
|
|
METHOD(&NativeMapView::resetNorth, "nativeResetNorth"),
|
|
METHOD(&NativeMapView::setVisibleCoordinateBounds, "nativeSetVisibleCoordinateBounds"),
|
|
METHOD(&NativeMapView::scheduleSnapshot, "nativeTakeSnapshot"),
|
|
METHOD(&NativeMapView::getCameraPosition, "nativeGetCameraPosition"),
|
|
METHOD(&NativeMapView::updateMarker, "nativeUpdateMarker"),
|
|
METHOD(&NativeMapView::addMarkers, "nativeAddMarkers"),
|
|
METHOD(&NativeMapView::setDebug, "nativeSetDebug"),
|
|
METHOD(&NativeMapView::getDebug, "nativeGetDebug"),
|
|
METHOD(&NativeMapView::setRetainedUnderlayEnabled, "nativeSetRetainedUnderlayEnabled"),
|
|
METHOD(&NativeMapView::setContinuousLayerRange, "nativeSetContinuousLayerRange"),
|
|
METHOD(&NativeMapView::setRenderSegmentPlan, "nativeSetRenderSegmentPlan"),
|
|
METHOD(&NativeMapView::getRenderSegmentDiagnostics, "nativeGetRenderSegmentDiagnostics"),
|
|
METHOD(&NativeMapView::getRetainedUnderlayDiagnostics, "nativeGetRetainedUnderlayDiagnostics"),
|
|
METHOD(&NativeMapView::getActionJournalLogFiles, "nativeGetActionJournalLogFiles"),
|
|
METHOD(&NativeMapView::getActionJournalLog, "nativeGetActionJournalLog"),
|
|
METHOD(&NativeMapView::clearActionJournalLog, "nativeClearActionJournalLog"),
|
|
METHOD(&NativeMapView::isFullyLoaded, "nativeIsFullyLoaded"),
|
|
METHOD(&NativeMapView::onLowMemory, "nativeOnLowMemory"),
|
|
METHOD(&NativeMapView::getMetersPerPixelAtLatitude, "nativeGetMetersPerPixelAtLatitude"),
|
|
METHOD(&NativeMapView::projectedMetersForLatLng, "nativeProjectedMetersForLatLng"),
|
|
METHOD(&NativeMapView::pixelForLatLng, "nativePixelForLatLng"),
|
|
METHOD(&NativeMapView::pixelsForLatLngs, "nativePixelsForLatLngs"),
|
|
METHOD(&NativeMapView::getVisibleCoordinateBounds, "nativeGetVisibleCoordinateBounds"),
|
|
METHOD(&NativeMapView::latLngForProjectedMeters, "nativeLatLngForProjectedMeters"),
|
|
METHOD(&NativeMapView::latLngForPixel, "nativeLatLngForPixel"),
|
|
METHOD(&NativeMapView::latLngsForPixels, "nativeLatLngsForPixels"),
|
|
METHOD(&NativeMapView::addPolylines, "nativeAddPolylines"),
|
|
METHOD(&NativeMapView::addPolygons, "nativeAddPolygons"),
|
|
METHOD(&NativeMapView::updatePolyline, "nativeUpdatePolyline"),
|
|
METHOD(&NativeMapView::updatePolygon, "nativeUpdatePolygon"),
|
|
METHOD(&NativeMapView::removeAnnotations, "nativeRemoveAnnotations"),
|
|
METHOD(&NativeMapView::addAnnotationIcon, "nativeAddAnnotationIcon"),
|
|
METHOD(&NativeMapView::removeAnnotationIcon, "nativeRemoveAnnotationIcon"),
|
|
METHOD(&NativeMapView::getTopOffsetPixelsForAnnotationSymbol, "nativeGetTopOffsetPixelsForAnnotationSymbol"),
|
|
METHOD(&NativeMapView::getTransitionOptions, "nativeGetTransitionOptions"),
|
|
METHOD(&NativeMapView::setTransitionOptions, "nativeSetTransitionOptions"),
|
|
METHOD(&NativeMapView::queryPointAnnotations, "nativeQueryPointAnnotations"),
|
|
METHOD(&NativeMapView::queryShapeAnnotations, "nativeQueryShapeAnnotations"),
|
|
METHOD(&NativeMapView::queryRenderedFeaturesForPoint, "nativeQueryRenderedFeaturesForPoint"),
|
|
METHOD(&NativeMapView::queryRenderedFeaturesForBox, "nativeQueryRenderedFeaturesForBox"),
|
|
METHOD(&NativeMapView::setFeatureState, "nativeSetFeatureState"),
|
|
METHOD(&NativeMapView::getFeatureState, "nativeGetFeatureState"),
|
|
METHOD(&NativeMapView::removeFeatureState, "nativeRemoveFeatureState"),
|
|
METHOD(&NativeMapView::getLight, "nativeGetLight"),
|
|
METHOD(&NativeMapView::getLayers, "nativeGetLayers"),
|
|
METHOD(&NativeMapView::getLayer, "nativeGetLayer"),
|
|
METHOD(&NativeMapView::hasStyleLayer, "nativeHasStyleLayer"),
|
|
METHOD(&NativeMapView::setStyleLayerFloatProperty, "nativeSetStyleLayerFloatProperty"),
|
|
METHOD(&NativeMapView::setWeatherColorOpacity, "nativeSetWeatherColorOpacity"),
|
|
METHOD(&NativeMapView::setStyleLayerVisibility, "nativeSetStyleLayerVisibility"),
|
|
METHOD(&NativeMapView::setRasterParticleColors, "nativeSetRasterParticleColors"),
|
|
METHOD(&NativeMapView::setRasterParticleColor, "nativeSetRasterParticleColor"),
|
|
METHOD(&NativeMapView::setRasterParticleTrailColor, "nativeSetRasterParticleTrailColor"),
|
|
METHOD(&NativeMapView::setRasterArraySourceTargetFps, "nativeSetRasterArraySourceTargetFps"),
|
|
METHOD(&NativeMapView::getRasterArraySourceTargetFps, "nativeGetRasterArraySourceTargetFps"),
|
|
METHOD(&NativeMapView::setWeatherColorStops, "nativeSetWeatherColorStops"),
|
|
METHOD(&NativeMapView::getWeatherColorStopCapacity, "nativeGetWeatherColorStopCapacity"),
|
|
METHOD(&NativeMapView::advanceRasterArraySourceFieldWindow, "nativeAdvanceRasterArraySourceFieldWindow"),
|
|
METHOD(&NativeMapView::repeatRasterArraySourceFieldWindow, "nativeRepeatRasterArraySourceFieldWindow"),
|
|
METHOD(&NativeMapView::submitRasterArraySourceHostField, "nativeSubmitRasterArraySourceHostField"),
|
|
METHOD(&NativeMapView::submitRasterArraySourceHostFieldV2, "nativeSubmitRasterArraySourceHostFieldV2"),
|
|
METHOD(&NativeMapView::clearRasterArraySourceHostFields, "nativeClearRasterArraySourceHostFields"),
|
|
METHOD(&NativeMapView::resetRasterArraySourceWeatherRuntime, "nativeResetRasterArraySourceWeatherRuntime"),
|
|
METHOD(&NativeMapView::seekRasterArraySourceTimelineToTimestamp, "nativeSeekRasterArraySourceTimelineToTimestamp"),
|
|
METHOD(&NativeMapView::controlRasterArraySourceTimeline, "nativeControlRasterArraySourceTimeline"),
|
|
METHOD(&NativeMapView::submitRasterArraySourceEncodedField, "nativeSubmitRasterArraySourceEncodedField"),
|
|
METHOD(&NativeMapView::clearRasterArraySourceEncodedField, "nativeClearRasterArraySourceEncodedField"),
|
|
METHOD(&NativeMapView::clearAllRasterArraySourceEncodedFields, "nativeClearAllRasterArraySourceEncodedFields"),
|
|
METHOD(&NativeMapView::setRasterArraySourceEncodedColorBinding, "nativeSetRasterArraySourceEncodedColorBinding"),
|
|
METHOD(&NativeMapView::setRasterArraySourceEncodedParticleBinding, "nativeSetRasterArraySourceEncodedParticleBinding"),
|
|
METHOD(&NativeMapView::setRasterArraySourceEncodedForecastTime, "nativeSetRasterArraySourceEncodedForecastTime"),
|
|
METHOD(&NativeMapView::setRasterArraySourceEncodedForecastPlaybackState, "nativeSetRasterArraySourceEncodedForecastPlaybackState"),
|
|
METHOD(&NativeMapView::setRasterArraySourceEncodedForecastPlaybackSpeed, "nativeSetRasterArraySourceEncodedForecastPlaybackSpeed"),
|
|
METHOD(&NativeMapView::addLayer, "nativeAddLayer"),
|
|
METHOD(&NativeMapView::addLayerAbove, "nativeAddLayerAbove"),
|
|
METHOD(&NativeMapView::addLayerAt, "nativeAddLayerAt"),
|
|
METHOD(&NativeMapView::removeLayerAt, "nativeRemoveLayerAt"),
|
|
METHOD(&NativeMapView::removeLayer, "nativeRemoveLayer"),
|
|
METHOD(&NativeMapView::getSources, "nativeGetSources"),
|
|
METHOD(&NativeMapView::getSource, "nativeGetSource"),
|
|
METHOD(&NativeMapView::hasStyleSource, "nativeHasStyleSource"),
|
|
METHOD(&NativeMapView::addSource, "nativeAddSource"),
|
|
METHOD(&NativeMapView::removeSource, "nativeRemoveSource"),
|
|
METHOD(&NativeMapView::addImage, "nativeAddImage"),
|
|
METHOD(&NativeMapView::addImages, "nativeAddImages"),
|
|
METHOD(&NativeMapView::removeImage, "nativeRemoveImage"),
|
|
METHOD(&NativeMapView::getImage, "nativeGetImage"),
|
|
METHOD(&NativeMapView::setLatLngBounds, "nativeSetLatLngBounds"),
|
|
METHOD(&NativeMapView::setPrefetchTiles, "nativeSetPrefetchTiles"),
|
|
METHOD(&NativeMapView::getPrefetchTiles, "nativeGetPrefetchTiles"),
|
|
METHOD(&NativeMapView::setPrefetchZoomDelta, "nativeSetPrefetchZoomDelta"),
|
|
METHOD(&NativeMapView::getPrefetchZoomDelta, "nativeGetPrefetchZoomDelta"),
|
|
METHOD(&NativeMapView::setTileCacheEnabled, "nativeSetTileCacheEnabled"),
|
|
METHOD(&NativeMapView::getTileCacheEnabled, "nativeGetTileCacheEnabled"),
|
|
METHOD(&NativeMapView::setTileLodMinRadius, "nativeSetTileLodMinRadius"),
|
|
METHOD(&NativeMapView::getTileLodMinRadius, "nativeGetTileLodMinRadius"),
|
|
METHOD(&NativeMapView::setTileLodScale, "nativeSetTileLodScale"),
|
|
METHOD(&NativeMapView::getTileLodScale, "nativeGetTileLodScale"),
|
|
METHOD(&NativeMapView::setTileLodPitchThreshold, "nativeSetTileLodPitchThreshold"),
|
|
METHOD(&NativeMapView::getTileLodPitchThreshold, "nativeGetTileLodPitchThreshold"),
|
|
METHOD(&NativeMapView::setTileLodZoomShift, "nativeSetTileLodZoomShift"),
|
|
METHOD(&NativeMapView::getTileLodZoomShift, "nativeGetTileLodZoomShift"),
|
|
METHOD(&NativeMapView::triggerRepaint, "nativeTriggerRepaint"),
|
|
METHOD(&NativeMapView::isRenderingStatsViewEnabled, "nativeIsRenderingStatsViewEnabled"),
|
|
METHOD(&NativeMapView::enableRenderingStatsView, "nativeEnableRenderingStatsView"),
|
|
METHOD(&NativeMapView::setFrustumOffset, "nativeSetFrustumOffset"));
|
|
}
|
|
|
|
void NativeMapView::onRegisterShaders(gfx::ShaderRegistry&) {};
|
|
|
|
// Shader compilation
|
|
void NativeMapView::onPreCompileShader(shaders::BuiltIn id,
|
|
gfx::Backend::Type type,
|
|
const std::string& additionalDefines) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onPreCompileShader = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(*_env,
|
|
"onPreCompileShader");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env,
|
|
onPreCompileShader,
|
|
static_cast<jni::jint>(id),
|
|
static_cast<jni::jint>(type),
|
|
jni::Make<jni::String>(*_env, additionalDefines));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onPostCompileShader(shaders::BuiltIn id,
|
|
gfx::Backend::Type type,
|
|
const std::string& additionalDefines) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onPostCompileShader = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(
|
|
*_env, "onPostCompileShader");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env,
|
|
onPostCompileShader,
|
|
static_cast<jni::jint>(id),
|
|
static_cast<jni::jint>(type),
|
|
jni::Make<jni::String>(*_env, additionalDefines));
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onShaderCompileFailed(shaders::BuiltIn id,
|
|
gfx::Backend::Type type,
|
|
const std::string& additionalDefines) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onShaderCompileFailed = javaClass.GetMethod<void(jni::jint, jni::jint, jni::String)>(
|
|
*_env, "onShaderCompileFailed");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env,
|
|
onShaderCompileFailed,
|
|
static_cast<jni::jint>(id),
|
|
static_cast<jni::jint>(type),
|
|
jni::Make<jni::String>(*_env, additionalDefines));
|
|
}
|
|
}
|
|
|
|
// Glyph requests
|
|
void NativeMapView::onGlyphsLoaded(const FontStack& stack, const GlyphRange& range) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onGlyphsLoaded = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
|
|
*_env, "onGlyphsLoaded");
|
|
|
|
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
|
|
for (std::size_t i = 0; i < stack.size(); i++) {
|
|
fontStack.Set(*_env, i, jni::Make<jni::String>(*_env, stack.at(i)));
|
|
}
|
|
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onGlyphsLoaded, fontStack, range.first, range.second);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onGlyphsError(const FontStack& stack, const GlyphRange& range, std::exception_ptr) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onGlyphsError = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
|
|
*_env, "onGlyphsError");
|
|
|
|
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
|
|
for (std::size_t i = 0; i < stack.size(); i++) {
|
|
fontStack.Set(*_env, i, jni::Make<jni::String>(*_env, stack.at(i)));
|
|
}
|
|
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onGlyphsError, fontStack, range.first, range.second);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onGlyphsRequested(const FontStack& stack, const GlyphRange& range) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onGlyphsRequested = javaClass.GetMethod<void(jni::Array<jni::String>, jni::jint, jni::jint)>(
|
|
*_env, "onGlyphsRequested");
|
|
|
|
auto fontStack = jni::Array<jni::String>::New(*_env, stack.size());
|
|
for (std::size_t i = 0; i < stack.size(); i++) {
|
|
fontStack.Set(*_env, i, jni::Make<jni::String>(*_env, stack.at(i)));
|
|
}
|
|
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onGlyphsRequested, fontStack, range.first, range.second);
|
|
}
|
|
}
|
|
|
|
// Tile requests
|
|
void NativeMapView::onTileAction(mln::TileOperation op, const OverscaledTileID& id, const std::string& sourceID) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onTileAction = javaClass.GetMethod<void(
|
|
jni::Object<mln::android::TileOperation>, jni::jint, jni::jint, jni::jint, jni::jint, jni::jint, jni::String)>(
|
|
*_env, "onTileAction");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env,
|
|
onTileAction,
|
|
mln::android::TileOperation::Create(*_env, op),
|
|
static_cast<jni::jint>(id.canonical.x),
|
|
static_cast<jni::jint>(id.canonical.y),
|
|
static_cast<jni::jint>(id.canonical.z),
|
|
static_cast<jni::jint>(id.wrap),
|
|
static_cast<jni::jint>(id.overscaledZ),
|
|
jni::Make<jni::String>(*_env, sourceID));
|
|
}
|
|
}
|
|
|
|
// Sprite requests
|
|
void NativeMapView::onSpriteLoaded(const std::optional<style::Sprite>& sprite) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSpriteLoaded = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteLoaded");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
if (sprite) {
|
|
weakReference.Call(*_env,
|
|
onSpriteLoaded,
|
|
jni::Make<jni::String>(*_env, sprite->id),
|
|
jni::Make<jni::String>(*_env, sprite->spriteURL));
|
|
} else {
|
|
weakReference.Call(
|
|
*_env, onSpriteLoaded, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onSpriteError(const std::optional<style::Sprite>& sprite, std::exception_ptr ex) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSpriteError = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteError");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
if (sprite) {
|
|
weakReference.Call(*_env,
|
|
onSpriteError,
|
|
jni::Make<jni::String>(*_env, sprite->id),
|
|
jni::Make<jni::String>(*_env, sprite->spriteURL));
|
|
} else {
|
|
weakReference.Call(
|
|
*_env, onSpriteError, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onSpriteRequested(const std::optional<style::Sprite>& sprite) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSpriteRequested = javaClass.GetMethod<void(jni::String, jni::String)>(*_env, "onSpriteRequested");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
if (sprite) {
|
|
weakReference.Call(*_env,
|
|
onSpriteRequested,
|
|
jni::Make<jni::String>(*_env, sprite->id),
|
|
jni::Make<jni::String>(*_env, sprite->spriteURL));
|
|
} else {
|
|
weakReference.Call(
|
|
*_env, onSpriteRequested, jni::Make<jni::String>(*_env, ""), jni::Make<jni::String>(*_env, ""));
|
|
}
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onRenderError(std::exception_ptr) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onRenderError = javaClass.GetMethod<void()>(*_env, "onRenderError");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onRenderError);
|
|
}
|
|
}
|
|
|
|
void NativeMapView::onSymbolError(const std::string& message) {
|
|
assert(vm != nullptr);
|
|
|
|
android::UniqueEnv _env = android::AttachEnv();
|
|
static auto& javaClass = jni::Class<NativeMapView>::Singleton(*_env);
|
|
static auto onSymbolError = javaClass.GetMethod<void(jni::String)>(*_env, "onSymbolError");
|
|
auto weakReference = javaPeer.get(*_env);
|
|
if (weakReference) {
|
|
weakReference.Call(*_env, onSymbolError, jni::Make<jni::String>(*_env, message));
|
|
}
|
|
}
|
|
|
|
} // namespace android
|
|
} // namespace mln
|