Skip to content

Commit 4bb3ced

Browse files
LouisRaverdyjosxha
authored andcommitted
[FEATURE] Returns layer Id on Feature Tap & update NDK version to fix crash on Android 15 (maplibre#475)
Hello MapLibre Team ! I needed to retrieve the layer Id of the feature tapped, so I've implemented it. This is much cleaner than using the given Id to have a unique identifier and the layer id. Best regards, Louis --------- Co-authored-by: Joscha <[email protected]>
1 parent bb12f55 commit 4bb3ced

File tree

7 files changed

+73
-63
lines changed

7 files changed

+73
-63
lines changed

example/lib/layer.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,11 @@ class LayerState extends State {
145145
controller.onFeatureTapped.add(onFeatureTap);
146146
}
147147

148-
void onFeatureTap(dynamic featureId, Point<double> point, LatLng latLng) {
148+
void onFeatureTap(
149+
dynamic featureId, Point<double> point, LatLng latLng, String layerId) {
149150
final snackBar = SnackBar(
150151
content: Text(
151-
'Tapped feature with id $featureId',
152+
'Tapped feature with id $featureId on layer $layerId',
152153
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
153154
),
154155
backgroundColor: Theme.of(context).primaryColor,

maplibre_gl/android/build.gradle

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ android {
3030
}
3131

3232
compileSdkVersion 34
33-
ndkVersion "26.1.10909125"
33+
ndkVersion "27.0.12077973"
3434

3535
defaultConfig {
3636
minSdkVersion 21
@@ -48,9 +48,9 @@ android {
4848
jvmTarget = JavaVersion.VERSION_1_8
4949
}
5050
dependencies {
51-
implementation 'org.maplibre.gl:android-sdk:11.0.0'
52-
implementation 'org.maplibre.gl:android-plugin-annotation-v9:3.0.0'
53-
implementation 'org.maplibre.gl:android-plugin-offline-v9:3.0.0'
51+
implementation 'org.maplibre.gl:android-sdk:11.6.1'
52+
implementation 'org.maplibre.gl:android-plugin-annotation-v9:3.0.2'
53+
implementation 'org.maplibre.gl:android-plugin-offline-v9:3.0.2'
5454
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
5555
}
5656
}

maplibre_gl/android/src/main/java/org/maplibre/maplibregl/MapLibreMapController.java

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import android.view.TextureView;
2323
import android.view.View;
2424
import android.widget.FrameLayout;
25+
import android.util.Pair;
2526

2627
import androidx.annotation.NonNull;
2728
import androidx.lifecycle.DefaultLifecycleObserver;
@@ -31,8 +32,8 @@
3132
import com.google.gson.JsonArray;
3233
import com.google.gson.JsonElement;
3334
import com.google.gson.JsonParser;
34-
import com.mapbox.android.gestures.AndroidGesturesManager;
35-
import com.mapbox.android.gestures.MoveGestureDetector;
35+
import org.maplibre.android.gestures.AndroidGesturesManager;
36+
import org.maplibre.android.gestures.MoveGestureDetector;
3637
import org.maplibre.geojson.Feature;
3738
import org.maplibre.geojson.FeatureCollection;
3839
import org.maplibre.android.camera.CameraPosition;
@@ -652,7 +653,7 @@ private void addHeatmapLayer(
652653
}
653654
}
654655

655-
private Feature firstFeatureOnLayers(RectF in) {
656+
private Pair<Feature, String> firstFeatureOnLayers(RectF in) {
656657
if (style != null) {
657658
final List<Layer> layers = style.getLayers();
658659
final List<String> layersInOrder = new ArrayList<String>();
@@ -665,7 +666,7 @@ private Feature firstFeatureOnLayers(RectF in) {
665666
for (String id : layersInOrder) {
666667
List<Feature> features = mapLibreMap.queryRenderedFeatures(in, id);
667668
if (!features.isEmpty()) {
668-
return features.get(0);
669+
return new Pair<Feature, String>(features.get(0), id);
669670
}
670671
}
671672
}
@@ -1677,14 +1678,15 @@ public void onDidBecomeIdle() {
16771678
public boolean onMapClick(@NonNull LatLng point) {
16781679
PointF pointf = mapLibreMap.getProjection().toScreenLocation(point);
16791680
RectF rectF = new RectF(pointf.x - 10, pointf.y - 10, pointf.x + 10, pointf.y + 10);
1680-
Feature feature = firstFeatureOnLayers(rectF);
1681+
Pair<Feature, String> featureLayerPair = firstFeatureOnLayers(rectF);
16811682
final Map<String, Object> arguments = new HashMap<>();
16821683
arguments.put("x", pointf.x);
16831684
arguments.put("y", pointf.y);
16841685
arguments.put("lng", point.getLongitude());
16851686
arguments.put("lat", point.getLatitude());
1686-
if (feature != null) {
1687-
arguments.put("id", feature.id());
1687+
if (featureLayerPair != null && featureLayerPair.first != null) {
1688+
arguments.put("layerId", featureLayerPair.second);
1689+
arguments.put("id", featureLayerPair.first.id());
16881690
methodChannel.invokeMethod("feature#onTap", arguments);
16891691
} else {
16901692
methodChannel.invokeMethod("map#onMapClick", arguments);
@@ -2155,8 +2157,8 @@ boolean onMoveBegin(MoveGestureDetector detector) {
21552157
PointF pointf = detector.getFocalPoint();
21562158
LatLng origin = mapLibreMap.getProjection().fromScreenLocation(pointf);
21572159
RectF rectF = new RectF(pointf.x - 10, pointf.y - 10, pointf.x + 10, pointf.y + 10);
2158-
Feature feature = firstFeatureOnLayers(rectF);
2159-
if (feature != null && startDragging(feature, origin)) {
2160+
Pair<Feature, String> featureLayerPair = firstFeatureOnLayers(rectF);
2161+
if (featureLayerPair != null && featureLayerPair.first != null && startDragging(featureLayerPair.first, origin)) {
21602162
invokeFeatureDrag(pointf, "start");
21612163
return true;
21622164
}

maplibre_gl/ios/maplibre_gl/Sources/maplibre_gl/MapLibreMapController.swift

Lines changed: 47 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
7171
longPress.require(toFail: recognizer)
7272
}
7373
var longPressRecognizerAdded = false
74-
74+
7575
if let args = args as? [String: Any] {
76-
76+
7777
Convert.interpretMapLibreMapOptions(options: args["options"], delegate: self)
7878
if let initialCameraPosition = args["initialCameraPosition"] as? [String: Any],
7979
let camera = MLNMapCamera.fromDict(initialCameraPosition, mapView: mapView),
@@ -179,7 +179,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
179179
if let langStr = Locale.current.languageCode {
180180
setMapLanguage(language: langStr)
181181
}
182-
182+
183183
result(nil)
184184
case "map#updateContentInsets":
185185
guard let arguments = methodCall.arguments as? [String: Any] else { return }
@@ -325,7 +325,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
325325
case "camera#move":
326326
guard let arguments = methodCall.arguments as? [String: Any] else { return }
327327
guard let cameraUpdate = arguments["cameraUpdate"] as? [Any] else { return }
328-
328+
329329
if let camera = Convert.parseCameraUpdate(cameraUpdate: cameraUpdate, mapView: mapView) {
330330
mapView.setCamera(camera, animated: false)
331331
}
@@ -334,12 +334,12 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
334334
guard let arguments = methodCall.arguments as? [String: Any] else { return }
335335
guard let cameraUpdate = arguments["cameraUpdate"] as? [Any] else { return }
336336
guard let camera = Convert.parseCameraUpdate(cameraUpdate: cameraUpdate, mapView: mapView) else { return }
337-
338-
337+
338+
339339
let completion = {
340340
result(nil)
341341
}
342-
342+
343343
if let duration = arguments["duration"] as? TimeInterval {
344344
if let padding = Convert.parseLatLngBoundsPadding(cameraUpdate) {
345345
mapView.fly(to: camera, edgePadding: padding, withDuration: duration / 1000, completionHandler: completion)
@@ -546,7 +546,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
546546
properties: properties
547547
)
548548
result(nil)
549-
549+
550550
case "heatmapLayer#add":
551551
guard let arguments = methodCall.arguments as? [String: Any] else { return }
552552
guard let sourceId = arguments["sourceId"] as? String else { return }
@@ -849,11 +849,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
849849
}
850850
layer.isVisible = visible
851851
result(nil)
852-
852+
853853
case "map#querySourceFeatures":
854854
guard let arguments = methodCall.arguments as? [String: Any] else { return }
855855
guard let sourceId = arguments["sourceId"] as? String else { return }
856-
856+
857857
var sourceLayerId = Set<String>()
858858
if let layerId = arguments["sourceLayerId"] as? String {
859859
sourceLayerId.insert(layerId)
@@ -862,10 +862,10 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
862862
if let filter = arguments["filter"] as? [Any] {
863863
filterExpression = NSPredicate(mglJSONObject: filter)
864864
}
865-
865+
866866
var reply = [String: NSObject]()
867867
var features: [MLNFeature] = []
868-
868+
869869
guard let style = mapView.style else { return }
870870
if let source = style.source(withIdentifier: sourceId) {
871871
if let vectorSource = source as? MLNVectorTileSource {
@@ -874,7 +874,7 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
874874
features = shapeSource.features(matching: filterExpression)
875875
}
876876
}
877-
877+
878878
var featuresJson = [String]()
879879
for feature in features {
880880
let dictionary = feature.geoJSONDictionary()
@@ -892,11 +892,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
892892

893893
case "style#getLayerIds":
894894
var layerIds = [String]()
895-
895+
896896
guard let style = mapView.style else { return }
897-
897+
898898
style.layers.forEach { layer in layerIds.append(layer.identifier) }
899-
899+
900900
var reply = [String: NSObject]()
901901
reply["layers"] = layerIds as NSObject
902902
result(reply)
@@ -911,18 +911,18 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
911911
var reply = [String: NSObject]()
912912
reply["sources"] = sourceIds as NSObject
913913
result(reply)
914-
914+
915915
case "style#getFilter":
916916
guard let arguments = methodCall.arguments as? [String: Any] else { return }
917917
guard let layerId = arguments["layerId"] as? String else { return }
918-
918+
919919
guard let style = mapView.style else { return }
920920
guard let layer = style.layer(withIdentifier: layerId) else { return }
921-
921+
922922
var currentLayerFilter : String = ""
923923
if let vectorLayer = layer as? MLNVectorStyleLayer {
924924
if let layerFilter = vectorLayer.predicate {
925-
925+
926926
let jsonExpression = layerFilter.mgl_jsonExpressionObject
927927
if let data = try? JSONSerialization.data(withJSONObject: jsonExpression, options: []) {
928928
currentLayerFilter = String(data: data, encoding: String.Encoding.utf8) ?? ""
@@ -934,11 +934,11 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
934934
).flutterError)
935935
return;
936936
}
937-
937+
938938
var reply = [String: NSObject]()
939939
reply["filter"] = currentLayerFilter as NSObject
940940
result(reply)
941-
941+
942942
default:
943943
result(FlutterMethodNotImplemented)
944944
}
@@ -976,16 +976,16 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
976976
private func getCamera() -> MLNMapCamera? {
977977
return trackCameraPosition ? mapView.camera : nil
978978
}
979-
979+
980980
private func setMapLanguage(language: String) {
981981
self.mapView.setMapLanguage(language)
982982
}
983983

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

990990
// get layers in order (interactiveFeatureLayerIds is unordered)
991991
let clickableLayers = style.layers.filter { layer in
@@ -998,10 +998,10 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
998998
styleLayerIdentifiers: [layer.identifier]
999999
)
10001000
if let feature = features.first {
1001-
return feature
1001+
return (feature, layer.identifier)
10021002
}
10031003
}
1004-
return nil
1004+
return (nil, nil)
10051005
}
10061006

10071007
/*
@@ -1013,13 +1013,15 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
10131013
let point = sender.location(in: mapView)
10141014
let coordinate = mapView.convert(point, toCoordinateFrom: mapView)
10151015

1016-
if let feature = firstFeatureOnLayers(at: point) {
1016+
let result = firstFeatureOnLayers(at: point)
1017+
if let feature = result.feature {
10171018
channel?.invokeMethod("feature#onTap", arguments: [
10181019
"id": feature.identifier,
10191020
"x": point.x,
10201021
"y": point.y,
10211022
"lng": coordinate.longitude,
10221023
"lat": coordinate.latitude,
1024+
"layerId": result.layerId,
10231025
])
10241026
} else {
10251027
channel?.invokeMethod("map#onMapClick", arguments: [
@@ -1062,22 +1064,23 @@ class MapLibreMapController: NSObject, FlutterPlatformView, MLNMapViewDelegate,
10621064
let point = sender.location(in: mapView)
10631065
let coordinate = mapView.convert(point, toCoordinateFrom: mapView)
10641066

1065-
if dragFeature == nil, began, sender.numberOfTouches == 1,
1066-
let feature = firstFeatureOnLayers(at: point),
1067-
let draggable = feature.attribute(forKey: "draggable") as? Bool,
1068-
draggable
1069-
{
1070-
sender.state = UIGestureRecognizer.State.began
1071-
dragFeature = feature
1072-
originDragCoordinate = coordinate
1073-
previousDragCoordinate = coordinate
1074-
mapView.allowsScrolling = false
1075-
let eventType = "start"
1076-
invokeFeatureDrag(point, coordinate, eventType)
1077-
for gestureRecognizer in mapView.gestureRecognizers! {
1078-
if let _ = gestureRecognizer as? UIPanGestureRecognizer {
1079-
gestureRecognizer.addTarget(self, action: #selector(handleMapPan))
1080-
break
1067+
if dragFeature == nil, began, sender.numberOfTouches == 1 {
1068+
let result = firstFeatureOnLayers(at: point)
1069+
if let feature = result.feature,
1070+
let draggable = feature.attribute(forKey: "draggable") as? Bool,
1071+
draggable {
1072+
sender.state = UIGestureRecognizer.State.began
1073+
dragFeature = feature
1074+
originDragCoordinate = coordinate
1075+
previousDragCoordinate = coordinate
1076+
mapView.allowsScrolling = false
1077+
let eventType = "start"
1078+
invokeFeatureDrag(point, coordinate, eventType)
1079+
for gestureRecognizer in mapView.gestureRecognizers! {
1080+
if let _ = gestureRecognizer as? UIPanGestureRecognizer {
1081+
gestureRecognizer.addTarget(self, action: #selector(handleMapPan))
1082+
break
1083+
}
10811084
}
10821085
}
10831086
}

maplibre_gl/lib/src/annotation_manager.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ abstract class AnnotationManager<T extends Annotation> {
5555
}
5656
}
5757

58-
_onFeatureTapped(dynamic id, Point<double> point, LatLng coordinates) {
58+
_onFeatureTapped(
59+
dynamic id, Point<double> point, LatLng coordinates, String layerId) {
5960
final annotation = _idToAnnotation[id];
6061
if (annotation != null) {
6162
onTap!(annotation);

maplibre_gl/lib/src/controller.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ typedef OnMapClickCallback = void Function(
88
Point<double> point, LatLng coordinates);
99

1010
typedef OnFeatureInteractionCallback = void Function(
11-
dynamic id, Point<double> point, LatLng coordinates);
11+
dynamic id, Point<double> point, LatLng coordinates, String layerId);
1212

1313
typedef OnFeatureDragnCallback = void Function(dynamic id,
1414
{required Point<double> point,
@@ -93,7 +93,8 @@ class MapLibreMapController extends ChangeNotifier {
9393
_maplibrePlatform.onFeatureTappedPlatform.add((payload) {
9494
for (final fun
9595
in List<OnFeatureInteractionCallback>.from(onFeatureTapped)) {
96-
fun(payload["id"], payload["point"], payload["latLng"]);
96+
fun(payload["id"], payload["point"], payload["latLng"],
97+
payload["layerId"]);
9798
}
9899
});
99100

maplibre_gl_platform_interface/lib/src/method_channel_maplibre_gl.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@ class MapLibreMethodChannel extends MapLibrePlatform {
1818
final double y = call.arguments['y'];
1919
final double lng = call.arguments['lng'];
2020
final double lat = call.arguments['lat'];
21+
final String layerId = call.arguments['layerId'];
2122
onFeatureTappedPlatform({
2223
'id': id,
2324
'point': Point<double>(x, y),
24-
'latLng': LatLng(lat, lng)
25+
'latLng': LatLng(lat, lng),
26+
'layerId': layerId
2527
});
2628
case 'feature#onDrag':
2729
final id = call.arguments['id'];

0 commit comments

Comments
 (0)