Skip to content
Draft
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
30 changes: 22 additions & 8 deletions packages/maplibre_android/lib/src/map_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -528,14 +528,28 @@ final class MapLibreMapStateAndroid extends MapLibreMapState
final gson = jni.Gson();
return features
.map(
(feature) => RenderedFeature(
id: feature.id()?.toDartString(releaseOriginal: true),
properties:
jsonDecode(
gson.toJson(feature.properties())?.toString() ?? '{}',
)
as Map<String, Object?>,
),
(feature) {
Map<String, Object?>? geometry;
try {
final geomJson =
gson.toJson(feature.geometry())?.toString();
if (geomJson != null && geomJson.isNotEmpty) {
final decoded = jsonDecode(geomJson);
if (decoded is Map<String, Object?>) {
geometry = decoded;
}
}
} catch (_) {}
return RenderedFeature(
id: feature.id()?.toDartString(releaseOriginal: true),
properties:
jsonDecode(
gson.toJson(feature.properties())?.toString() ?? '{}',
)
as Map<String, Object?>,
geometry: geometry,
);
},
)
.toList(growable: false);
}
Expand Down
56 changes: 56 additions & 0 deletions packages/maplibre_android/lib/src/style_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,62 @@ class StyleControllerAndroid extends StyleController {
_jStyle.addImage(jId, jBitmap);
});

@override
Future<void> setFilter(String layerId, List<Object>? filter) async =>
using((arena) {
final jId = layerId.toJString()..releasedBy(arena);

final jni.Expression jExpr;
if (filter == null) {
jExpr = jni.Expression$Converter.convert$2(
'["all"]'.toJString()..releasedBy(arena),
)!
..releasedBy(arena);
} else {
final parsed = jni.Expression$Converter.convert$2(
jsonEncode(filter).toJString()..releasedBy(arena),
);
if (parsed == null) {
throw Exception('Invalid filter expression: $filter');
}
jExpr = parsed..releasedBy(arena);
}

// getLayerAs로 각 타입 시도 (null이면 타입 불일치)
final symbol = _jStyle.getLayerAs(jId, T: jni.SymbolLayer.type);
if (symbol != null) {
symbol.setFilter(jExpr);
return;
}
final fill = _jStyle.getLayerAs(jId, T: jni.FillLayer.type);
if (fill != null) {
fill.setFilter(jExpr);
return;
}
final line = _jStyle.getLayerAs(jId, T: jni.LineLayer.type);
if (line != null) {
line.setFilter(jExpr);
return;
}
final circle = _jStyle.getLayerAs(jId, T: jni.CircleLayer.type);
if (circle != null) {
circle.setFilter(jExpr);
return;
}
final fillExt =
_jStyle.getLayerAs(jId, T: jni.FillExtrusionLayer.type);
if (fillExt != null) {
fillExt.setFilter(jExpr);
return;
}
final heatmap = _jStyle.getLayerAs(jId, T: jni.HeatmapLayer.type);
if (heatmap != null) {
heatmap.setFilter(jExpr);
return;
}
throw Exception('Layer "$layerId" does not exist or support filters.');
});

@override
Future<void> removeImage(String id) async =>
_jStyle.removeImage(id.toJString());
Expand Down
20 changes: 16 additions & 4 deletions packages/maplibre_ios/lib/src/extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,10 +142,22 @@ NSExpression? parseNSExpression(String propertyName, String json) =>
/// Internal extensions on [MLNFeature].
extension MLNFeatureExt on MLNFeature {
/// Convert a [MLNFeature] to a [RenderedFeature].
RenderedFeature toRenderedFeature() => RenderedFeature(
id: identifier == null ? null : toDartObject(identifier!),
properties: attributes.toDartMap().map((k, v) => MapEntry(k.toString(), v)),
);
RenderedFeature toRenderedFeature() {
Map<String, Object?>? geometry;
try {
final coord = coordinate;
geometry = {
'type': 'Point',
'coordinates': [coord.longitude, coord.latitude],
};
} catch (_) {}
return RenderedFeature(
id: identifier == null ? null : toDartObject(identifier!),
properties:
attributes.toDartMap().map((k, v) => MapEntry(k.toString(), v)),
geometry: geometry,
);
}
}

/// Internal extensions on [MLNStyleLayer].
Expand Down
18 changes: 18 additions & 0 deletions packages/maplibre_ios/lib/src/style_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,24 @@ class StyleControllerIos extends StyleController {
return layers.map((l) => l.identifier.toDartString()).toList();
}

@override
Future<void> setFilter(String layerId, List<Object>? filter) async {
final ffiLayer = _ffiStyle.layerWithIdentifier(layerId.toNSString());
if (ffiLayer == null) {
throw Exception('Layer "$layerId" does not exist.');
}
if (!MLNVectorStyleLayer.isA(ffiLayer)) {
throw Exception('Layer "$layerId" does not support filters.');
}
final vectorLayer = MLNVectorStyleLayer.as(ffiLayer);
if (filter == null) {
vectorLayer.predicate = null;
} else {
final expression = jsonEncode(filter).toNSString();
vectorLayer.predicate = Helpers.parsePredicateWithRaw(expression);
}
}

@override
Future<void> removeImage(String id) async {
final ffiId = id.toNSString();
Expand Down
14 changes: 13 additions & 1 deletion packages/maplibre_platform_interface/lib/src/queried_layer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ import 'package:maplibre_platform_interface/maplibre_platform_interface.dart';
@immutable
class RenderedFeature {
/// Create a new [RenderedFeature].
const RenderedFeature({required this.id, required this.properties});
const RenderedFeature({
required this.id,
required this.properties,
this.geometry,
});

/// If present, an object uniquely identifying the feature in the vector
/// source. May be either a string or an integer.
Expand All @@ -30,6 +34,14 @@ class RenderedFeature {
/// The properties of the feature as provided by its source.
final Map<String, Object?> properties;

/// The GeoJSON geometry of the feature, if available.
///
/// For point features, this is typically:
/// ```json
/// {"type": "Point", "coordinates": [lon, lat]}
/// ```
final Map<String, Object?>? geometry;

@override
String toString() => 'RenderedFeature(id: $id, properties: $properties)';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,15 @@ abstract class StyleController {
await addImage(id, bytes.buffer.asUint8List());
}

/// Set or clear the filter expression on an existing layer.
///
/// [layerId] The ID of the layer to update.
/// [filter] A MapLibre filter expression (e.g. `['!=', ['\$id'], 42]`).
/// Pass `null` to clear the filter.
///
/// Only supported for vector style layers (symbol, fill, line, circle, etc.).
Future<void> setFilter(String layerId, List<Object>? filter);

/// Removes an image from the map
Future<void> removeImage(String id);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ part of 'interop.dart';
extension type GeoJSONFeature._(JSObject _) implements JSObject {
external JSAny? id;
external JSObject properties;
external JSObject? geometry;
}

/// https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/MapGeoJSONFeature/
Expand Down
2 changes: 2 additions & 0 deletions packages/maplibre_web/lib/src/map_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,7 @@ final class MapLibreMapStateWeb extends MapLibreMapState {
(f) => RenderedFeature(
id: f.id.dartify(),
properties: f.properties.asStringMap() ?? {},
geometry: f.geometry?.asStringMap(),
),
)
.toList(growable: false);
Expand All @@ -458,6 +459,7 @@ final class MapLibreMapStateWeb extends MapLibreMapState {
(f) => RenderedFeature(
id: f.id.dartify(),
properties: f.properties.asStringMap() ?? {},
geometry: f.geometry?.asStringMap(),
),
)
.toList(growable: false);
Expand Down
Loading