Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[FEATURE] Returns layer Id on Feature Tap & update NDK version to fix crash on Android 15 #475

Merged
merged 15 commits into from
Dec 2, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions example/lib/layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,11 @@ class LayerState extends State {
controller.onFeatureTapped.add(onFeatureTap);
}

void onFeatureTap(dynamic featureId, Point<double> point, LatLng latLng) {
void onFeatureTap(
dynamic featureId, Point<double> point, LatLng latLng, String layerId) {
final snackBar = SnackBar(
content: Text(
'Tapped feature with id $featureId',
'Tapped feature with id $featureId on payer $layerId',
LouisRaverdy marked this conversation as resolved.
Show resolved Hide resolved
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
),
backgroundColor: Theme.of(context).primaryColor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import android.view.TextureView;
import android.view.View;
import android.widget.FrameLayout;
import android.util.Pair;

import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
Expand Down Expand Up @@ -652,7 +653,7 @@ private void addHeatmapLayer(
}
}

private Feature firstFeatureOnLayers(RectF in) {
private Pair<Feature, String> firstFeatureOnLayers(RectF in) {
if (style != null) {
final List<Layer> layers = style.getLayers();
final List<String> layersInOrder = new ArrayList<String>();
Expand All @@ -665,7 +666,7 @@ private Feature firstFeatureOnLayers(RectF in) {
for (String id : layersInOrder) {
List<Feature> features = mapLibreMap.queryRenderedFeatures(in, id);
if (!features.isEmpty()) {
return features.get(0);
return new Pair<Feature, String>(features.get(0), id);
}
}
}
Expand Down Expand Up @@ -1659,14 +1660,15 @@ public void onDidBecomeIdle() {
public boolean onMapClick(@NonNull LatLng point) {
PointF pointf = mapLibreMap.getProjection().toScreenLocation(point);
RectF rectF = new RectF(pointf.x - 10, pointf.y - 10, pointf.x + 10, pointf.y + 10);
Feature feature = firstFeatureOnLayers(rectF);
Pair<Feature, String> featureLayerPair = firstFeatureOnLayers(rectF);
final Map<String, Object> arguments = new HashMap<>();
arguments.put("x", pointf.x);
arguments.put("y", pointf.y);
arguments.put("lng", point.getLongitude());
arguments.put("lat", point.getLatitude());
if (feature != null) {
arguments.put("id", feature.id());
if (featureLayerPair != null && featureLayerPair.first != null) {
arguments.put("layerId", featureLayerPair.second);
arguments.put("id", featureLayerPair.first.id());
methodChannel.invokeMethod("feature#onTap", arguments);
} else {
methodChannel.invokeMethod("map#onMapClick", arguments);
Expand Down Expand Up @@ -2137,8 +2139,8 @@ boolean onMoveBegin(MoveGestureDetector detector) {
PointF pointf = detector.getFocalPoint();
LatLng origin = mapLibreMap.getProjection().fromScreenLocation(pointf);
RectF rectF = new RectF(pointf.x - 10, pointf.y - 10, pointf.x + 10, pointf.y + 10);
Feature feature = firstFeatureOnLayers(rectF);
if (feature != null && startDragging(feature, origin)) {
Pair<Feature, String> featureLayerPair = firstFeatureOnLayers(rectF);
if (featureLayerPair != null && featureLayerPair.first != null && startDragging(featureLayerPair.first, origin)) {
invokeFeatureDrag(pointf, "start");
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
longPress.require(toFail: recognizer)
}
var longPressRecognizerAdded = false

if let args = args as? [String: Any] {

Convert.interpretMapLibreMapOptions(options: args["options"], delegate: self)
if let initialCameraPosition = args["initialCameraPosition"] as? [String: Any],
let camera = MLNMapCamera.fromDict(initialCameraPosition, mapView: mapView),
Expand Down Expand Up @@ -170,7 +170,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
if let langStr = Locale.current.languageCode {
setMapLanguage(language: langStr)
}

result(nil)
case "map#updateContentInsets":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
Expand Down Expand Up @@ -316,7 +316,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
case "camera#move":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let cameraUpdate = arguments["cameraUpdate"] as? [Any] else { return }

if let camera = Convert.parseCameraUpdate(cameraUpdate: cameraUpdate, mapView: mapView) {
mapView.setCamera(camera, animated: false)
}
Expand All @@ -325,12 +325,12 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let cameraUpdate = arguments["cameraUpdate"] as? [Any] else { return }
guard let camera = Convert.parseCameraUpdate(cameraUpdate: cameraUpdate, mapView: mapView) else { return }


let completion = {
result(nil)
}

if let duration = arguments["duration"] as? TimeInterval {
if let padding = Convert.parseLatLngBoundsPadding(cameraUpdate) {
mapView.fly(to: camera, edgePadding: padding, withDuration: duration / 1000, completionHandler: completion)
Expand Down Expand Up @@ -537,7 +537,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
properties: properties
)
result(nil)

case "heatmapLayer#add":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
Expand Down Expand Up @@ -840,11 +840,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
}
layer.isVisible = visible
result(nil)

case "map#querySourceFeatures":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }

var sourceLayerId = Set<String>()
if let layerId = arguments["sourceLayerId"] as? String {
sourceLayerId.insert(layerId)
Expand All @@ -853,10 +853,10 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
if let filter = arguments["filter"] as? [Any] {
filterExpression = NSPredicate(mglJSONObject: filter)
}

var reply = [String: NSObject]()
var features: [MLNFeature] = []

guard let style = mapView.style else { return }
if let source = style.source(withIdentifier: sourceId) {
if let vectorSource = source as? MLNVectorTileSource {
Expand All @@ -865,7 +865,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
features = shapeSource.features(matching: filterExpression)
}
}

var featuresJson = [String]()
for feature in features {
let dictionary = feature.geoJSONDictionary()
Expand All @@ -883,11 +883,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,

case "style#getLayerIds":
var layerIds = [String]()

guard let style = mapView.style else { return }

style.layers.forEach { layer in layerIds.append(layer.identifier) }

var reply = [String: NSObject]()
reply["layers"] = layerIds as NSObject
result(reply)
Expand All @@ -902,18 +902,18 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
var reply = [String: NSObject]()
reply["sources"] = sourceIds as NSObject
result(reply)

case "style#getFilter":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let layerId = arguments["layerId"] as? String else { return }

guard let style = mapView.style else { return }
guard let layer = style.layer(withIdentifier: layerId) else { return }

var currentLayerFilter : String = ""
if let vectorLayer = layer as? MLNVectorStyleLayer {
if let layerFilter = vectorLayer.predicate {

let jsonExpression = layerFilter.mgl_jsonExpressionObject
if let data = try? JSONSerialization.data(withJSONObject: jsonExpression, options: []) {
currentLayerFilter = String(data: data, encoding: String.Encoding.utf8) ?? ""
Expand All @@ -925,11 +925,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
).flutterError)
return;
}

var reply = [String: NSObject]()
reply["filter"] = currentLayerFilter as NSObject
result(reply)

default:
result(FlutterMethodNotImplemented)
}
Expand Down Expand Up @@ -967,16 +967,16 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
private func getCamera() -> MLNMapCamera? {
return trackCameraPosition ? mapView.camera : nil
}

private func setMapLanguage(language: String) {
self.mapView.setMapLanguage(language)
}

/*
* Scan layers from top to bottom and return the first matching feature
*/
private func firstFeatureOnLayers(at: CGPoint) -> MLNFeature? {
guard let style = mapView.style else { return nil }
private func firstFeatureOnLayers(at: CGPoint) -> (feature: MLNFeature?, layerId: String?) {
guard let style = mapView.style else { return (nil, nil) }

// get layers in order (interactiveFeatureLayerIds is unordered)
let clickableLayers = style.layers.filter { layer in
Expand All @@ -989,10 +989,10 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
styleLayerIdentifiers: [layer.identifier]
)
if let feature = features.first {
return feature
return (feature, layer.identifier)
}
}
return nil
return (nil, nil)
}

/*
Expand All @@ -1004,13 +1004,15 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
let point = sender.location(in: mapView)
let coordinate = mapView.convert(point, toCoordinateFrom: mapView)

if let feature = firstFeatureOnLayers(at: point) {
let result = firstFeatureOnLayers(at: point)
if let feature = result.feature {
channel?.invokeMethod("feature#onTap", arguments: [
"id": feature.identifier,
"x": point.x,
"y": point.y,
"lng": coordinate.longitude,
"lat": coordinate.latitude,
"layerId": result.layerId,
])
} else {
channel?.invokeMethod("map#onMapClick", arguments: [
Expand Down Expand Up @@ -1053,22 +1055,23 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
let point = sender.location(in: mapView)
let coordinate = mapView.convert(point, toCoordinateFrom: mapView)

if dragFeature == nil, began, sender.numberOfTouches == 1,
let feature = firstFeatureOnLayers(at: point),
let draggable = feature.attribute(forKey: "draggable") as? Bool,
draggable
{
sender.state = UIGestureRecognizer.State.began
dragFeature = feature
originDragCoordinate = coordinate
previousDragCoordinate = coordinate
mapView.allowsScrolling = false
let eventType = "start"
invokeFeatureDrag(point, coordinate, eventType)
for gestureRecognizer in mapView.gestureRecognizers! {
if let _ = gestureRecognizer as? UIPanGestureRecognizer {
gestureRecognizer.addTarget(self, action: #selector(handleMapPan))
break
if dragFeature == nil, began, sender.numberOfTouches == 1 {
let result = firstFeatureOnLayers(at: point)
if let feature = result.feature,
let draggable = feature.attribute(forKey: "draggable") as? Bool,
draggable {
sender.state = UIGestureRecognizer.State.began
dragFeature = feature
originDragCoordinate = coordinate
previousDragCoordinate = coordinate
mapView.allowsScrolling = false
let eventType = "start"
invokeFeatureDrag(point, coordinate, eventType)
for gestureRecognizer in mapView.gestureRecognizers! {
if let _ = gestureRecognizer as? UIPanGestureRecognizer {
gestureRecognizer.addTarget(self, action: #selector(handleMapPan))
break
}
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion maplibre_gl/lib/src/annotation_manager.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ abstract class AnnotationManager<T extends Annotation> {
}
}

_onFeatureTapped(dynamic id, Point<double> point, LatLng coordinates) {
_onFeatureTapped(
dynamic id, Point<double> point, LatLng coordinates, String layerId) {
final annotation = _idToAnnotation[id];
if (annotation != null) {
onTap!(annotation);
Expand Down
5 changes: 3 additions & 2 deletions maplibre_gl/lib/src/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ typedef OnMapClickCallback = void Function(
Point<double> point, LatLng coordinates);

typedef OnFeatureInteractionCallback = void Function(
dynamic id, Point<double> point, LatLng coordinates);
dynamic id, Point<double> point, LatLng coordinates, String layerId);
LouisRaverdy marked this conversation as resolved.
Show resolved Hide resolved

typedef OnFeatureDragnCallback = void Function(dynamic id,
{required Point<double> point,
Expand Down Expand Up @@ -93,7 +93,8 @@ class MapLibreMapController extends ChangeNotifier {
_maplibrePlatform.onFeatureTappedPlatform.add((payload) {
for (final fun
in List<OnFeatureInteractionCallback>.from(onFeatureTapped)) {
fun(payload["id"], payload["point"], payload["latLng"]);
fun(payload["id"], payload["point"], payload["latLng"],
payload["layerId"]);
}
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ class MapLibreMethodChannel extends MapLibrePlatform {
final double y = call.arguments['y'];
final double lng = call.arguments['lng'];
final double lat = call.arguments['lat'];
final String layerId = call.arguments['layerId'];
onFeatureTappedPlatform({
'id': id,
'point': Point<double>(x, y),
'latLng': LatLng(lat, lng)
'latLng': LatLng(lat, lng),
'layerId': layerId
});
case 'feature#onDrag':
final id = call.arguments['id'];
Expand Down