Skip to content

Commit 05bfb44

Browse files
authored
feat: apply maxBounds, throw exception if layer or source id already exists in style (#389)
- [x] android - [x] ios - [x] web
1 parent c3f533c commit 05bfb44

File tree

9 files changed

+198
-118
lines changed

9 files changed

+198
-118
lines changed

example/macos/RunnerTests/RunnerTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import Cocoa
22
import FlutterMacOS
33
import XCTest
44

5+
@testable import maplibre
6+
57
// If your plugin has been explicitly set to "type: .dynamic" in the Package.swift,
68
// you will need to add your plugin as a dependency of RunnerTests within Xcode.
79

8-
@testable import maplibre
9-
1010
// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
1111
//
1212
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.

lib/src/platform/android/map_state.dart

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,9 @@ final class MapLibreMapStateAndroid extends MapLibreMapStateNative
236236
}),
237237
),
238238
),
239+
)
240+
..setLatLngBoundsForCameraTarget(
241+
options.maxBounds?.toJLatLngBounds(arena: arena),
239242
);
240243
setStyle(options.initStyle);
241244
widget.onEvent?.call(MapEventMapCreated(mapController: this));
@@ -279,8 +282,7 @@ final class MapLibreMapStateAndroid extends MapLibreMapStateNative
279282
final oldBounds = oldOptions.maxBounds;
280283
final newBounds = options.maxBounds;
281284
if (oldBounds != null && newBounds == null) {
282-
// TODO @Nullable latLngBounds, https://github.com/dart-lang/native/issues/1644
283-
// _jMapLibreMap.setLatLngBoundsForCameraTarget(null);
285+
jMap.setLatLngBoundsForCameraTarget(null);
284286
} else if ((oldBounds == null && newBounds != null) ||
285287
(newBounds != null && oldBounds != newBounds)) {
286288
final bounds = newBounds.toJLatLngBounds(arena: arena);
@@ -294,7 +296,7 @@ final class MapLibreMapStateAndroid extends MapLibreMapStateNative
294296
}
295297
// TODO: pan is not handled, there is no setPanGestureEnabled on Android.
296298
/*if (options.gestures.pan != oldOptions.gestures.pan) {
297-
uiSettings.setRotateGesturesEnabled(options.gestures.pan);
299+
uiSettings.setPanGesturesEnabled(options.gestures.pan);
298300
}*/
299301
if (options.gestures.zoom != oldOptions.gestures.zoom) {
300302
uiSettings.setZoomGesturesEnabled(options.gestures.zoom);

lib/src/platform/android/style_controller.dart

Lines changed: 92 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -7,87 +7,103 @@ class StyleControllerAndroid implements StyleController {
77
final jni.Style _jStyle;
88

99
@override
10-
Future<void> addLayer(StyleLayer layer, {String? belowLayerId}) async =>
11-
using((arena) {
12-
final iId = layer.id.toJString()..releasedBy(arena);
13-
final jLayer = switch (layer) {
14-
FillStyleLayer() => jni.FillLayer(
15-
iId,
16-
layer.sourceId.toJString()..releasedBy(arena),
17-
),
18-
CircleStyleLayer() => jni.CircleLayer(
19-
iId,
20-
layer.sourceId.toJString()..releasedBy(arena),
21-
),
22-
BackgroundStyleLayer() => jni.BackgroundLayer(layer.id.toJString()),
23-
FillExtrusionStyleLayer() => jni.FillExtrusionLayer(
24-
iId,
25-
layer.sourceId.toJString()..releasedBy(arena),
26-
),
27-
HeatmapStyleLayer() => jni.HeatmapLayer(
28-
iId,
29-
layer.sourceId.toJString()..releasedBy(arena),
30-
),
31-
HillshadeStyleLayer() => jni.HillshadeLayer(
32-
iId,
33-
layer.sourceId.toJString()..releasedBy(arena),
34-
),
35-
LineStyleLayer() => jni.LineLayer(
36-
iId,
37-
layer.sourceId.toJString()..releasedBy(arena),
38-
),
39-
RasterStyleLayer() => jni.RasterLayer(
40-
iId,
41-
layer.sourceId.toJString()..releasedBy(arena),
42-
),
43-
SymbolStyleLayer() => jni.SymbolLayer(
44-
iId,
45-
layer.sourceId.toJString()..releasedBy(arena),
46-
),
47-
_ => throw UnimplementedError(
48-
'The Layer is not supported: ${layer.runtimeType}',
49-
),
50-
};
51-
jLayer.setMinZoom(layer.minZoom);
52-
jLayer.setMaxZoom(layer.maxZoom);
53-
54-
// paint and layout properties
55-
final layoutEntries = layer.layout.entries.toList(growable: false);
56-
final paintEntries = layer.paint.entries.toList(growable: false);
57-
final props = JArray(
58-
jni.PropertyValue.nullableType(JObject.nullableType),
59-
layoutEntries.length + paintEntries.length,
60-
)..releasedBy(arena);
61-
for (var i = 0; i < paintEntries.length; i++) {
62-
final entry = paintEntries[i];
63-
props[i] = jni.PaintPropertyValue(
64-
entry.key.toJString(),
65-
entry.value.toJObject(arena),
66-
T: JObject.type,
67-
)..releasedBy(arena);
68-
}
69-
for (var i = 0; i < layoutEntries.length; i++) {
70-
final entry = layoutEntries[i];
71-
props[paintEntries.length + i] = jni.LayoutPropertyValue(
72-
entry.key.toJString(),
73-
entry.value.toJObject(arena),
74-
T: JObject.type,
75-
)..releasedBy(arena);
76-
}
77-
jLayer.releasedBy(arena);
78-
jLayer.setProperties(props);
10+
Future<void> addLayer(
11+
StyleLayer layer, {
12+
String? belowLayerId,
13+
}) async => using((arena) {
14+
final jId = layer.id.toJString()..releasedBy(arena);
15+
final prevLayer = _jStyle.getLayer(jId);
16+
if (prevLayer != null) {
17+
throw Exception(
18+
'A Layer with the id "${layer.id}" already exists in the map style.',
19+
);
20+
}
7921

80-
// add to style
81-
if (belowLayerId case final String belowId) {
82-
_jStyle.addLayerBelow(jLayer, belowId.toJString()..releasedBy(arena));
83-
} else {
84-
_jStyle.addLayer(jLayer);
85-
}
86-
});
22+
final jLayer = switch (layer) {
23+
FillStyleLayer() => jni.FillLayer(
24+
jId,
25+
layer.sourceId.toJString()..releasedBy(arena),
26+
),
27+
CircleStyleLayer() => jni.CircleLayer(
28+
jId,
29+
layer.sourceId.toJString()..releasedBy(arena),
30+
),
31+
BackgroundStyleLayer() => jni.BackgroundLayer(layer.id.toJString()),
32+
FillExtrusionStyleLayer() => jni.FillExtrusionLayer(
33+
jId,
34+
layer.sourceId.toJString()..releasedBy(arena),
35+
),
36+
HeatmapStyleLayer() => jni.HeatmapLayer(
37+
jId,
38+
layer.sourceId.toJString()..releasedBy(arena),
39+
),
40+
HillshadeStyleLayer() => jni.HillshadeLayer(
41+
jId,
42+
layer.sourceId.toJString()..releasedBy(arena),
43+
),
44+
LineStyleLayer() => jni.LineLayer(
45+
jId,
46+
layer.sourceId.toJString()..releasedBy(arena),
47+
),
48+
RasterStyleLayer() => jni.RasterLayer(
49+
jId,
50+
layer.sourceId.toJString()..releasedBy(arena),
51+
),
52+
SymbolStyleLayer() => jni.SymbolLayer(
53+
jId,
54+
layer.sourceId.toJString()..releasedBy(arena),
55+
),
56+
_ => throw UnimplementedError(
57+
'The Layer is not supported: ${layer.runtimeType}',
58+
),
59+
};
60+
jLayer.setMinZoom(layer.minZoom);
61+
jLayer.setMaxZoom(layer.maxZoom);
62+
63+
// paint and layout properties
64+
final layoutEntries = layer.layout.entries.toList(growable: false);
65+
final paintEntries = layer.paint.entries.toList(growable: false);
66+
final props = JArray(
67+
jni.PropertyValue.nullableType(JObject.nullableType),
68+
layoutEntries.length + paintEntries.length,
69+
)..releasedBy(arena);
70+
for (var i = 0; i < paintEntries.length; i++) {
71+
final entry = paintEntries[i];
72+
props[i] = jni.PaintPropertyValue(
73+
entry.key.toJString(),
74+
entry.value.toJObject(arena),
75+
T: JObject.type,
76+
)..releasedBy(arena);
77+
}
78+
for (var i = 0; i < layoutEntries.length; i++) {
79+
final entry = layoutEntries[i];
80+
props[paintEntries.length + i] = jni.LayoutPropertyValue(
81+
entry.key.toJString(),
82+
entry.value.toJObject(arena),
83+
T: JObject.type,
84+
)..releasedBy(arena);
85+
}
86+
jLayer.releasedBy(arena);
87+
jLayer.setProperties(props);
88+
89+
// add to style
90+
if (belowLayerId case final String belowId) {
91+
_jStyle.addLayerBelow(jLayer, belowId.toJString()..releasedBy(arena));
92+
} else {
93+
_jStyle.addLayer(jLayer);
94+
}
95+
});
8796

8897
@override
8998
Future<void> addSource(Source source) async => using((arena) {
9099
final jId = source.id.toJString()..releasedBy(arena);
100+
final prevSource = _jStyle.getSource(jId);
101+
if (prevSource != null) {
102+
throw Exception(
103+
'A Source with the id "${source.id}" already exists in the map style.',
104+
);
105+
}
106+
91107
final jni.Source jSource;
92108
switch (source) {
93109
case GeoJsonSource():

lib/src/platform/ios/map_state.dart

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,7 @@ final class MapLibreMapStateIos extends MapLibreMapStateNative
100100
bool webLinear = false,
101101
EdgeInsets padding = EdgeInsets.zero,
102102
}) async {
103-
final sw = Struct.create<CLLocationCoordinate2D>()
104-
..longitude = bounds.longitudeWest
105-
..latitude = bounds.latitudeSouth;
106-
final ne = Struct.create<CLLocationCoordinate2D>()
107-
..longitude = bounds.longitudeEast
108-
..latitude = bounds.latitudeNorth;
109-
final ffiBounds = Struct.create<MLNCoordinateBounds>()
110-
..sw = sw
111-
..ne = ne;
103+
final ffiBounds = bounds.toMLNCoordinateBounds();
112104
// TODO support padding with Struct UIEdgeInsets
113105
_mapView.setVisibleCoordinateBounds(ffiBounds, animated: true);
114106
}

0 commit comments

Comments
 (0)