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

Implement layer filtering #997

Merged
merged 1 commit into from
Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from all 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
43 changes: 43 additions & 0 deletions android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.mapbox.android.core.location.LocationEngine;
import com.mapbox.android.core.location.LocationEngineCallback;
import com.mapbox.android.core.location.LocationEngineProvider;
Expand Down Expand Up @@ -59,7 +60,9 @@
import com.mapbox.mapboxsdk.plugins.localization.LocalizationPlugin;
import com.mapbox.mapboxsdk.style.expressions.Expression;
import com.mapbox.mapboxsdk.style.layers.CircleLayer;
import com.mapbox.mapboxsdk.style.layers.FillExtrusionLayer;
import com.mapbox.mapboxsdk.style.layers.FillLayer;
import com.mapbox.mapboxsdk.style.layers.HeatmapLayer;
import com.mapbox.mapboxsdk.style.layers.HillshadeLayer;
import com.mapbox.mapboxsdk.style.layers.Layer;
import com.mapbox.mapboxsdk.style.layers.LineLayer;
Expand Down Expand Up @@ -1107,6 +1110,46 @@ public void onFailure(@NonNull Exception exception) {
style.removeLayer(layerId);
interactiveFeatureLayerIds.remove(layerId);

result.success(null);
break;
}
case "style#setFilter":
{
if (style == null) {
result.error(
"STYLE IS NULL",
"The style is null. Has onStyleLoaded() already been invoked?",
null);
}
String layerId = call.argument("layerId");
String filter = call.argument("filter");

Layer layer = style.getLayer(layerId);

JsonParser parser = new JsonParser();
JsonElement jsonElement = parser.parse(filter);
Expression expression = Expression.Converter.convert(jsonElement);

if (layer instanceof CircleLayer) {
((CircleLayer) layer).setFilter(expression);
} else if (layer instanceof FillExtrusionLayer) {
((FillExtrusionLayer) layer).setFilter(expression);
} else if (layer instanceof FillLayer) {
((FillLayer) layer).setFilter(expression);
} else if (layer instanceof HeatmapLayer) {
((HeatmapLayer) layer).setFilter(expression);
} else if (layer instanceof LineLayer) {
((LineLayer) layer).setFilter(expression);
} else if (layer instanceof SymbolLayer) {
((SymbolLayer) layer).setFilter(expression);
} else {
result.error(
"INVALID LAYER TYPE",
String.format("Layer '%s' does not support filtering.", layerId),
null);
break;
}

result.success(null);
break;
}
Expand Down
25 changes: 17 additions & 8 deletions example/lib/layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ class LayerState extends State {
static final LatLng center = const LatLng(-33.86711, 151.1947171);

late MapboxMapController controller;
Timer? timer;
Timer? bikeTimer;
Timer? filterTimer;
int filteredId = 0;

@override
Widget build(BuildContext context) {
Expand Down Expand Up @@ -136,15 +138,22 @@ class LayerState extends State {
),
minzoom: 11,
);
timer = Timer.periodic(
Duration(milliseconds: 10),
(t) => controller.setGeoJsonSource(
"moving", _movingFeature(t.tick / 2000)));

bikeTimer = Timer.periodic(Duration(milliseconds: 10), (t) {
controller.setGeoJsonSource("moving", _movingFeature(t.tick / 2000));
});

controller.setFilter('fills', ['==', 'id', filteredId]);
filterTimer = Timer.periodic(Duration(seconds: 3), (t) {
filteredId = filteredId == 0 ? 1 : 0;
controller.setFilter('fills', ['==', 'id', filteredId]);
});
}

@override
void dispose() {
timer?.cancel();
bikeTimer?.cancel();
filterTimer?.cancel();
super.dispose();
}
}
Expand Down Expand Up @@ -186,7 +195,7 @@ final _fills = {
{
"type": "Feature",
"id": 0, // web currently only supports number ids
"properties": <String, dynamic>{},
"properties": <String, dynamic>{'id': 0},
"geometry": {
"type": "Polygon",
"coordinates": [
Expand All @@ -211,7 +220,7 @@ final _fills = {
{
"type": "Feature",
"id": 1,
"properties": <String, dynamic>{},
"properties": <String, dynamic>{'id': 1},
"geometry": {
"type": "Polygon",
"coordinates": [
Expand Down
34 changes: 34 additions & 0 deletions ios/Classes/MapboxMapController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,40 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma
mapView.style?.removeLayer(layer)
result(nil)

case "style#setFilter":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let layerId = arguments["layerId"] as? String else { return }
guard let filter = arguments["filter"] as? String else { return }
guard let layer = mapView.style?.layer(withIdentifier: layerId) else {
result(nil)
return
}

do {
let filter = try JSONSerialization.jsonObject(
with: filter.data(using: .utf8)!,
options: .fragmentsAllowed
)
let predicate = NSPredicate(mglJSONObject: filter)
if let layer = layer as? MGLVectorStyleLayer {
layer.predicate = predicate
} else {
result(FlutterError(
code: "invalidLayerType",
message: "Invalid layer type",
details: "Layer '\(layerId)' does not support filtering."
))
return
}
result(nil)
} catch {
result(FlutterError(
code: "invalidExpression",
message: "Invalid filter expression",
details: "Could not parse filter expression."
))
}

case "source#addGeoJson":
guard let arguments = methodCall.arguments as? [String: Any] else { return }
guard let sourceId = arguments["sourceId"] as? String else { return }
Expand Down
4 changes: 4 additions & 0 deletions lib/src/controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1056,6 +1056,10 @@ class MapboxMapController extends ChangeNotifier {
return _mapboxGlPlatform.removeLayer(layerId);
}

Future<void> setFilter(String layerId, dynamic filter) {
return _mapboxGlPlatform.setFilter(layerId, filter);
}

/// Returns the point on the screen that corresponds to a geographical coordinate ([latLng]). The screen location is in screen pixels (not display pixels) relative to the top left of the map (not of the whole screen)
///
/// Note: The resulting x and y coordinates are rounded to [int] on web, on other platforms they may differ very slightly (in the range of about 10^-10) from the actual nearest screen coordinate.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ abstract class MapboxGlPlatform {

Future<void> removeLayer(String imageLayerId);

Future<void> setFilter(String layerId, dynamic filter);

Future<Point> toScreenLocation(LatLng latLng);

Future<List<Point>> toScreenLocationBatch(Iterable<LatLng> latLngs);
Expand Down
10 changes: 10 additions & 0 deletions mapbox_gl_platform_interface/lib/src/method_channel_mapbox_gl.dart
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,16 @@ class MethodChannelMapboxGl extends MapboxGlPlatform {
}
}

@override
Future<void> setFilter(String layerId, dynamic filter) async {
try {
return await _channel.invokeMethod('style#setFilter',
<String, Object>{'layerId': layerId, 'filter': jsonEncode(filter)});
} on PlatformException catch (e) {
return new Future.error(e);
}
}

@override
Future<LatLng> toLatLng(Point screenLocation) async {
try {
Expand Down
5 changes: 5 additions & 0 deletions mapbox_gl_web/lib/src/mapbox_web_gl_platform.dart
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,11 @@ class MapboxWebGlPlatform extends MapboxGlPlatform
_map.removeLayer(layerId);
}

@override
Future<void> setFilter(String layerId, dynamic filter) async {
_map.setFilter(layerId, filter);
}

@override
Future<void> addGeoJsonSource(String sourceId, Map<String, dynamic> geojson,
{String? promoteId}) async {
Expand Down