diff --git a/.gitignore b/.gitignore index 9ff60d998..37527c234 100644 --- a/.gitignore +++ b/.gitignore @@ -42,7 +42,10 @@ Package.resolved .vscode ios/maplibre/.build +packages/maplibre_ios/example/ios/Flutter/flutter_export_environment.sh +packages/maplibre_ios/example/ios/Flutter/Generated.xcconfig + # Zensical website site/ .venv/ -.cache/ \ No newline at end of file +.cache/ diff --git a/examples/integration_test/controller_test.dart b/examples/integration_test/controller_test.dart index 5d9f864dd..9a09c4b94 100644 --- a/examples/integration_test/controller_test.dart +++ b/examples/integration_test/controller_test.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart' show rootBundle; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:maplibre/maplibre.dart'; @@ -241,7 +241,7 @@ void test() { // ensure no crash if a layer does not exist await ctrl.style?.removeLayer('notExisting'); - const layer = RasterStyleLayer(id: 'rasterLayer', sourceId: 'source'); + final layer = RasterStyleLayer(id: 'rasterLayer', sourceId: 'source'); await ctrl.style?.addLayer(layer); await ctrl.style?.removeLayer(layer.id); }); @@ -321,10 +321,10 @@ void test() { ); const pointLayerId = 'point_layer'; await style.addLayer( - const CircleStyleLayer( + CircleStyleLayer( id: pointLayerId, sourceId: pointSourceId, - paint: {'circle-radius': 10, 'circle-color': '#FF0000'}, + radius: const PropertyValue.value(10), ), ); const expectedPoint = QueriedLayer( @@ -332,7 +332,7 @@ void test() { sourceId: pointSourceId, sourceLayer: null, ); - await tester.pump(const Duration(seconds: 2)); + await tester.pumpAndSettle(const Duration(seconds: 2)); final size = tester.getSize(find.byType(MapLibreMap)); final centerScreen = Offset(size.width / 2, size.height / 2); @@ -366,10 +366,10 @@ void test() { ); const polygonLayerId = 'polygon_layer'; await style.addLayer( - const FillStyleLayer( + FillStyleLayer( id: polygonLayerId, sourceId: polygonSourceId, - paint: {'fill-color': '#00FF00'}, + color: const PropertyValue.value(Colors.green), ), ); const expectedPolygon = QueriedLayer( @@ -443,11 +443,7 @@ void test() { ); const pointLayerId = 'point_layer'; await style.addLayer( - const CircleStyleLayer( - id: pointLayerId, - sourceId: pointSourceId, - paint: {'circle-radius': 5, 'circle-color': '#FF0000'}, - ), + CircleStyleLayer(id: pointLayerId, sourceId: pointSourceId), ); const polygonSourceId = 'polygon_source'; await style.addSource( @@ -472,10 +468,10 @@ void test() { ); const polygonLayerId = 'polygon_layer'; await style.addLayer( - const FillStyleLayer( + FillStyleLayer( id: polygonLayerId, sourceId: polygonSourceId, - paint: {'fill-color': '#00FF00'}, + color: const PropertyValue.value(Colors.green), ), ); await tester.pump(const Duration(seconds: 1)); @@ -508,10 +504,10 @@ void test() { expect(features.first.properties['poly'], 'gon'); const pointLayer2Id = 'point_layer_2'; await style.addLayer( - const CircleStyleLayer( + CircleStyleLayer( id: pointLayer2Id, sourceId: pointSourceId, - paint: {'circle-radius': 5, 'circle-color': '#FF00FF'}, + color: const PropertyValue.value(Colors.purple), ), ); await tester.pump(const Duration(seconds: 1)); @@ -556,11 +552,7 @@ void test() { ); const pointLayerId = 'point_layer'; await style.addLayer( - const CircleStyleLayer( - id: pointLayerId, - sourceId: pointSourceId, - paint: {'circle-radius': 5, 'circle-color': '#FF0000'}, - ), + CircleStyleLayer(id: pointLayerId, sourceId: pointSourceId), ); const lineSourceId = 'line_source'; await style.addSource( @@ -580,10 +572,10 @@ void test() { ); const lineLayerId = 'line_layer'; await style.addLayer( - const LineStyleLayer( + LineStyleLayer( id: lineLayerId, sourceId: lineSourceId, - paint: {'line-color': '#0000FF', 'line-width': 5}, + color: const PropertyValue.value(Colors.blue), ), ); const polygonSourceId = 'polygon_source'; @@ -609,10 +601,10 @@ void test() { ); const polygonLayerId = 'polygon_layer'; await style.addLayer( - const FillStyleLayer( + FillStyleLayer( id: polygonLayerId, sourceId: polygonSourceId, - paint: {'fill-color': '#00FF00'}, + color: const PropertyValue.value(Colors.green), ), ); await tester.pump(const Duration(seconds: 1)); @@ -831,7 +823,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = BackgroundStyleLayer(id: '1', color: Colors.black); + final layer = BackgroundStyleLayer(id: '1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -840,7 +832,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = FillStyleLayer(id: '1', sourceId: 'source1'); + final layer = FillStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -849,7 +841,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = CircleStyleLayer(id: '1', sourceId: 'source1'); + final layer = CircleStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -858,7 +850,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = FillExtrusionStyleLayer(id: '1', sourceId: 'source1'); + final layer = FillExtrusionStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -867,7 +859,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = HeatmapStyleLayer(id: '1', sourceId: 'source1'); + final layer = HeatmapStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -876,7 +868,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = HillshadeStyleLayer(id: '1', sourceId: 'source1'); + final layer = HillshadeStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -885,7 +877,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = LineStyleLayer(id: '1', sourceId: 'source1'); + final layer = LineStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -894,7 +886,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = RasterStyleLayer(id: '1', sourceId: 'source1'); + final layer = RasterStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -903,7 +895,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = SymbolStyleLayer(id: '1', sourceId: 'source1'); + final layer = SymbolStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); @@ -912,7 +904,7 @@ void test() { final app = App(onMapCreated: ctrlCompleter.complete); await tester.pumpWidget(app); final ctrl = await ctrlCompleter.future; - const layer = SymbolStyleLayer(id: '1', sourceId: 'source1'); + final layer = SymbolStyleLayer(id: '1', sourceId: 'source1'); await ctrl.style?.addLayer(layer); await tester.pumpAndSettle(); }); diff --git a/examples/lib/animation_page.dart b/examples/lib/animation_page.dart index 2072fe38d..78e4e18ad 100644 --- a/examples/lib/animation_page.dart +++ b/examples/lib/animation_page.dart @@ -51,10 +51,11 @@ class _AnimationPageState extends State { GeoJsonSource(id: _sourceId, data: collection.toText()), ); await style.addLayer( - const LineStyleLayer( + LineStyleLayer( id: 'geojson-line', sourceId: _sourceId, - paint: {'line-color': '#F00', 'line-width': 3}, + color: const PropertyValue.value(Colors.red), + width: const PropertyValue.value(3), ), ); diff --git a/examples/lib/features_query_page.dart b/examples/lib/features_query_page.dart index e446dfdbf..37a9a9a67 100644 --- a/examples/lib/features_query_page.dart +++ b/examples/lib/features_query_page.dart @@ -114,18 +114,20 @@ class _FeaturesQueryPageState extends State { ); await style.addLayer( - const FillStyleLayer( + FillStyleLayer( id: 'polygons', sourceId: 'polygons', - paint: {'fill-color': '#429ef5', 'fill-opacity': 0.6}, + color: const PropertyValue.value(Color(0xFF429EF5)), + opacity: const PropertyValue.value(0.6), ), ); await style.addLayer( - const CircleStyleLayer( + CircleStyleLayer( id: 'points', sourceId: 'points', - paint: {'circle-color': '#f54242', 'circle-radius': 8}, + color: const PropertyValue.value(Color(0xFFF54242)), + radius: const PropertyValue.value(8), ), ); } diff --git a/examples/lib/layers_marker_page.dart b/examples/lib/layers_marker_page.dart index 1a653ffe9..46cdde1b4 100644 --- a/examples/lib/layers_marker_page.dart +++ b/examples/lib/layers_marker_page.dart @@ -70,9 +70,9 @@ class _LayersMarkerPageState extends State { textField: '{name}', textAllowOverlap: true, iconImage: _imageLoaded ? 'marker' : null, - iconSize: 0.15, + iconSize: 0.5, iconAnchor: IconAnchor.bottom, - textOffset: const [0, 1], + textOffset: const Offset(0, 1.7), ), ], ), diff --git a/examples/lib/layers_polyline_page.dart b/examples/lib/layers_polyline_page.dart index b89b4accc..972debc88 100644 --- a/examples/lib/layers_polyline_page.dart +++ b/examples/lib/layers_polyline_page.dart @@ -49,7 +49,6 @@ class _LayersPolylinePageState extends State { polylines: _polylines, color: Colors.red, width: 4, - blur: 3, dashArray: const [5, 5], ), ], diff --git a/examples/lib/layers_widget_marker_page.dart b/examples/lib/layers_widget_marker_page.dart index e64ad030a..a920675c4 100644 --- a/examples/lib/layers_widget_marker_page.dart +++ b/examples/lib/layers_widget_marker_page.dart @@ -81,7 +81,7 @@ class _LayersWidgetMarkerPageState extends State { iconImage: _imageLoaded ? 'marker' : null, iconSize: 0.15, iconAnchor: IconAnchor.bottom, - textOffset: const [0, 1], + textOffset: const Offset(0, 1), ), ], ), diff --git a/examples/lib/style_layers_circle_page.dart b/examples/lib/style_layers_circle_page.dart index 6b591e516..c669f3d71 100644 --- a/examples/lib/style_layers_circle_page.dart +++ b/examples/lib/style_layers_circle_page.dart @@ -11,9 +11,6 @@ class StyleLayersCirclePage extends StatefulWidget { State createState() => _StyleLayersCirclePageState(); } -const _layerId = 'showcaseLayer'; -const _sourceId = 'earthquakes'; - class _StyleLayersCirclePageState extends State { @override Widget build(BuildContext context) { @@ -30,65 +27,69 @@ class _StyleLayersCirclePageState extends State { } Future _onStyleLoaded(StyleController style) async { + const sourceId = 'earthquakes'; const earthquakes = GeoJsonSource( - id: _sourceId, + id: sourceId, data: 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson', ); await style.addSource(earthquakes); - await style.addLayer(_circleStyleLayer); + final circleLayer = CircleStyleLayer( + id: 'showcaseLayer', + sourceId: sourceId, + // Size circle radius by earthquake magnitude and zoom level + radius: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['zoom'], + 7, + [ + 'interpolate', + ['linear'], + ['get', 'mag'], + 1, + 1, + 6, + 50, + ], + ]), + ), + // Color circle by earthquake magnitude + color: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['get', 'mag'], + 1, + 'rgba(33,102,172,0)', + 2, + 'rgb(103,169,207)', + 3, + 'rgb(209,229,240)', + 4, + 'rgb(253,219,199)', + 5, + 'rgb(239,138,98)', + 6, + 'rgb(178,24,43)', + ]), + ), + strokeColor: const PropertyValue.value(Colors.white), + strokeWidth: const PropertyValue.value(1), + // Transition from heatmap to circle layer by zoom level + opacity: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['zoom'], + 7, + 0, + 8, + 1, + ]), + ), + ); + await style.addLayer(circleLayer); } } - -const _circleStyleLayer = CircleStyleLayer( - id: _layerId, - sourceId: _sourceId, - paint: { - // Size circle radius by earthquake magnitude and zoom level - 'circle-radius': [ - 'interpolate', - ['linear'], - ['zoom'], - 7, - [ - 'interpolate', - ['linear'], - ['get', 'mag'], - 1, - 1, - 6, - 50, - ], - ], - // Color circle by earthquake magnitude - 'circle-color': [ - 'interpolate', - ['linear'], - ['get', 'mag'], - 1, - 'rgba(33,102,172,0)', - 2, - 'rgb(103,169,207)', - 3, - 'rgb(209,229,240)', - 4, - 'rgb(253,219,199)', - 5, - 'rgb(239,138,98)', - 6, - 'rgb(178,24,43)', - ], - 'circle-stroke-color': 'white', - 'circle-stroke-width': 1, - // Transition from heatmap to circle layer by zoom level - 'circle-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 7, - 0, - 8, - 1, - ], - }, -); diff --git a/examples/lib/style_layers_fill_extrusion_page.dart b/examples/lib/style_layers_fill_extrusion_page.dart index c255b4baa..74b116fbe 100644 --- a/examples/lib/style_layers_fill_extrusion_page.dart +++ b/examples/lib/style_layers_fill_extrusion_page.dart @@ -1,5 +1,5 @@ import 'package:flutter/material.dart'; -import 'package:maplibre/maplibre.dart'; +import 'package:maplibre/expressions.dart'; import 'package:maplibre_example/utils/map_styles.dart'; @immutable @@ -42,27 +42,21 @@ class _StyleLayersFillExtrusionPageState 'https://maplibre.org/maplibre-gl-js/docs/assets/indoor-3d-map.geojson', ), ); - await style.addLayer(_fillExtrusionStyleLayer); + final fillExtrusionLayer = FillExtrusionStyleLayer( + id: 'room-extrusion', + sourceId: _sourceId, + // See the MapLibre Style Specification for details on data expressions. + // https://maplibre.org/maplibre-style-spec/expressions/ + + // Get the fill-extrusion-color from the source 'color' property. + color: PropertyValue.expression(get('color')), + // Get fill-extrusion-height from the source 'height' property. + height: PropertyValue.expression(get('height')), + // Get fill-extrusion-base from the source 'base_height' property. + base: PropertyValue.expression(get('base_height')), + // Make extrusions slightly opaque for see through indoor walls. + opacity: const PropertyValue.value(0.5), + ); + await style.addLayer(fillExtrusionLayer); } } - -const _fillExtrusionStyleLayer = FillExtrusionStyleLayer( - id: 'room-extrusion', - sourceId: _sourceId, - paint: { - // See the MapLibre Style Specification for details on data expressions. - // https://maplibre.org/maplibre-style-spec/expressions/ - - // Get the fill-extrusion-color from the source 'color' property. - 'fill-extrusion-color': ['get', 'color'], - - // Get fill-extrusion-height from the source 'height' property. - 'fill-extrusion-height': ['get', 'height'], - - // Get fill-extrusion-base from the source 'base_height' property. - 'fill-extrusion-base': ['get', 'base_height'], - - // Make extrusions slightly opaque for see through indoor walls. - 'fill-extrusion-opacity': 0.5, - }, -); diff --git a/examples/lib/style_layers_fill_page.dart b/examples/lib/style_layers_fill_page.dart index 24ed70b16..319ec2bce 100644 --- a/examples/lib/style_layers_fill_page.dart +++ b/examples/lib/style_layers_fill_page.dart @@ -60,10 +60,10 @@ class _StyleLayersFillPageState extends State { GeoJsonSource(id: 'LakeConstance-Source', data: geojsonPolygon), ); await style.addLayer( - const FillStyleLayer( + FillStyleLayer( id: 'LakeConstance-Layer', sourceId: 'LakeConstance-Source', - paint: {'fill-color': '#429ef5'}, + color: const PropertyValue.value(Color(0xFF429ef5)), ), ); } diff --git a/examples/lib/style_layers_heatmap_page.dart b/examples/lib/style_layers_heatmap_page.dart index 7e544186f..574f515cd 100644 --- a/examples/lib/style_layers_heatmap_page.dart +++ b/examples/lib/style_layers_heatmap_page.dart @@ -36,74 +36,81 @@ class _StyleLayersHeatmapPageState extends State { 'https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson', ); await style.addSource(earthquakes); - await style.addLayer(_heatmapStyleLayer); + final heatmapLayer = HeatmapStyleLayer( + id: _layerId, + sourceId: _sourceId, + // Increase the heatmap weight based on frequency and property magnitude + weight: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['get', 'mag'], + 0, + 0, + 6, + 1, + ]), + ), + // Increase the heatmap color weight weight by zoom level + // heatmap-intensity is a multiplier on top of heatmap-weight + intensity: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['zoom'], + 0, + 1, + 9, + 3, + ]), + ), + // Color ramp for heatmap. Domain is 0 (low) to 1 (high). + // Begin color ramp at 0-stop with a 0-transparency color + // to create a blur-like effect. + color: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['heatmap-density'], + 0, + 'rgba(33,102,172,0)', + 0.2, + 'rgb(103,169,207)', + 0.4, + 'rgb(209,229,240)', + 0.6, + 'rgb(253,219,199)', + 0.8, + 'rgb(239,138,98)', + 1, + 'rgb(178,24,43)', + ]), + ), + // Adjust the heatmap radius by zoom level + radius: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['zoom'], + 0, + 2, + 9, + 20, + ]), + ), + // Transition from heatmap to circle layer by zoom level + opacity: const PropertyValue.expression( + Expression.fromJson([ + 'interpolate', + ['linear'], + ['zoom'], + 7, + 1, + 9, + 0, + ]), + ), + ); + await style.addLayer(heatmapLayer); } } - -const _heatmapStyleLayer = HeatmapStyleLayer( - id: _layerId, - sourceId: _sourceId, - paint: { - // Increase the heatmap weight based on frequency and property magnitude - 'heatmap-weight': [ - 'interpolate', - ['linear'], - ['get', 'mag'], - 0, - 0, - 6, - 1, - ], - // Increase the heatmap color weight weight by zoom level - // heatmap-intensity is a multiplier on top of heatmap-weight - 'heatmap-intensity': [ - 'interpolate', - ['linear'], - ['zoom'], - 0, - 1, - 9, - 3, - ], - // Color ramp for heatmap. Domain is 0 (low) to 1 (high). - // Begin color ramp at 0-stop with a 0-transparency color - // to create a blur-like effect. - 'heatmap-color': [ - 'interpolate', - ['linear'], - ['heatmap-density'], - 0, - 'rgba(33,102,172,0)', - 0.2, - 'rgb(103,169,207)', - 0.4, - 'rgb(209,229,240)', - 0.6, - 'rgb(253,219,199)', - 0.8, - 'rgb(239,138,98)', - 1, - 'rgb(178,24,43)', - ], - // Adjust the heatmap radius by zoom level - 'heatmap-radius': [ - 'interpolate', - ['linear'], - ['zoom'], - 0, - 2, - 9, - 20, - ], - // Transition from heatmap to circle layer by zoom level - 'heatmap-opacity': [ - 'interpolate', - ['linear'], - ['zoom'], - 7, - 1, - 9, - 0, - ], - }, -); diff --git a/examples/lib/style_layers_hillshade_page.dart b/examples/lib/style_layers_hillshade_page.dart index d7543d480..5c207bc82 100644 --- a/examples/lib/style_layers_hillshade_page.dart +++ b/examples/lib/style_layers_hillshade_page.dart @@ -12,9 +12,6 @@ class StyleLayersHillshadePage extends StatefulWidget { _StyleLayersHillshadePageState(); } -const _layerId = 'showcaseLayer'; -const _sourceId = 'hills'; - class _StyleLayersHillshadePageState extends State { @override Widget build(BuildContext context) { @@ -31,18 +28,19 @@ class _StyleLayersHillshadePageState extends State { } Future _onStyleLoaded(StyleController style) async { + const sourceId = 'hills'; const hillshade = RasterDemSource( - id: _sourceId, + id: sourceId, url: 'https://demotiles.maplibre.org/terrain-tiles/tiles.json', tileSize: 256, ); await style.addSource(hillshade); - await style.addLayer(_hillshadeStyleLayer); + final hillshadeLayer = HillshadeStyleLayer( + id: 'showcaseLayer', + sourceId: sourceId, + shadowColor: const PropertyValue.value(Color(0xFF473B24)), + ); + + await style.addLayer(hillshadeLayer); } } - -const _hillshadeStyleLayer = HillshadeStyleLayer( - id: _layerId, - sourceId: _sourceId, - paint: {'hillshade-shadow-color': '#473B24'}, -); diff --git a/examples/lib/style_layers_line_page.dart b/examples/lib/style_layers_line_page.dart index 9430a2c72..419f14d05 100644 --- a/examples/lib/style_layers_line_page.dart +++ b/examples/lib/style_layers_line_page.dart @@ -31,10 +31,11 @@ class _StyleLayersLinePageState extends State { final geojsonLine = await rootBundle.loadString('assets/geojson/path.json'); await style.addSource(GeoJsonSource(id: 'Path', data: geojsonLine)); await style.addLayer( - const LineStyleLayer( + LineStyleLayer( id: 'geojson-line', sourceId: 'Path', - paint: {'line-color': '#F00', 'line-width': 3}, + color: const PropertyValue.value(Colors.red), + width: const PropertyValue.value(3), ), ); } diff --git a/examples/lib/style_layers_raster_page.dart b/examples/lib/style_layers_raster_page.dart index acd0982b2..139847bde 100644 --- a/examples/lib/style_layers_raster_page.dart +++ b/examples/lib/style_layers_raster_page.dart @@ -11,9 +11,6 @@ class StyleLayersRasterPage extends StatefulWidget { State createState() => _StyleLayersRasterPageState(); } -const _layerId = 'showcaseLayer'; -const _sourceId = 'openStreetMap'; - class _StyleLayersRasterPageState extends State { // If you are looking for just a way how to display OpenStreetMap on the // map then this is NOT the right approach. You can create a Style JSON that @@ -36,7 +33,7 @@ class _StyleLayersRasterPageState extends State { Future _onStyleLoaded(StyleController style) async { const openStreetMap = RasterSource( - id: _sourceId, + id: 'openStreetMap', tiles: ['https://tile.openstreetmap.org/{z}/{x}/{y}.png'], maxZoom: 20, tileSize: 256, @@ -44,8 +41,10 @@ class _StyleLayersRasterPageState extends State { 'OpenStreetMap', ); await style.addSource(openStreetMap); - await style.addLayer(_rasterStyleLayer); + final rasterLayer = RasterStyleLayer( + id: 'showcaseLayer', + sourceId: 'openStreetMap', + ); + await style.addLayer(rasterLayer); } } - -const _rasterStyleLayer = RasterStyleLayer(id: _layerId, sourceId: _sourceId); diff --git a/examples/lib/style_layers_symbol_page.dart b/examples/lib/style_layers_symbol_page.dart index 6b1b2c0a0..d9afe8f11 100644 --- a/examples/lib/style_layers_symbol_page.dart +++ b/examples/lib/style_layers_symbol_page.dart @@ -42,16 +42,14 @@ class _StyleLayersSymbolPageState extends State { // display the image on the map await style.addLayer( - const SymbolStyleLayer( + SymbolStyleLayer( id: 'images', sourceId: 'points', - layout: { - // see https://maplibre.org/maplibre-style-spec/layers/#symbol - 'icon-image': 'marker', - 'icon-size': 0.08, - 'icon-allow-overlap': true, - 'icon-anchor': 'bottom', - }, + // see https://maplibre.org/maplibre-style-spec/layers/#symbol + iconImage: const PropertyValue.value('marker'), + iconSize: const PropertyValue.value(0.2), + iconAllowOverlap: const PropertyValue.value(true), + iconAnchor: const PropertyValue.value(IconAnchor.bottom), ), ); } on Exception catch (error, stacktrace) { diff --git a/examples/lib/style_sources_vector_page.dart b/examples/lib/style_sources_vector_page.dart index 208b2aa17..be295a757 100644 --- a/examples/lib/style_sources_vector_page.dart +++ b/examples/lib/style_sources_vector_page.dart @@ -41,48 +41,45 @@ class _StyleSourcesVectorPageState extends State { ); await style.addSource(vectorSource); - const fillLayer = FillStyleLayer( + final fillLayer = FillStyleLayer( id: 'fill_layer', sourceId: sourceId, sourceLayerId: 'countries', - paint: {'fill-color': '#429ef5', 'fill-opacity': 0.6}, + color: const PropertyValue.value(Color(0xFF429EF5)), + opacity: const PropertyValue.value(0.6), ); await style.addLayer(fillLayer); - const lineLayer = LineStyleLayer( + final lineLayer = LineStyleLayer( id: 'line_layer', sourceId: sourceId, sourceLayerId: 'countries', - paint: {'line-color': '#00F', 'line-width': 3}, + color: const PropertyValue.value(Colors.blue), + width: const PropertyValue.value(3), ); await style.addLayer(lineLayer); - const circleLayer = CircleStyleLayer( + final circleLayer = CircleStyleLayer( id: 'circle_layer', sourceId: sourceId, sourceLayerId: 'centroids', - paint: { - 'circle-radius': 18, - 'circle-color': '#000000', - 'circle-opacity': 0.5, - 'circle-stroke-color': '#FFF', - 'circle-stroke-width': 2, - }, + radius: const PropertyValue.value(18), + opacity: const PropertyValue.value(0.5), + strokeColor: const PropertyValue.value(Colors.white), + strokeWidth: const PropertyValue.value(2), ); await style.addLayer(circleLayer); - const symbolLayer = SymbolStyleLayer( + final symbolLayer = SymbolStyleLayer( id: 'images_layer', sourceId: sourceId, sourceLayerId: 'centroids', - layout: { - 'icon-image': 'marker', - 'icon-size': 0.18, - 'icon-allow-overlap': true, - 'icon-anchor': 'bottom', - }, + iconImage: const PropertyValue.value('marker'), + iconSize: const PropertyValue.value(0.18), + iconAllowOverlap: const PropertyValue.value(true), + iconAnchor: const PropertyValue.value(IconAnchor.bottom), ); await style.addLayer(symbolLayer); } on Exception catch (error, stacktrace) { diff --git a/packages/maplibre/lib/expressions.dart b/packages/maplibre/lib/expressions.dart new file mode 100644 index 000000000..0c767a20a --- /dev/null +++ b/packages/maplibre/lib/expressions.dart @@ -0,0 +1 @@ +export 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; diff --git a/packages/maplibre/lib/maplibre.dart b/packages/maplibre/lib/maplibre.dart index 026a37d69..32ef8a709 100644 --- a/packages/maplibre/lib/maplibre.dart +++ b/packages/maplibre/lib/maplibre.dart @@ -11,11 +11,13 @@ export 'package:maplibre_platform_interface/maplibre_platform_interface.dart' CameraChangeReason, CircleStyleLayer, ColorExtension, + Expression, FillExtrusionStyleLayer, FillStyleLayer, GeoJsonSource, HeatmapStyleLayer, HillshadeStyleLayer, + IconAnchor, ImageSource, Layer, LineStyleLayer, @@ -45,6 +47,7 @@ export 'package:maplibre_platform_interface/maplibre_platform_interface.dart' OfflineManager, OfflineRegion, PermissionManager, + PropertyValue, QueriedLayer, RasterDemCustomEncoding, RasterDemEncoding, @@ -71,7 +74,7 @@ export 'package:maplibre_platform_interface/maplibre_platform_interface.dart' htmlColorNames; export 'src/layer/circle_layer.dart' show CircleLayer; -export 'src/layer/marker_layer.dart' show IconAnchor, MarkerLayer; +export 'src/layer/marker_layer.dart' show MarkerLayer; export 'src/layer/polygon_layer.dart' show PolygonLayer; export 'src/layer/polyline_layer.dart' show PolylineLayer; export 'src/ui/map_compass.dart' show MapCompass; diff --git a/packages/maplibre/lib/src/layer/circle_layer.dart b/packages/maplibre/lib/src/layer/circle_layer.dart index f1d71519d..b1b3a109c 100644 --- a/packages/maplibre/lib/src/layer/circle_layer.dart +++ b/packages/maplibre/lib/src/layer/circle_layer.dart @@ -21,7 +21,7 @@ class CircleLayer extends Layer> { }) : super(list: points); /// Circle radius in pixels. Defaults to 5px. - final int radius; + final double radius; /// The color of the circle. Defaults to black. final Color color; @@ -34,7 +34,7 @@ class CircleLayer extends Layer> { double get opacity => color.a; /// The outline width - final int strokeWidth; + final double strokeWidth; /// The outline color final Color strokeColor; @@ -46,26 +46,17 @@ class CircleLayer extends Layer> { StyleLayer createStyleLayer(int index) => CircleStyleLayer( id: getLayerId(index), sourceId: getSourceId(index), - paint: getPaint(), - layout: getLayout(), minZoom: minZoom, maxZoom: maxZoom, + radius: PropertyValue.value(radius), + color: PropertyValue.value(color), + blur: PropertyValue.value(blur), + opacity: PropertyValue.value(opacity), + strokeWidth: PropertyValue.value(strokeWidth), + strokeColor: PropertyValue.value(strokeColor), + strokeOpacity: PropertyValue.value(strokeOpacity), ); - @override - Map getPaint() => { - 'circle-radius': radius, - 'circle-color': color.toHexString(), - 'circle-blur': blur, - 'circle-opacity': opacity, - 'circle-stroke-width': strokeWidth, - 'circle-stroke-color': strokeColor.toHexString(), - 'circle-stroke-opacity': strokeOpacity, - }; - - @override - Map getLayout() => {}; - @override bool operator ==(Object other) => identical(this, other) || diff --git a/packages/maplibre/lib/src/layer/marker_layer.dart b/packages/maplibre/lib/src/layer/marker_layer.dart index 50cb8a64b..bcee695c0 100644 --- a/packages/maplibre/lib/src/layer/marker_layer.dart +++ b/packages/maplibre/lib/src/layer/marker_layer.dart @@ -1,6 +1,5 @@ -import 'dart:ui'; - import 'package:flutter/foundation.dart'; +import 'package:flutter/painting.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; /// A [Point] layer. @@ -17,9 +16,9 @@ class MarkerLayer extends Layer> { this.iconSize = 1, this.iconImage, this.iconRotate = 0, - this.iconPadding = 2, + this.iconPadding = const EdgeInsets.all(2), this.iconKeepUpright = false, - this.iconOffset = const [0, 0], + this.iconOffset = Offset.zero, this.textField = '', this.textFont = const ['Open Sans Regular', 'Arial Unicode MS Regular'], this.textSize = 16, @@ -31,7 +30,7 @@ class MarkerLayer extends Layer> { this.textRotate = 0, this.textPadding = 2, this.textKeepUpright = true, - this.textOffset = const [0, 0], + this.textOffset = Offset.zero, this.textAllowOverlap = false, this.textIgnorePlacement = false, this.textOptional = false, @@ -40,13 +39,13 @@ class MarkerLayer extends Layer> { this.iconHaloColor = const Color(0xFF000000), this.iconHaloWidth = 0, this.iconHaloBlur = 0, - this.iconTranslate = const [0, 0], + this.iconTranslate = Offset.zero, this.textColor = const Color(0xFF000000), this.textOpacity = 1, this.textHaloColor = const Color(0xFF000000), this.textHaloWidth = 0, this.textHaloBlur = 0, - this.textTranslate = const [0, 0], + this.textTranslate = Offset.zero, this.iconAnchor = IconAnchor.center, super.minZoom = 0, super.maxZoom = 24, @@ -76,7 +75,7 @@ class MarkerLayer extends Layer> { /// Size of additional area round the icon bounding box used for detecting /// symbol collisions. - final int iconPadding; + final EdgeInsets iconPadding; /// If true, the icon may be flipped to prevent it from being rendered /// upside-down. @@ -87,7 +86,7 @@ class MarkerLayer extends Layer> { /// multiplied by the value of icon-size to obtain the final offset in /// pixels. When combined with icon-rotate the offset will be as if the /// rotated direction was up. - final List iconOffset; + final Offset iconOffset; /// Value to use for a text label. If a plain string is provided, it will /// be treated as a formatted with default/inherited formatting options. @@ -97,10 +96,10 @@ class MarkerLayer extends Layer> { final List textFont; /// Font stack to use for displaying text. - final int textSize; + final double textSize; /// Font stack to use for displaying text. - final int textMaxWidth; + final double textMaxWidth; /// Text leading value for multi-line text. final double textLineHeight; @@ -132,7 +131,7 @@ class MarkerLayer extends Layer> { /// text-variable-anchor, input values will be taken as absolute values. /// Offsets along the x- and y-axis will be applied automatically based on /// the anchor position. - final List textOffset; + final Offset textOffset; /// If true, the text will be visible even if it collides with other /// previously drawn symbols. @@ -157,15 +156,15 @@ class MarkerLayer extends Layer> { /// The unit is in pixels only for SDF sprites that were created with a blur /// radius of 8, multiplied by the display density. I.e., the radius needs /// to be 16 for @2x sprites, etc. - final int iconHaloWidth; + final double iconHaloWidth; /// Fade out the halo towards the outside. - final int iconHaloBlur; + final double iconHaloBlur; /// Distance that the icon's anchor is moved from its original placement. /// Positive values indicate right and down, while negative values indicate /// left and up. - final List iconTranslate; + final Offset iconTranslate; /// The opacity at which the text will be drawn. final double textOpacity; @@ -181,68 +180,57 @@ class MarkerLayer extends Layer> { final double textHaloWidth; /// The halo's fadeout distance towards the outside. - final int textHaloBlur; + final double textHaloBlur; /// Distance that the text's anchor is moved from its original placement. /// Positive values indicate right and down, while negative values indicate /// left and up. - final List textTranslate; + final Offset textTranslate; /// Part of the icon placed closest to the anchor. final IconAnchor iconAnchor; - @override - Map getPaint() => { - 'icon-opacity': iconOpacity, - 'icon-color': iconColor.toHexString(), - 'icon-halo-color': iconHaloColor.toHexString(), - 'icon-halo-width': iconHaloWidth, - 'icon-halo-blur': iconHaloBlur, - 'text-opacity': iconOpacity, - 'text-color': textColor.toHexString(), - 'text-halo-color': textHaloColor.toHexString(), - 'text-halo-width': textHaloWidth, - 'text-halo-blur': textHaloBlur, - 'text-translate': textTranslate, - }; - - @override - Map getLayout() => { - 'icon-allow-overlap': iconAllowOverlap, - 'icon-ignore-placement': iconIgnorePlacement, - 'icon-optional': iconOptional, - 'icon-size': iconSize, - if (iconImage case final String image) 'icon-image': image, - 'icon-rotate': iconRotate, - 'icon-padding': iconPadding, - 'icon-keep-upright': iconKeepUpright, - 'icon-offset': iconOffset, - 'icon-anchor': iconAnchor.name, - 'text-field': textField, - 'text-font': textFont, - 'text-size': textSize, - 'text-max-width': textMaxWidth, - 'text-line-height': textLineHeight, - 'text-letter-spacing': textLetterSpacing, - 'text-radial-offset': textRadialOffset, - 'text-max-angle': textMaxAngle, - 'text-rotate': textRotate, - 'text-padding': textPadding, - 'text-keep-upright': textKeepUpright, - 'text-offset': textOffset, - 'text-allow-overlap': textAllowOverlap, - 'text-ignore-placement': textIgnorePlacement, - 'text-optional': textOptional, - }; - @override StyleLayer createStyleLayer(int index) => SymbolStyleLayer( id: getLayerId(index), sourceId: getSourceId(index), - paint: getPaint(), - layout: getLayout(), minZoom: minZoom, maxZoom: maxZoom, + iconOpacity: PropertyValue.value(iconOpacity), + iconColor: PropertyValue.value(iconColor), + iconHaloColor: PropertyValue.value(iconHaloColor), + iconHaloWidth: PropertyValue.value(iconHaloWidth), + iconHaloBlur: PropertyValue.value(iconHaloBlur), + textOpacity: PropertyValue.value(textOpacity), + textColor: PropertyValue.value(textColor), + textHaloColor: PropertyValue.value(textHaloColor), + textHaloWidth: PropertyValue.value(textHaloWidth), + textHaloBlur: PropertyValue.value(textHaloBlur), + textTranslate: PropertyValue.value(textTranslate), + iconAllowOverlap: PropertyValue.value(iconAllowOverlap), + iconIgnorePlacement: PropertyValue.value(iconIgnorePlacement), + iconOptional: PropertyValue.value(iconOptional), + iconSize: PropertyValue.value(iconSize), + iconImage: iconImage != null ? PropertyValue.value(iconImage!) : null, + iconRotate: PropertyValue.value(iconRotate), + iconPadding: PropertyValue.value(iconPadding), + iconKeepUpright: PropertyValue.value(iconKeepUpright), + iconOffset: PropertyValue.value(iconOffset), + textField: PropertyValue.value(textField), + textFont: PropertyValue.value(textFont), + textSize: PropertyValue.value(textSize), + textMaxWidth: PropertyValue.value(textMaxWidth), + textLineHeight: PropertyValue.value(textLineHeight), + textLetterSpacing: PropertyValue.value(textLetterSpacing), + textRadialOffset: PropertyValue.value(textRadialOffset), + textMaxAngle: PropertyValue.value(textMaxAngle), + textRotate: PropertyValue.value(textRotate), + textPadding: PropertyValue.value(textPadding), + textKeepUpright: PropertyValue.value(textKeepUpright), + textOffset: PropertyValue.value(textOffset), + textAllowOverlap: PropertyValue.value(textAllowOverlap), + textIgnorePlacement: PropertyValue.value(textIgnorePlacement), + textOptional: PropertyValue.value(textOptional), ); @override @@ -331,38 +319,3 @@ class MarkerLayer extends Layer> { iconAnchor, ]); } - -/// Part of the icon placed closest to the anchor. -enum IconAnchor { - /// The center of the icon is placed closest to the anchor. - center('center'), - - /// The left side of the icon is placed closest to the anchor. - left('left'), - - /// The right side of the icon is placed closest to the anchor. - right('right'), - - /// The top of the icon is placed closest to the anchor. - top('top'), - - /// The bottom of the icon is placed closest to the anchor. - bottom('bottom'), - - /// The top left corner of the icon is placed closest to the anchor. - topLeft('top-left'), - - /// The top right corner of the icon is placed closest to the anchor. - topRight('top-right'), - - /// The bottom left corner of the icon is placed closest to the anchor. - bottomLeft('bottom-left'), - - /// The bottom right corner of the icon is placed closest to the anchor. - bottomRight('bottom-right'); - - const IconAnchor(this.name); - - /// The MapLibre Style spec compatible name. - final String name; -} diff --git a/packages/maplibre/lib/src/layer/polygon_layer.dart b/packages/maplibre/lib/src/layer/polygon_layer.dart index eb5221a89..50adc561f 100644 --- a/packages/maplibre/lib/src/layer/polygon_layer.dart +++ b/packages/maplibre/lib/src/layer/polygon_layer.dart @@ -26,24 +26,15 @@ class PolygonLayer extends Layer> { /// The outline color final Color outlineColor; - @override - Map getPaint() => { - 'fill-color': color.toHexString(), - 'fill-opacity': opacity, - 'fill-outline-color': outlineColor.toHexString(), - }; - - @override - Map getLayout() => {}; - @override StyleLayer createStyleLayer(int index) => FillStyleLayer( id: getLayerId(index), sourceId: getSourceId(index), - paint: getPaint(), - layout: getLayout(), minZoom: minZoom, maxZoom: maxZoom, + color: PropertyValue.value(color), + opacity: PropertyValue.value(opacity), + outlineColor: PropertyValue.value(outlineColor), ); @override diff --git a/packages/maplibre/lib/src/layer/polyline_layer.dart b/packages/maplibre/lib/src/layer/polyline_layer.dart index 2443872ed..928a15da3 100644 --- a/packages/maplibre/lib/src/layer/polyline_layer.dart +++ b/packages/maplibre/lib/src/layer/polyline_layer.dart @@ -27,36 +27,28 @@ class PolylineLayer extends Layer> { double get opacity => color.a; /// Stroke thickness. - final int width; + final double width; /// Stroke thickness. - final int gapWidth; + final double gapWidth; /// Blur applied to the line, in pixels. Defaults to 0. - final int blur; + final double blur; /// Specifies the lengths of the alternating dashes and gaps that form the /// dash pattern. The lengths are later scaled by the line width. - final List? dashArray; - - @override - Map getPaint() => { - 'line-color': color.toHexString(), - 'line-opacity': opacity, - 'line-width': width, - 'line-gap-width': gapWidth, - if (dashArray case final List dashArray) 'line-dasharray': dashArray, - }; - - @override - Map getLayout() => {}; + final List? dashArray; @override StyleLayer createStyleLayer(int index) => LineStyleLayer( id: getLayerId(index), sourceId: getSourceId(index), - paint: getPaint(), - layout: getLayout(), + color: PropertyValue.value(color), + opacity: PropertyValue.value(opacity), + width: PropertyValue.value(width), + gapWidth: PropertyValue.value(gapWidth), + blur: PropertyValue.value(blur), + dashArray: dashArray != null ? PropertyValue.value(dashArray!) : null, minZoom: minZoom, maxZoom: maxZoom, ); diff --git a/packages/maplibre/test/annotation/models_test.dart b/packages/maplibre/test/annotation/models_test.dart index 24785e91d..5ed87b86a 100644 --- a/packages/maplibre/test/annotation/models_test.dart +++ b/packages/maplibre/test/annotation/models_test.dart @@ -28,8 +28,6 @@ void main() { expect(o.getSourceId(5123), contains(5123.toString())); expect(o.getLayerId(1532), contains(1532.toString())); expect(o.createStyleLayer(142), isA()); - expect(o.getLayout(), isA>()); - expect(o.getPaint(), isA>()); }); test('MarkerAnnotationLayer', () { const o = MarkerLayer( @@ -41,7 +39,7 @@ void main() { textHaloColor: Colors.greenAccent, iconHaloColor: Colors.amber, iconColor: Colors.yellow, - textOffset: [2, 4], + textOffset: Offset(2, 4), iconImage: 'test.png', textAllowOverlap: true, textSize: 23, @@ -58,8 +56,6 @@ void main() { expect(o.getSourceId(5123), contains(5123.toString())); expect(o.getLayerId(1532), contains(1532.toString())); expect(o.createStyleLayer(142), isA()); - expect(o.getLayout(), isA>()); - expect(o.getPaint(), isA>()); }); test('PolygonAnnotationLayer', () { final o = PolygonLayer( @@ -112,8 +108,6 @@ void main() { expect(o.getSourceId(5123), contains(5123.toString())); expect(o.getLayerId(1532), contains(1532.toString())); expect(o.createStyleLayer(142), isA()); - expect(o.getLayout(), isA>()); - expect(o.getPaint(), isA>()); }); test('PolylineAnnotationLayer', () { final o = PolylineLayer( @@ -158,8 +152,6 @@ void main() { expect(o.getSourceId(5123), contains(5123.toString())); expect(o.getLayerId(1532), contains(1532.toString())); expect(o.createStyleLayer(142), isA()); - expect(o.getLayout(), isA>()); - expect(o.getPaint(), isA>()); }); }); } diff --git a/packages/maplibre/test/style/expression/expression_test.dart b/packages/maplibre/test/style/expression/expression_test.dart new file mode 100644 index 000000000..22bcf8790 --- /dev/null +++ b/packages/maplibre/test/style/expression/expression_test.dart @@ -0,0 +1,61 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:maplibre/expressions.dart'; + +void main() { + group('Style Expressions', () { + test('numberFormat', () { + final expression = numberFormat( + input: e_(), + locale: 'de-DE', + currency: 'EUR', + minFractionDigits: 2, + maxFractionDigits: 2, + ); + final expected = [ + 'number-format', + ['e'], + { + 'locale': 'de-DE', + 'currency': 'EUR', + 'min-fraction-digits': 2, + 'max-fraction-digits': 2, + }, + ]; + expect(expression.json, equals(expected)); + }); + test('match', () { + final expression = match( + input: join([toString(e_()), toString(ln2_()), toString(pi_())], ', '), + cases: {'case1': 'value1', 'case2': 'value2'}, + fallback: 'fallback', + ); + final expected = [ + 'match', + [ + 'join', + [ + [ + 'to-string', + ['e'], + ], + [ + 'to-string', + ['ln2'], + ], + [ + 'to-string', + ['pi'], + ], + ], + ', ', + ], + 'case1', + 'value1', + 'case2', + 'value2', + 'fallback', + ]; + expect(expression.json, equals(expected)); + }); + }); +} diff --git a/packages/maplibre/test/style/layer/models_test.dart b/packages/maplibre/test/style/layer/models_test.dart index 336cfd7cc..9c2325791 100644 --- a/packages/maplibre/test/style/layer/models_test.dart +++ b/packages/maplibre/test/style/layer/models_test.dart @@ -1,12 +1,11 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:maplibre/maplibre.dart'; -import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; void main() { group('Style Layer Model Classes', () { test('BackgroundLayer', () { - const o = BackgroundStyleLayer(id: '12'); - const o2 = BackgroundStyleLayer(id: '42'); + final o = BackgroundStyleLayer(id: '12'); + final o2 = BackgroundStyleLayer(id: '42'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -14,8 +13,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('CircleLayer', () { - const o = CircleStyleLayer(id: '12', sourceId: '342'); - const o2 = CircleStyleLayer(id: '42', sourceId: '342'); + final o = CircleStyleLayer(id: '12', sourceId: '342'); + final o2 = CircleStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -23,8 +22,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('FillExtrusionLayer', () { - const o = FillExtrusionStyleLayer(id: '12', sourceId: '342'); - const o2 = FillExtrusionStyleLayer(id: '42', sourceId: '342'); + final o = FillExtrusionStyleLayer(id: '12', sourceId: '342'); + final o2 = FillExtrusionStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -32,8 +31,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('FillLayer', () { - const o = FillStyleLayer(id: '12', sourceId: '342'); - const o2 = FillStyleLayer(id: '42', sourceId: '342'); + final o = FillStyleLayer(id: '12', sourceId: '342'); + final o2 = FillStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -41,8 +40,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('HeatmapLayer', () { - const o = HeatmapStyleLayer(id: '12', sourceId: '342'); - const o2 = HeatmapStyleLayer(id: '42', sourceId: '342'); + final o = HeatmapStyleLayer(id: '12', sourceId: '342'); + final o2 = HeatmapStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -50,8 +49,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('HillshadeLayer', () { - const o = HillshadeStyleLayer(id: '12', sourceId: '342'); - const o2 = HillshadeStyleLayer(id: '42', sourceId: '342'); + final o = HillshadeStyleLayer(id: '12', sourceId: '342'); + final o2 = HillshadeStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -59,8 +58,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('LineLayer', () { - const o = LineStyleLayer(id: '12', sourceId: '342'); - const o2 = LineStyleLayer(id: '42', sourceId: '342'); + final o = LineStyleLayer(id: '12', sourceId: '342'); + final o2 = LineStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -68,8 +67,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('RasterLayer', () { - const o = RasterStyleLayer(id: '12', sourceId: '342'); - const o2 = RasterStyleLayer(id: '42', sourceId: '342'); + final o = RasterStyleLayer(id: '12', sourceId: '342'); + final o2 = RasterStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); @@ -77,8 +76,8 @@ void main() { expect(o.hashCode, isNot(equals(o2.hashCode))); }); test('SymbolLayer', () { - const o = SymbolStyleLayer(id: '12', sourceId: '342'); - const o2 = SymbolStyleLayer(id: '42', sourceId: '342'); + final o = SymbolStyleLayer(id: '12', sourceId: '342'); + final o2 = SymbolStyleLayer(id: '42', sourceId: '342'); expect(o, equals(o)); expect(o2, equals(o2)); expect(o, isNot(equals(o2))); diff --git a/packages/maplibre_android/lib/src/extensions.dart b/packages/maplibre_android/lib/src/extensions.dart index 78e76b3d2..215359b11 100644 --- a/packages/maplibre_android/lib/src/extensions.dart +++ b/packages/maplibre_android/lib/src/extensions.dart @@ -1,6 +1,8 @@ import 'dart:convert'; + import 'package:flutter/rendering.dart'; import 'package:jni/jni.dart'; +import 'package:jni/jni.dart' as jni; import 'package:maplibre_android/src/jni.g.dart' as jni; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; @@ -30,11 +32,44 @@ extension PointExt on jni.PointF { } } +/// Extension methods for the [jni.PointF] class. Not exported publicly. +extension JFloatArrayExt on jni.JArray { + /// Convert an [jni.JArray] to a [Offset]. + Offset toOffset({bool releaseOriginal = false}) { + final x = this[0]?.floatValue(releaseOriginal: true) ?? 0; + final y = this[1]?.floatValue(releaseOriginal: true) ?? 0; + final offset = Offset(x, y); + if (releaseOriginal) release(); + return offset; + } +} + /// Extension methods for the [Offset] class. Not exported publicly. extension OffsetExt on Offset { /// Convert an [Offset] to an [jni.PointF]. jni.PointF toJPointF({required Arena arena}) => jni.PointF.new$3(dx, dy)..releasedBy(arena); + + /// Convert an [Offset] to a [JArray]. + JArray toJFloatArray(Arena arena) { + final jArray = JArray(JFloat.nullableType, 2)..releasedBy(arena); + jArray[0] = dx.toJFloat()..releasedBy(arena); + jArray[1] = dy.toJFloat()..releasedBy(arena); + return jArray; + } +} + +/// Extension methods for the [EdgeInsets] class. Not exported publicly. +extension EdgeInsetsExt on EdgeInsets { + /// Convert an [EdgeInsets] to a [JArray]. + JArray toJFloatArray(Arena arena) { + final jArray = JArray(JFloat.nullableType, 4)..releasedBy(arena); + jArray[0] = top.toJFloat()..releasedBy(arena); + jArray[1] = right.toJFloat()..releasedBy(arena); + jArray[2] = bottom.toJFloat()..releasedBy(arena); + jArray[3] = left.toJFloat()..releasedBy(arena); + return jArray; + } } /// Extension methods for the [LngLatBounds] class. Not exported publicly. @@ -64,16 +99,36 @@ extension LatLngBounds on jni.LatLngBounds { } } -/// Extension methods for the [EdgeInsets] class. Not exported publicly. +/// Extension methods for the [List] class. Not exported publicly. +extension DoubleListExt on List { + /// Convert a [List] to a [JArray]. + JArray toJFloatArray(Arena arena) { + final jArray = JArray(JFloat.nullableType, this.length)..releasedBy(arena); + for (var i = 0; i < this.length; i++) { + jArray[i] = this[i].toJFloat()..releasedBy(arena); + } + return jArray; + } +} + +/// Extension methods for the [List] class. Not exported publicly. +extension StringListExt on List { + /// Convert a [List] to a [JArray]. + JArray toJStringArray(Arena arena) { + final jArray = JArray(JString.nullableType, this.length)..releasedBy(arena); + for (var i = 0; i < this.length; i++) { + jArray[i] = this[i].toJString()..releasedBy(arena); + } + return jArray; + } +} + +/// Extension methods for the [jni.OfflineRegion] class. Not exported publicly. extension OfflineRegionExt on jni.OfflineRegion { - /// Convert an internal [jni.OfflineRegion] to an [OfflineRegion]. + /// Convert an [jni.OfflineRegion] to an [OfflineRegion]. OfflineRegion toOfflineRegion() => using((arena) { final jDefinition = getDefinition()..releasedBy(arena); - final jMetadata = getMetadata()..releasedBy(arena); - final metadataBytes = jMetadata.toList(); - final metadataJson = utf8.decode(metadataBytes); - final metadata = jsonDecode(metadataJson) as Map; - + // TODO add getMetadata(); return OfflineRegion( id: getId(), bounds: jDefinition.getBounds()!.toLngLatBounds(releaseOriginal: true), @@ -81,47 +136,261 @@ extension OfflineRegionExt on jni.OfflineRegion { maxZoom: jDefinition.getMaxZoom(), pixelRatio: jDefinition.getPixelRatio(), styleUrl: jDefinition.getStyleURL()!.toDartString(releaseOriginal: true), - metadata: metadata, ); }); } -/// Extension methods on [Object]. -extension ObjectExt on Object { - /// Convert a [Object] to a [JObject]. - JObject toJObject() { - switch (this) { - case final Map value: - final jMap = jni.HashMap( - K: JObject.nullableType, - V: JObject.nullableType, - ); - for (final entry in value.entries) { - final jKey = entry.key.toJObject(); - final jValue = entry.value?.toJObject(); - jMap.put(jKey, jValue); - jKey.release(); - jValue?.release(); +/// Extension methods for the [jni.Layer] class. Not exported publicly. +extension JniLayerExt on jni.Layer { + /// Set a single property on this layer. + void setProperty(jni.PropertyValue? property) => using((arena) { + final jProperties = + JArray(jni.PropertyValue.nullableType(JObject.nullableType), 1) + ..releasedBy(arena) + ..[0] = property; + setProperties(jProperties); + }); +} + +/// Extension methods for the [Expression] class. Not exported publicly. +extension ExpressionExt on Expression { + /// Set a single property on this layer. + jni.Expression? toJExpression(Arena arena) { + final expressionString = jsonEncode(this.json).toJString() + ..releasedBy(arena); + return jni.Expression$Converter.convert$2(expressionString) + ?..releasedBy(arena); + } +} + +/// Extension methods for the [jni.Expression] class. Not exported publicly. +extension JExpressionExt on jni.Expression { + /// Convert a [jni.Expression] to an [Expression]. + Expression toDart({bool releaseOriginal = false}) => using((arena) { + final expressionString = toString$2()!.toDartString(releaseOriginal: true); + if (releaseOriginal) release(); + final json = jsonDecode(expressionString) as List; + final expression = Expression.fromJson(json); + return expression; + }); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JStringPropertyValueExt on jni.PropertyValue { + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDart({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + if (jExpression == null) return null; + return PropertyValue.expression(jExpression.toDart()); } - return jMap; - case final List value: - final jArray = JArray(JObject.nullableType, value.length); - for (var i = 0; i < value.length; i++) { - final jElement = value[i]?.toJObject(); - jArray[i] = jElement; - jElement?.release(); + final jValue = getValue(); + if (jValue == null) return null; + final value = jValue.toDartString(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(value); + }); + + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDartColor({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + if (jExpression == null) return null; + return PropertyValue.expression(jExpression.toDart()); + } + final jValue = getColorInt(); + if (jValue == null) return null; + final value = jValue.intValue(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(Color(value)); + }); + + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDartReferenceSpace({ + bool releaseOriginal = false, + }) => toDartEnum( + values: ReferenceSpace.values, + releaseOriginal: releaseOriginal, + ); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JDoublePropertyValueExt on jni.PropertyValue { + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDart({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + if (jExpression == null) return null; + return PropertyValue.expression(jExpression.toDart()); } - return jArray; - case final String value: - return value.toJString(); - case final double value: - return value.toJDouble(); - case final int value: - return value.toJInteger(); - case final bool value: - return value.toJBoolean(); - default: - throw Exception('Unsupported property type: $runtimeType, $this'); + final jValue = getValue(); + if (jValue == null) return null; + final value = jValue.doubleValue(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(value); + }); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JFloatPropertyValueExt on jni.PropertyValue { + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDart({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + if (jExpression == null) return null; + return PropertyValue.expression(jExpression.toDart()); + } + final jValue = getValue(); + if (jValue == null) return null; + final value = jValue.doubleValue(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(value); + }); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JBooleanPropertyValueExt on jni.PropertyValue { + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDart({bool releaseOriginal = false}) => using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + if (jExpression == null) return null; + return PropertyValue.expression(jExpression.toDart()); + } + final jValue = getValue(); + if (jValue == null) return null; + final value = jValue.booleanValue(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(value); + }); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JStringArrayPropertyValueExt on jni.PropertyValue?> { + /// Convert a [jni.PropertyValue] to a [PropertyValue>]. + PropertyValue>? toDartStringList({ + bool releaseOriginal = false, + }) => using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()!..releasedBy(arena); + final expression = jExpression.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + final jValue = getValue()!..releasedBy(arena); + final list = jValue.nonNulls + .map((e) => e.toDartString(releaseOriginal: true)) + .toList(growable: true); + if (releaseOriginal) release(); + return PropertyValue.value(list); + }); +} + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JFloatArrayPropertyValueExt on jni.PropertyValue?> { + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDartOffset({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()!..releasedBy(arena); + final expression = jExpression.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + final jValue = getValue()!..releasedBy(arena); + final x = jValue[0]!.floatValue(releaseOriginal: true); + final y = jValue[1]!.floatValue(releaseOriginal: true); + if (releaseOriginal) release(); + return PropertyValue.value(Offset(x, y)); + }); + + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue? toDartEdgeInsets({bool releaseOriginal = false}) => + using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()!..releasedBy(arena); + final expression = jExpression.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + final jValue = getValue()!..releasedBy(arena); + final padding = EdgeInsets.only( + top: jValue[0]?.floatValue(releaseOriginal: true) ?? 0, + right: jValue[1]?.floatValue(releaseOriginal: true) ?? 0, + bottom: jValue[2]?.floatValue(releaseOriginal: true) ?? 0, + left: jValue[3]?.floatValue(releaseOriginal: true) ?? 0, + ); + if (releaseOriginal) release(); + return PropertyValue.value(padding); + }); + + /// Convert a [jni.PropertyValue] to a [PropertyValue]. + PropertyValue>? toDartDoubleList({ + bool releaseOriginal = false, + }) => using((arena) { + if (isNull$1()) return null; + if (isExpression()) { + final jExpression = getExpression()!..releasedBy(arena); + final expression = jExpression.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + final jValue = getValue()!..releasedBy(arena); + final list = jValue.nonNulls + .map((e) => e.doubleValue(releaseOriginal: true)) + .toList(growable: true); + if (releaseOriginal) release(); + return PropertyValue.value(list); + }); +} + +/// Extension methods for the [PropertyValue] class. Not exported publicly. +extension PropertyValueExt on PropertyValue? { + /// Applies the [PropertyValue] to a style layer property. + jni.PropertyValue? apply({ + required Arena arena, + required jni.PropertyValue? Function(jni.Expression? expression) + onExpression, + required jni.PropertyValue? Function(V value) onValue, + jni.PropertyValue? Function()? onNull, + }) { + final property = this; + if (property == null) { + return onNull?.call(); + } else if (property.isExpression) { + return onExpression(property.expression.toJExpression(arena)); + } else { + return onValue(property.value); } } } + +/// Extension methods for the [jni.PropertyValue] class. Not exported publicly. +extension JPropertyValueStringExt on jni.PropertyValue { + /// Convert a [jni.PropertyValue] to a [PropertyValue] where T is an enum. + PropertyValue? toDartEnum({ + required List values, + bool releaseOriginal = false, + }) => using((arena) { + if (isExpression()) { + final jExpression = getExpression()?..releasedBy(arena); + final expression = jExpression?.toDart(releaseOriginal: true); + if (expression == null) return null; + return PropertyValue.expression(expression); + } + final jValue = getValue(); + if (jValue == null) return null; + final value = jValue.toDartString(releaseOriginal: true); + final match = values.firstWhere((e) => e.name == value); + if (releaseOriginal) release(); + return PropertyValue.value(match); + }); +} diff --git a/packages/maplibre_android/lib/src/jni.g.dart b/packages/maplibre_android/lib/src/jni.g.dart index 29d5fd1ef..060afbc59 100644 --- a/packages/maplibre_android/lib/src/jni.g.dart +++ b/packages/maplibre_android/lib/src/jni.g.dart @@ -134391,15 +134391,15 @@ class PropertyFactory extends jni$_.JObject { /// from: `static public org.maplibre.android.style.layers.PropertyValue textField(org.maplibre.android.style.types.Formatted formatted)` /// The returned object must be released after use, by calling the [release] method. - static PropertyValue? textField$1(jni$_.JObject? formatted) { + static PropertyValue? textField$1(Formatted? formatted) { final _$formatted = formatted?.reference ?? jni$_.jNullReference; return _textField$1( _class.reference.pointer, _id_textField$1 as jni$_.JMethodIDPtr, _$formatted.pointer, - ).object?>( - const $PropertyValue$NullableType$( - jni$_.$JObject$NullableType$(), + ).object?>( + const $PropertyValue$NullableType$( + $Formatted$NullableType$(), ), ); } @@ -138619,14 +138619,12 @@ class SymbolLayer extends Layer { /// from: `public org.maplibre.android.style.layers.PropertyValue getTextField()` /// The returned object must be released after use, by calling the [release] method. - PropertyValue getTextField() { + PropertyValue getTextField() { return _getTextField( reference.pointer, _id_getTextField as jni$_.JMethodIDPtr, - ).object>( - const $PropertyValue$Type$( - jni$_.$JObject$NullableType$(), - ), + ).object>( + const $PropertyValue$Type$($Formatted$NullableType$()), ); } @@ -149985,6 +149983,944 @@ final class $VectorSource$Type$ extends jni$_.JType { } } +/// from: `org.maplibre.android.style.types.Formatted` +class Formatted extends jni$_.JObject { + @jni$_.internal + @core$_.override + final jni$_.JType $type; + + @jni$_.internal + Formatted.fromReference(jni$_.JReference reference) + : $type = type, + super.fromReference(reference); + + static final _class = jni$_.JClass.forName( + r'org/maplibre/android/style/types/Formatted', + ); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType nullableType = + $Formatted$NullableType$(); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType type = $Formatted$Type$(); + static final _id_new$ = _class.constructorId( + r'([Lorg/maplibre/android/style/types/FormattedSection;)V', + ); + + static final _new$ = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public void (org.maplibre.android.style.types.FormattedSection[] formattedSections)` + /// The returned object must be released after use, by calling the [release] method. + factory Formatted(jni$_.JArray formattedSections) { + final _$formattedSections = formattedSections.reference; + return Formatted.fromReference( + _new$( + _class.reference.pointer, + _id_new$ as jni$_.JMethodIDPtr, + _$formattedSections.pointer, + ).reference, + ); + } + + static final _id_getFormattedSections = _class.instanceMethodId( + r'getFormattedSections', + r'()[Lorg/maplibre/android/style/types/FormattedSection;', + ); + + static final _getFormattedSections = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public final org.maplibre.android.style.types.FormattedSection[] getFormattedSections()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JArray getFormattedSections() { + return _getFormattedSections( + reference.pointer, + _id_getFormattedSections as jni$_.JMethodIDPtr, + ).object>( + const jni$_.$JArray$Type$($FormattedSection$Type$()), + ); + } + + static final _id_toArray = _class.instanceMethodId( + r'toArray', + r'()[Ljava/lang/Object;', + ); + + static final _toArray = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public fun toArray(): kotlin.Array` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JArray toArray() { + return _toArray( + reference.pointer, + _id_toArray as jni$_.JMethodIDPtr, + ).object>( + const jni$_.$JArray$Type$(jni$_.$JObject$NullableType$()), + ); + } + + static final _id_equals = _class.instanceMethodId( + r'equals', + r'(Ljava/lang/Object;)Z', + ); + + static final _equals = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_CallBooleanMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public operator fun equals(o: kotlin.Any?): kotlin.Boolean` + bool equals(jni$_.JObject? object) { + final _$object = object?.reference ?? jni$_.jNullReference; + return _equals( + reference.pointer, + _id_equals as jni$_.JMethodIDPtr, + _$object.pointer, + ).boolean; + } + + static final _id_hashCode$1 = _class.instanceMethodId(r'hashCode', r'()I'); + + static final _hashCode$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallIntMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public fun hashCode(): kotlin.Int` + int hashCode$1() { + return _hashCode$1( + reference.pointer, + _id_hashCode$1 as jni$_.JMethodIDPtr, + ).integer; + } + + static final _id_toString$1 = _class.instanceMethodId( + r'toString', + r'()Ljava/lang/String;', + ); + + static final _toString$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public fun toString(): kotlin.String` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JString toString$1() { + return _toString$1( + reference.pointer, + _id_toString$1 as jni$_.JMethodIDPtr, + ).object(const jni$_.$JString$Type$()); + } +} + +final class $Formatted$NullableType$ extends jni$_.JType { + @jni$_.internal + const $Formatted$NullableType$(); + + @jni$_.internal + @core$_.override + String get signature => r'Lorg/maplibre/android/style/types/Formatted;'; + + @jni$_.internal + @core$_.override + Formatted? fromReference(jni$_.JReference reference) => + reference.isNull ? null : Formatted.fromReference(reference); + @jni$_.internal + @core$_.override + jni$_.JType get superType => const jni$_.$JObject$Type$(); + + @jni$_.internal + @core$_.override + jni$_.JType get nullableType => this; + + @jni$_.internal + @core$_.override + final superCount = 1; + + @core$_.override + int get hashCode => ($Formatted$NullableType$).hashCode; + + @core$_.override + bool operator ==(Object other) { + return other.runtimeType == ($Formatted$NullableType$) && + other is $Formatted$NullableType$; + } +} + +final class $Formatted$Type$ extends jni$_.JType { + @jni$_.internal + const $Formatted$Type$(); + + @jni$_.internal + @core$_.override + String get signature => r'Lorg/maplibre/android/style/types/Formatted;'; + + @jni$_.internal + @core$_.override + Formatted fromReference(jni$_.JReference reference) => + Formatted.fromReference(reference); + @jni$_.internal + @core$_.override + jni$_.JType get superType => const jni$_.$JObject$Type$(); + + @jni$_.internal + @core$_.override + jni$_.JType get nullableType => const $Formatted$NullableType$(); + + @jni$_.internal + @core$_.override + final superCount = 1; + + @core$_.override + int get hashCode => ($Formatted$Type$).hashCode; + + @core$_.override + bool operator ==(Object other) { + return other.runtimeType == ($Formatted$Type$) && other is $Formatted$Type$; + } +} + +/// from: `org.maplibre.android.style.types.FormattedSection` +class FormattedSection extends jni$_.JObject { + @jni$_.internal + @core$_.override + final jni$_.JType $type; + + @jni$_.internal + FormattedSection.fromReference(jni$_.JReference reference) + : $type = type, + super.fromReference(reference); + + static final _class = jni$_.JClass.forName( + r'org/maplibre/android/style/types/FormattedSection', + ); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType nullableType = + $FormattedSection$NullableType$(); + + /// The type which includes information such as the signature of this class. + static const jni$_.JType type = $FormattedSection$Type$(); + static final _id_new$ = _class.constructorId(r'(Ljava/lang/String;)V'); + + static final _new$ = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public void (java.lang.String string)` + /// The returned object must be released after use, by calling the [release] method. + factory FormattedSection(jni$_.JString string) { + final _$string = string.reference; + return FormattedSection.fromReference( + _new$( + _class.reference.pointer, + _id_new$ as jni$_.JMethodIDPtr, + _$string.pointer, + ).reference, + ); + } + + static final _id_new$1 = _class.constructorId( + r'(Ljava/lang/String;Ljava/lang/Number;[Ljava/lang/String;Ljava/lang/String;)V', + ); + + static final _new$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// from: `public void (java.lang.String string, java.lang.Number number, java.lang.String[] strings, java.lang.String string1)` + /// The returned object must be released after use, by calling the [release] method. + factory FormattedSection.new$1( + jni$_.JString string, + jni$_.JNumber? number, + jni$_.JArray? strings, + jni$_.JString? string1, + ) { + final _$string = string.reference; + final _$number = number?.reference ?? jni$_.jNullReference; + final _$strings = strings?.reference ?? jni$_.jNullReference; + final _$string1 = string1?.reference ?? jni$_.jNullReference; + return FormattedSection.fromReference( + _new$1( + _class.reference.pointer, + _id_new$1 as jni$_.JMethodIDPtr, + _$string.pointer, + _$number.pointer, + _$strings.pointer, + _$string1.pointer, + ).reference, + ); + } + + static final _id_new$2 = _class.constructorId( + r'(Ljava/lang/String;Ljava/lang/Number;[Ljava/lang/String;)V', + ); + + static final _new$2 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + ( + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// from: `public void (java.lang.String string, java.lang.Number number, java.lang.String[] strings)` + /// The returned object must be released after use, by calling the [release] method. + factory FormattedSection.new$2( + jni$_.JString string, + jni$_.JNumber? number, + jni$_.JArray? strings, + ) { + final _$string = string.reference; + final _$number = number?.reference ?? jni$_.jNullReference; + final _$strings = strings?.reference ?? jni$_.jNullReference; + return FormattedSection.fromReference( + _new$2( + _class.reference.pointer, + _id_new$2 as jni$_.JMethodIDPtr, + _$string.pointer, + _$number.pointer, + _$strings.pointer, + ).reference, + ); + } + + static final _id_new$3 = _class.constructorId( + r'(Ljava/lang/String;Ljava/lang/Number;)V', + ); + + static final _new$3 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + (jni$_.Pointer, jni$_.Pointer) + >, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// from: `public void (java.lang.String string, java.lang.Number number)` + /// The returned object must be released after use, by calling the [release] method. + factory FormattedSection.new$3(jni$_.JString string, jni$_.JNumber? number) { + final _$string = string.reference; + final _$number = number?.reference ?? jni$_.jNullReference; + return FormattedSection.fromReference( + _new$3( + _class.reference.pointer, + _id_new$3 as jni$_.JMethodIDPtr, + _$string.pointer, + _$number.pointer, + ).reference, + ); + } + + static final _id_new$4 = _class.constructorId( + r'(Ljava/lang/String;[Ljava/lang/String;)V', + ); + + static final _new$4 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs< + (jni$_.Pointer, jni$_.Pointer) + >, + ) + > + >('globalEnv_NewObject') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + jni$_.Pointer, + ) + >(); + + /// from: `public void (java.lang.String string, java.lang.String[] strings)` + /// The returned object must be released after use, by calling the [release] method. + factory FormattedSection.new$4( + jni$_.JString string, + jni$_.JArray? strings, + ) { + final _$string = string.reference; + final _$strings = strings?.reference ?? jni$_.jNullReference; + return FormattedSection.fromReference( + _new$4( + _class.reference.pointer, + _id_new$4 as jni$_.JMethodIDPtr, + _$string.pointer, + _$strings.pointer, + ).reference, + ); + } + + static final _id_getText = _class.instanceMethodId( + r'getText', + r'()Ljava/lang/String;', + ); + + static final _getText = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public java.lang.String getText()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JString getText() { + return _getText( + reference.pointer, + _id_getText as jni$_.JMethodIDPtr, + ).object(const jni$_.$JString$Type$()); + } + + static final _id_getFontScale = _class.instanceMethodId( + r'getFontScale', + r'()Ljava/lang/Number;', + ); + + static final _getFontScale = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public java.lang.Number getFontScale()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JNumber? getFontScale() { + return _getFontScale( + reference.pointer, + _id_getFontScale as jni$_.JMethodIDPtr, + ).object(const jni$_.$JNumber$NullableType$()); + } + + static final _id_getFontStack = _class.instanceMethodId( + r'getFontStack', + r'()[Ljava/lang/String;', + ); + + static final _getFontStack = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public java.lang.String[] getFontStack()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JArray? getFontStack() { + return _getFontStack( + reference.pointer, + _id_getFontStack as jni$_.JMethodIDPtr, + ).object?>( + const jni$_.$JArray$NullableType$( + jni$_.$JString$NullableType$(), + ), + ); + } + + static final _id_getTextColor = _class.instanceMethodId( + r'getTextColor', + r'()Ljava/lang/String;', + ); + + static final _getTextColor = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public java.lang.String getTextColor()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JString? getTextColor() { + return _getTextColor( + reference.pointer, + _id_getTextColor as jni$_.JMethodIDPtr, + ).object(const jni$_.$JString$NullableType$()); + } + + static final _id_setFontScale = _class.instanceMethodId( + r'setFontScale', + r'(Ljava/lang/Number;)V', + ); + + static final _setFontScale = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public void setFontScale(java.lang.Number number)` + void setFontScale(jni$_.JNumber? number) { + final _$number = number?.reference ?? jni$_.jNullReference; + _setFontScale( + reference.pointer, + _id_setFontScale as jni$_.JMethodIDPtr, + _$number.pointer, + ).check(); + } + + static final _id_setFontStack = _class.instanceMethodId( + r'setFontStack', + r'([Ljava/lang/String;)V', + ); + + static final _setFontStack = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public void setFontStack(java.lang.String[] strings)` + void setFontStack(jni$_.JArray? strings) { + final _$strings = strings?.reference ?? jni$_.jNullReference; + _setFontStack( + reference.pointer, + _id_setFontStack as jni$_.JMethodIDPtr, + _$strings.pointer, + ).check(); + } + + static final _id_setTextColor = _class.instanceMethodId( + r'setTextColor', + r'(Ljava/lang/String;)V', + ); + + static final _setTextColor = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public void setTextColor(java.lang.String string)` + void setTextColor(jni$_.JString? string) { + final _$string = string?.reference ?? jni$_.jNullReference; + _setTextColor( + reference.pointer, + _id_setTextColor as jni$_.JMethodIDPtr, + _$string.pointer, + ).check(); + } + + static final _id_setTextColor$1 = _class.instanceMethodId( + r'setTextColor', + r'(I)V', + ); + + static final _setTextColor$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Int32,)>, + ) + > + >('globalEnv_CallVoidMethod') + .asFunction< + jni$_.JThrowablePtr Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + int, + ) + >(); + + /// from: `public void setTextColor(int i)` + void setTextColor$1(int i) { + _setTextColor$1( + reference.pointer, + _id_setTextColor$1 as jni$_.JMethodIDPtr, + i, + ).check(); + } + + static final _id_equals = _class.instanceMethodId( + r'equals', + r'(Ljava/lang/Object;)Z', + ); + + static final _equals = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.VarArgs<(jni$_.Pointer,)>, + ) + > + >('globalEnv_CallBooleanMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + jni$_.Pointer, + ) + >(); + + /// from: `public boolean equals(java.lang.Object object)` + bool equals(jni$_.JObject? object) { + final _$object = object?.reference ?? jni$_.jNullReference; + return _equals( + reference.pointer, + _id_equals as jni$_.JMethodIDPtr, + _$object.pointer, + ).boolean; + } + + static final _id_hashCode$1 = _class.instanceMethodId(r'hashCode', r'()I'); + + static final _hashCode$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallIntMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public int hashCode()` + int hashCode$1() { + return _hashCode$1( + reference.pointer, + _id_hashCode$1 as jni$_.JMethodIDPtr, + ).integer; + } + + static final _id_toString$1 = _class.instanceMethodId( + r'toString', + r'()Ljava/lang/String;', + ); + + static final _toString$1 = + jni$_.ProtectedJniExtensions.lookup< + jni$_.NativeFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + > + >('globalEnv_CallObjectMethod') + .asFunction< + jni$_.JniResult Function( + jni$_.Pointer, + jni$_.JMethodIDPtr, + ) + >(); + + /// from: `public java.lang.String toString()` + /// The returned object must be released after use, by calling the [release] method. + jni$_.JString? toString$1() { + return _toString$1( + reference.pointer, + _id_toString$1 as jni$_.JMethodIDPtr, + ).object(const jni$_.$JString$NullableType$()); + } +} + +final class $FormattedSection$NullableType$ + extends jni$_.JType { + @jni$_.internal + const $FormattedSection$NullableType$(); + + @jni$_.internal + @core$_.override + String get signature => + r'Lorg/maplibre/android/style/types/FormattedSection;'; + + @jni$_.internal + @core$_.override + FormattedSection? fromReference(jni$_.JReference reference) => + reference.isNull ? null : FormattedSection.fromReference(reference); + @jni$_.internal + @core$_.override + jni$_.JType get superType => const jni$_.$JObject$NullableType$(); + + @jni$_.internal + @core$_.override + jni$_.JType get nullableType => this; + + @jni$_.internal + @core$_.override + final superCount = 1; + + @core$_.override + int get hashCode => ($FormattedSection$NullableType$).hashCode; + + @core$_.override + bool operator ==(Object other) { + return other.runtimeType == ($FormattedSection$NullableType$) && + other is $FormattedSection$NullableType$; + } +} + +final class $FormattedSection$Type$ extends jni$_.JType { + @jni$_.internal + const $FormattedSection$Type$(); + + @jni$_.internal + @core$_.override + String get signature => + r'Lorg/maplibre/android/style/types/FormattedSection;'; + + @jni$_.internal + @core$_.override + FormattedSection fromReference(jni$_.JReference reference) => + FormattedSection.fromReference(reference); + @jni$_.internal + @core$_.override + jni$_.JType get superType => const jni$_.$JObject$NullableType$(); + + @jni$_.internal + @core$_.override + jni$_.JType get nullableType => + const $FormattedSection$NullableType$(); + + @jni$_.internal + @core$_.override + final superCount = 1; + + @core$_.override + int get hashCode => ($FormattedSection$Type$).hashCode; + + @core$_.override + bool operator ==(Object other) { + return other.runtimeType == ($FormattedSection$Type$) && + other is $FormattedSection$Type$; + } +} + /// from: `org.maplibre.geojson.Feature` /// /// This defines a GeoJson Feature object which represents a spatially bound thing. Every Feature diff --git a/packages/maplibre_android/lib/src/map_state.dart b/packages/maplibre_android/lib/src/map_state.dart index 8b480a8b3..18193da0e 100644 --- a/packages/maplibre_android/lib/src/map_state.dart +++ b/packages/maplibre_android/lib/src/map_state.dart @@ -12,6 +12,7 @@ import 'package:maplibre_android/src/flutter_api.dart'; import 'package:maplibre_android/src/functions.dart'; import 'package:maplibre_android/src/jni.g.dart' as jni; import 'package:maplibre_android/src/registry.dart'; +import 'package:maplibre_android/src/style/layers/style_layer.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; part 'style_controller.dart'; diff --git a/packages/maplibre_android/lib/src/maplibre_plugin.dart b/packages/maplibre_android/lib/src/maplibre_plugin.dart index 31c4abdb3..165142d05 100644 --- a/packages/maplibre_android/lib/src/maplibre_plugin.dart +++ b/packages/maplibre_android/lib/src/maplibre_plugin.dart @@ -1,6 +1,8 @@ +import 'package:flutter/painting.dart'; import 'package:maplibre_android/src/map_state.dart'; import 'package:maplibre_android/src/offline_manager.dart'; import 'package:maplibre_android/src/permission_manager.dart'; +import 'package:maplibre_android/src/style/layers/style_layer.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; /// Android implementation of the federated MapLibre plugin. @@ -26,4 +28,436 @@ final class MapLibrePlugin extends MapLibrePlatform { @override bool get userLocationIsSupported => true; + + /// Create a platform specific [BackgroundStyleLayer] object. + @override + BackgroundStyleLayer createBackgroundStyleLayer({ + required String id, + required bool visible, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => BackgroundStyleLayerAndroid( + id: id, + visible: visible, + color: color, + pattern: pattern, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [CircleStyleLayer] object. + @override + CircleStyleLayer createCircleStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => CircleStyleLayerAndroid( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + translate: translate, + translateAnchor: translateAnchor, + sortKey: sortKey, + radius: radius, + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + /// Create a platform specific [ColorReliefStyleLayer] object. + @override + ColorReliefStyleLayer createColorReliefStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => throw UnsupportedError( + 'ColorReliefStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [FillExtrusionStyleLayer] object. + @override + FillExtrusionStyleLayer createFillExtrusionStyleLayer({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => FillExtrusionStyleLayerAndroid( + id: id, + sourceId: sourceId, + minZoom: minZoom, + maxZoom: maxZoom, + visible: visible, + filter: filter, + sourceLayerId: sourceLayerId, + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + /// Create a platform specific [FillStyleLayer] object. + @override + FillStyleLayer createFillStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) => FillStyleLayerAndroid( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + ); + + /// Create a platform specific [HeatmapStyleLayer] object. + @override + HeatmapStyleLayer createHeatmapStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => HeatmapStyleLayerAndroid( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + /// Create a platform specific [HillshadeStyleLayer] object. + @override + HillshadeStyleLayer createHillshadeStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) => HillshadeStyleLayerAndroid( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + illuminationDirection: illuminationDirection, + // illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + // method: method, + ); + + /// Create a platform specific [LineStyleLayer] object. + @override + LineStyleLayer createLineStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => LineStyleLayerAndroid( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + opacity: opacity, + color: color, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + cap: cap, + ); + + /// Create a platform specific [RasterStyleLayer] object. + @override + RasterStyleLayer createRasterStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => RasterStyleLayerAndroid( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + /// Create a platform specific [SymbolStyleLayer] object. + @override + SymbolStyleLayer createSymbolStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) => SymbolStyleLayerAndroid( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + // iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + // textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); } diff --git a/packages/maplibre_android/lib/src/style/layers/background_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/background_style_layer.dart new file mode 100644 index 000000000..eef80712c --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/background_style_layer.dart @@ -0,0 +1,84 @@ +part of 'style_layer.dart'; + +/// Android implementation of [BackgroundStyleLayer]. +class BackgroundStyleLayerAndroid extends StyleLayerAndroid + implements BackgroundStyleLayer { + /// Factory for a [BackgroundStyleLayerAndroid] instance. + factory BackgroundStyleLayerAndroid({ + required String id, + required double minZoom, + required double maxZoom, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required bool visible, + }) => using((arena) { + return BackgroundStyleLayerAndroid.fromNativeLayer( + jni.BackgroundLayer(id.toJString()..releasedBy(arena)), + ) + ..minZoom = minZoom + ..maxZoom = maxZoom + ..color = color + ..opacity = opacity + ..visible = visible + ..pattern = pattern; + }); + + /// Construct an [BackgroundStyleLayerAndroid] from a JNI.. + BackgroundStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get opacity => + jLayer.getBackgroundOpacity().toDart(releaseOriginal: true) ?? + BackgroundStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.backgroundOpacity$1, + onValue: (value) => jni.PropertyFactory.backgroundOpacity( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get pattern => + jLayer.getBackgroundPattern().toDart(releaseOriginal: true); + + @override + set pattern(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.backgroundPattern$1, + onValue: (value) => jni.PropertyFactory.backgroundPattern( + value.toJString()..releasedBy(arena), + ), + onNull: () => jni.PropertyFactory.backgroundPattern(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get color => + jLayer.getBackgroundColor().toDartColor() ?? + BackgroundStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.backgroundColor$2, + onValue: (value) => jni.PropertyFactory.backgroundColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/circle_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/circle_style_layer.dart new file mode 100644 index 000000000..3ebb04963 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/circle_style_layer.dart @@ -0,0 +1,303 @@ +part of 'style_layer.dart'; + +/// Android implementation of [CircleStyleLayer]. +class CircleStyleLayerAndroid extends StyleLayerAndroid + implements CircleStyleLayer { + /// Factory for a [CircleStyleLayerAndroid] instance. + factory CircleStyleLayerAndroid({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required PropertyValue color, + required PropertyValue opacity, + required bool visible, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue blur, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => using((arena) { + final layer = CircleStyleLayerAndroid.fromNativeLayer( + jni.CircleLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.color = color; + layer.opacity = opacity; + layer.visible = visible; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.sortKey = sortKey; + layer.radius = radius; + layer.blur = blur; + layer.pitchScale = pitchScale; + layer.pitchAlignment = pitchAlignment; + layer.strokeWidth = strokeWidth; + layer.strokeColor = strokeColor; + layer.strokeOpacity = strokeOpacity; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + return layer; + }); + + /// Construct an [CircleStyleLayerAndroid] from a JNI.. + CircleStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get opacity => + jLayer.getCircleOpacity().toDart(releaseOriginal: true) ?? + CircleStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleOpacity$1, + onValue: (value) => jni.PropertyFactory.circleOpacity( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get color => + jLayer.getCircleColor().toDartColor(releaseOriginal: true) ?? + CircleStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleColor$2, + onValue: (value) => jni.PropertyFactory.circleColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get blur => + jLayer.getCircleBlur().toDart(releaseOriginal: true) ?? + CircleStyleLayer.defaultBlur; + + @override + set blur(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleBlur$1, + onValue: (value) => + jni.PropertyFactory.circleBlur(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter(); + if (jFilter == null) return null; + return jFilter.toDart(releaseOriginal: true); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena); + if (jFilter != null) jLayer.setFilter(jFilter); + }); + + @override + PropertyValue get pitchAlignment => + jLayer.getCirclePitchAlignment().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + CircleStyleLayer.defaultPitchAlignment; + + @override + set pitchAlignment(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circlePitchAlignment$1, + onValue: (value) => jni.PropertyFactory.circlePitchAlignment( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get pitchScale => + jLayer.getCirclePitchScale().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + CircleStyleLayer.defaultPitchScale; + + @override + set pitchScale(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circlePitchScale$1, + onValue: (value) => jni.PropertyFactory.circlePitchScale( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get radius => + jLayer.getCircleRadius().toDart(releaseOriginal: true) ?? + CircleStyleLayer.defaultRadius; + + @override + set radius(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleRadius$1, + onValue: (value) => + jni.PropertyFactory.circleRadius(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get sortKey => + jLayer.getCircleSortKey().toDart(releaseOriginal: true); + + @override + set sortKey(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onNull: () => jni.PropertyFactory.circleSortKey(null), + onExpression: jni.PropertyFactory.circleSortKey$1, + onValue: (value) => jni.PropertyFactory.circleSortKey( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String value) => using((arena) { + final jValue = value.toJString()..releasedBy(arena); + jLayer.setSourceLayer(jValue); + }); + + @override + PropertyValue get strokeColor => + jLayer.getCircleStrokeColor().toDartColor(releaseOriginal: true) ?? + CircleStyleLayer.defaultStrokeColor; + + @override + set strokeColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleStrokeColor$2, + onValue: (value) => jni.PropertyFactory.circleStrokeColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get strokeOpacity => + jLayer.getCircleStrokeOpacity().toDart(releaseOriginal: true) ?? + CircleStyleLayer.defaultStrokeOpacity; + + @override + set strokeOpacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleStrokeOpacity$1, + onValue: (value) => jni.PropertyFactory.circleStrokeOpacity( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get strokeWidth => + jLayer.getCircleStrokeWidth().toDart(releaseOriginal: true) ?? + CircleStyleLayer.defaultStrokeWidth; + + @override + set strokeWidth(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleStrokeWidth$1, + onValue: (value) => jni.PropertyFactory.circleStrokeWidth( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translate => + jLayer.getCircleTranslate().toDartOffset(releaseOriginal: true) ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleTranslate$1, + onValue: (value) => + jni.PropertyFactory.circleTranslate(value.toJFloatArray(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translateAnchor => + jLayer.getCircleTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.circleTranslateAnchor$1, + onValue: (value) => jni.PropertyFactory.circleTranslateAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/fill_extrusion_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/fill_extrusion_style_layer.dart new file mode 100644 index 000000000..ac2f193e1 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/fill_extrusion_style_layer.dart @@ -0,0 +1,224 @@ +part of 'style_layer.dart'; + +/// Android implementation of [FillExtrusionStyleLayer]. +class FillExtrusionStyleLayerAndroid + extends StyleLayerAndroid + implements FillExtrusionStyleLayer { + /// Default constructor for [FillExtrusionStyleLayerAndroid]. + factory FillExtrusionStyleLayerAndroid({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => using((arena) { + final layer = FillExtrusionStyleLayerAndroid.fromNativeLayer( + jni.FillExtrusionLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + layer.opacity = opacity; + layer.color = color; + layer.pattern = pattern; + layer.height = height; + layer.base = base; + layer.verticalGradient = verticalGradient; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + return layer; + }); + + /// Construct an [FillExtrusionStyleLayerAndroid] from a JNI layer. + FillExtrusionStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get base => + jLayer.getFillExtrusionBase().toDart(releaseOriginal: true) ?? + FillExtrusionStyleLayer.defaultBase; + + @override + set base(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionBase$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionBase( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get color => + jLayer.getFillExtrusionColor().toDartColor(releaseOriginal: true) ?? + FillExtrusionStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionColor$2, + onValue: (value) => jni.PropertyFactory.fillExtrusionColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter()?..releasedBy(arena); + if (jFilter == null) return null; + return jFilter.toDart(releaseOriginal: true); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena)?..releasedBy(arena); + if (jFilter != null) jLayer.setFilter(jFilter); + }); + + @override + PropertyValue get height => + jLayer.getFillExtrusionHeight().toDart(releaseOriginal: true) ?? + FillExtrusionStyleLayer.defaultHeight; + + @override + set height(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionHeight$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionHeight( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get opacity => + jLayer.getFillExtrusionOpacity().toDart(releaseOriginal: true) ?? + FillExtrusionStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionOpacity$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionOpacity( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get pattern => + jLayer.getFillExtrusionPattern().toDart(releaseOriginal: true); + + @override + set pattern(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionPattern$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionPattern( + value.toJString()..releasedBy(arena), + ), + onNull: () => jni.PropertyFactory.fillExtrusionPattern(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String? value) => using((arena) { + final jValue = value?.toJString(); + jValue?.releasedBy(arena); + jLayer.setSourceLayer(jValue); + }); + + @override + PropertyValue get translate => + jLayer.getFillExtrusionTranslate().toDartOffset(releaseOriginal: true) ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionTranslate$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionTranslate( + value.toJFloatArray(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translateAnchor => + jLayer.getFillExtrusionTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionTranslateAnchor$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionTranslateAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get verticalGradient => + jLayer.getFillExtrusionVerticalGradient().toDart(releaseOriginal: true) ?? + FillExtrusionStyleLayer.defaultVerticalGradient; + + @override + set verticalGradient(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillExtrusionVerticalGradient$1, + onValue: (value) => jni.PropertyFactory.fillExtrusionVerticalGradient( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); +} diff --git a/packages/maplibre_android/lib/src/style/layers/fill_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/fill_style_layer.dart new file mode 100644 index 000000000..a03f07cca --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/fill_style_layer.dart @@ -0,0 +1,219 @@ +part of 'style_layer.dart'; + +/// Android implementation of [FillStyleLayer]. +class FillStyleLayerAndroid extends StyleLayerAndroid + implements FillStyleLayer { + /// Default constructor for [FillStyleLayerAndroid]. + factory FillStyleLayerAndroid({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue outlineColor, + required PropertyValue? sortKey, + }) => using((arena) { + final layer = FillStyleLayerAndroid.fromNativeLayer( + jni.FillLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + layer.opacity = opacity; + layer.color = color; + layer.pattern = pattern; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.antialias = antialias; + layer.outlineColor = outlineColor; + if (sortKey != null) layer.sortKey = sortKey; + return layer; + }); + + /// Construct an [FillStyleLayerAndroid] from a JNI layer. + FillStyleLayerAndroid.fromNativeLayer(super.jLayer) : super.fromNativeLayer(); + + @override + PropertyValue get antialias => + jLayer.getFillAntialias().toDart(releaseOriginal: true) ?? + FillStyleLayer.defaultAntialias; + + @override + set antialias(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillAntialias$1, + onValue: (value) => jni.PropertyFactory.fillAntialias( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get color => + jLayer.getFillColor().toDartColor(releaseOriginal: true) ?? + FillStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillColor$2, + onValue: (value) => jni.PropertyFactory.fillColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get opacity => + jLayer.getFillOpacity().toDart(releaseOriginal: true) ?? + FillStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillOpacity$1, + onValue: (value) => + jni.PropertyFactory.fillOpacity(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get outlineColor => + jLayer.getFillOutlineColor().toDartColor(releaseOriginal: true) ?? + FillStyleLayer.defaultOutlineColor; + + @override + set outlineColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillOutlineColor$2, + onValue: (value) => jni.PropertyFactory.fillOutlineColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get pattern => + jLayer.getFillPattern().toDart(releaseOriginal: true); + + @override + set pattern(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillPattern$1, + onValue: (value) => + jni.PropertyFactory.fillPattern(value.toJString()..releasedBy(arena)), + onNull: () => jni.PropertyFactory.fillPattern(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translate => + jLayer.getFillTranslate().toDartOffset(releaseOriginal: true) ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillTranslate$1, + onValue: (value) => + jni.PropertyFactory.fillTranslate(value.toJFloatArray(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translateAnchor => + jLayer.getFillTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillTranslateAnchor$1, + onValue: (value) => jni.PropertyFactory.fillTranslateAnchor( + value.name.toJString()..releasedBy(arena), + ), + onNull: () => jni.PropertyFactory.fillTranslateAnchor(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String? value) => using((arena) { + final jValue = value?.toJString(); + jValue?.releasedBy(arena); + jLayer.setSourceLayer(jValue); + }); + + @override + PropertyValue? get sortKey => + jLayer.getFillSortKey().toDart(releaseOriginal: true); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter()?..releasedBy(arena); + if (jFilter == null) return null; + return jFilter.toDart(releaseOriginal: true); + }); + + @override + set sortKey(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.fillSortKey$1, + onValue: (value) => + jni.PropertyFactory.fillSortKey(value.toJFloat()..releasedBy(arena)), + onNull: () => jni.PropertyFactory.fillSortKey(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena)?..releasedBy(arena); + if (jFilter != null) jLayer.setFilter(jFilter); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/heatmap_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/heatmap_style_layer.dart new file mode 100644 index 000000000..b34c1bcbb --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/heatmap_style_layer.dart @@ -0,0 +1,160 @@ +part of 'style_layer.dart'; + +/// Android implementation of [HeatmapStyleLayer]. +class HeatmapStyleLayerAndroid extends StyleLayerAndroid + implements HeatmapStyleLayer { + /// Factory for a [HeatmapStyleLayerAndroid] instance. + factory HeatmapStyleLayerAndroid({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => using((arena) { + final layer = HeatmapStyleLayerAndroid.fromNativeLayer( + jni.HeatmapLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + layer.radius = radius; + layer.weight = weight; + layer.intensity = intensity; + layer.color = color; + layer.opacity = opacity; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + return layer; + }); + + /// Construct a [HeatmapStyleLayerAndroid] from a JNI layer. + HeatmapStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get radius => + jLayer.getHeatmapRadius().toDart(releaseOriginal: true) ?? + HeatmapStyleLayer.defaultRadius; + + @override + set radius(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.heatmapRadius$1, + onValue: (value) => jni.PropertyFactory.heatmapRadius( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get weight => + jLayer.getHeatmapWeight().toDart(releaseOriginal: true) ?? + HeatmapStyleLayer.defaultWeight; + + @override + set weight(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.heatmapWeight$1, + onValue: (value) => jni.PropertyFactory.heatmapWeight( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get intensity => + jLayer.getHeatmapIntensity().toDart(releaseOriginal: true) ?? + HeatmapStyleLayer.defaultIntensity; + + @override + set intensity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.heatmapIntensity$1, + onValue: (value) => jni.PropertyFactory.heatmapIntensity( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get color => + jLayer.getHeatmapColor().toDartColor(releaseOriginal: true); + + @override + set color(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.heatmapColor$2, + onValue: (value) => jni.PropertyFactory.heatmapColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get opacity => + jLayer.getHeatmapOpacity().toDart(releaseOriginal: true) ?? + HeatmapStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.heatmapOpacity$1, + onValue: (value) => jni.PropertyFactory.heatmapOpacity( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter()?..releasedBy(arena); + if (jFilter == null) return null; + return jFilter.toDart(releaseOriginal: true); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena)?..releasedBy(arena); + if (jFilter != null) jLayer.setFilter(jFilter); + }); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String? value) => using((arena) { + final jValue = value?.toJString(); + jValue?.releasedBy(arena); + jLayer.setSourceLayer(jValue); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/hillshade_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/hillshade_style_layer.dart new file mode 100644 index 000000000..a1bb3ee62 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/hillshade_style_layer.dart @@ -0,0 +1,211 @@ +part of 'style_layer.dart'; + +/// Android implementation of [HillshadeStyleLayer]. +class HillshadeStyleLayerAndroid extends StyleLayerAndroid + implements HillshadeStyleLayer { + /// Factory for a [HillshadeStyleLayerAndroid] instance. + factory HillshadeStyleLayerAndroid({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + // required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + // required PropertyValue method, + }) => using((arena) { + final layer = HillshadeStyleLayerAndroid.fromNativeLayer( + jni.HillshadeLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + layer.illuminationDirection = illuminationDirection; + // layer.illuminationAltitude = illuminationAltitude; + layer.illuminationAnchor = illuminationAnchor; + layer.exaggeration = exaggeration; + layer.shadowColor = shadowColor; + layer.highlightColor = highlightColor; + layer.accentColor = accentColor; + // layer.method = method; + return layer; + }); + + /// Construct a [HillshadeStyleLayerAndroid] from a JNI layer. + HillshadeStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get illuminationDirection => using((arena) { + final jProperty = jLayer.getHillshadeIlluminationDirection(); + if (jProperty.isNull$1()) { + return HillshadeStyleLayer.defaultIlluminationDirection; + } + if (jProperty.isExpression()) { + final jExpression = jProperty.getExpression()!..releasedBy(arena); + final expression = jExpression.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + final jValue = jProperty.getValue()?..releasedBy(arena); + if (jValue == null) { + return HillshadeStyleLayer.defaultIlluminationDirection; + } + final value = jValue.floatValue(releaseOriginal: true); + return PropertyValue.value(NumberArray.number(value)); + }); + + @override + set illuminationDirection(PropertyValue property) => + using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeIlluminationDirection$1, + onValue: (value) { + if (value.isArray) { + final expression = literal(value.array); + final jExpr = expression.toJExpression(arena); + return jni.PropertyFactory.hillshadeIlluminationAnchor$1(jExpr); + } else { + final jFloat = value.number.toJFloat(); + return jni.PropertyFactory.hillshadeIlluminationDirection(jFloat); + } + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get illuminationAltitude => throw UnsupportedError( + 'Getting illuminationAltitude is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + + @override + set illuminationAltitude(PropertyValue value) { + throw UnsupportedError( + 'Setting illuminationAltitude is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + } + + @override + PropertyValue get illuminationAnchor => + jLayer.getHillshadeIlluminationAnchor().toDartEnum( + values: IlluminationAnchor.values, + releaseOriginal: true, + ) ?? + HillshadeStyleLayer.defaultIlluminationAnchor; + + @override + set illuminationAnchor(PropertyValue value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeIlluminationAnchor$1, + onValue: (value) => jni.PropertyFactory.hillshadeIlluminationAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get exaggeration => + jLayer.getHillshadeExaggeration().toDart(releaseOriginal: true) ?? + HillshadeStyleLayer.defaultExaggeration; + + @override + set exaggeration(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeExaggeration$1, + onValue: (value) => jni.PropertyFactory.hillshadeExaggeration( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get shadowColor => + jLayer.getHillshadeShadowColor().toDartColor(releaseOriginal: true) ?? + HillshadeStyleLayer.defaultShadowColor; + + @override + set shadowColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeShadowColor$2, + onValue: (value) => jni.PropertyFactory.hillshadeShadowColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get highlightColor => + jLayer.getHillshadeHighlightColor().toDartColor(releaseOriginal: true) ?? + HillshadeStyleLayer.defaultHighlightColor; + + @override + set highlightColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeHighlightColor$2, + onValue: (value) => jni.PropertyFactory.hillshadeHighlightColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get accentColor => + jLayer.getHillshadeAccentColor().toDartColor(releaseOriginal: true) ?? + HillshadeStyleLayer.defaultAccentColor; + + @override + set accentColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.hillshadeAccentColor$2, + onValue: (value) => jni.PropertyFactory.hillshadeAccentColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get method => throw UnsupportedError( + 'Getting method is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + + @override + set method(PropertyValue value) { + throw UnsupportedError( + 'Setting method is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + } + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); +} diff --git a/packages/maplibre_android/lib/src/style/layers/line_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/line_style_layer.dart new file mode 100644 index 000000000..eaeba4e99 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/line_style_layer.dart @@ -0,0 +1,381 @@ +part of 'style_layer.dart'; + +/// Android implementation of [LineStyleLayer]. +class LineStyleLayerAndroid extends StyleLayerAndroid + implements LineStyleLayer { + /// Factory for a [LineStyleLayerAndroid] instance. + factory LineStyleLayerAndroid({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => using((arena) { + final layer = LineStyleLayerAndroid.fromNativeLayer( + jni.LineLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + layer.sortKey = sortKey; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.cap = cap; + layer.join = join; + layer.miterLimit = miterLimit; + layer.roundLimit = roundLimit; + layer.opacity = opacity; + layer.color = color; + layer.width = width; + layer.gapWidth = gapWidth; + layer.offset = offset; + layer.blur = blur; + layer.dashArray = dashArray; + layer.pattern = pattern; + layer.gradient = gradient; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + return layer; + }); + + /// Construct a [LineStyleLayerAndroid] from a JNI layer. + LineStyleLayerAndroid.fromNativeLayer(super.jLayer) : super.fromNativeLayer(); + + @override + PropertyValue get cap => + jLayer.getLineCap().toDartEnum( + values: LineCap.values, + releaseOriginal: true, + ) ?? + LineStyleLayer.defaultCap; + + @override + set cap(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineCap$1, + onValue: (value) => jni.PropertyFactory.lineCap( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get join => + jLayer.getLineJoin().toDartEnum( + values: LineJoin.values, + releaseOriginal: true, + ) ?? + LineStyleLayer.defaultJoin; + + @override + set join(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineJoin$1, + onValue: (value) => jni.PropertyFactory.lineJoin( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get miterLimit => + jLayer.getLineMiterLimit().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultMiterLimit; + + @override + set miterLimit(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineMiterLimit$1, + onValue: (value) => jni.PropertyFactory.lineMiterLimit( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get roundLimit => + jLayer.getLineRoundLimit().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultRoundLimit; + + @override + set roundLimit(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineRoundLimit$1, + onValue: (value) => jni.PropertyFactory.lineRoundLimit( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get opacity => + jLayer.getLineOpacity().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineOpacity$1, + onValue: (value) => + jni.PropertyFactory.lineOpacity(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get color => + jLayer.getLineColor().toDartColor(releaseOriginal: true) ?? + LineStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineColor$2, + onValue: (value) => jni.PropertyFactory.lineColor$1( + value.toHexString().toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get width => + jLayer.getLineWidth().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultWidth; + + @override + set width(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineWidth$1, + onValue: (value) => + jni.PropertyFactory.lineWidth(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get gapWidth => + jLayer.getLineGapWidth().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultGapWidth; + + @override + set gapWidth(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineGapWidth$1, + onValue: (value) => + jni.PropertyFactory.lineGapWidth(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get offset => + jLayer.getLineOffset().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultOffset; + + @override + set offset(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineOffset$1, + onValue: (value) => + jni.PropertyFactory.lineOffset(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get blur => + jLayer.getLineBlur().toDart(releaseOriginal: true) ?? + LineStyleLayer.defaultBlur; + + @override + set blur(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineBlur$1, + onValue: (value) => + jni.PropertyFactory.lineBlur(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue>? get dashArray => + jLayer.getLineDasharray().toDartDoubleList(releaseOriginal: true); + + @override + set dashArray(PropertyValue>? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineDasharray$1, + onValue: (value) { + final jArray = value.toJFloatArray(arena); + return jni.PropertyFactory.lineDasharray(jArray); + }, + onNull: () => jni.PropertyFactory.lineDasharray(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get pattern => + jLayer.getLinePattern().toDart(releaseOriginal: true); + + @override + set pattern(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.linePattern$1, + onValue: (value) => + jni.PropertyFactory.linePattern(value.toJString()..releasedBy(arena)), + onNull: () => jni.PropertyFactory.linePattern(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get gradient => + jLayer.getLineGradient().toDartColor(releaseOriginal: true); + + @override + set gradient(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineGradient$2, + onValue: (value) => jni.PropertyFactory.lineGradient$1( + value.toHexString().toJString()..releasedBy(arena), + ), + onNull: () => jni.PropertyFactory.lineGradient$1(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translate => + jLayer.getLineTranslate().toDartOffset(releaseOriginal: true) ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineTranslate$1, + onValue: (value) => + jni.PropertyFactory.lineTranslate(value.toJFloatArray(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get translateAnchor => + jLayer.getLineTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineTranslateAnchor$1, + onValue: (value) => jni.PropertyFactory.lineTranslateAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get sortKey => + jLayer.getLineSortKey().toDart(releaseOriginal: true); + + @override + set sortKey(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.lineSortKey$1, + onValue: (value) => + jni.PropertyFactory.lineSortKey(value.toJFloat()..releasedBy(arena)), + onNull: () => jni.PropertyFactory.lineSortKey(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter()?..releasedBy(arena); + if (jFilter == null) return null; + return jFilter.toDart(releaseOriginal: true); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena)?..releasedBy(arena); + if (jFilter != null) jLayer.setFilter(jFilter); + }); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String? value) => using((arena) { + final jValue = value?.toJString(); + jValue?.releasedBy(arena); + jLayer.setSourceLayer(jValue); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/raster_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/raster_style_layer.dart new file mode 100644 index 000000000..52c075d83 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/raster_style_layer.dart @@ -0,0 +1,195 @@ +part of 'style_layer.dart'; + +/// Android implementation of [RasterStyleLayer]. +class RasterStyleLayerAndroid extends StyleLayerAndroid + implements RasterStyleLayer { + /// Factory for a [RasterStyleLayerAndroid] instance. + factory RasterStyleLayerAndroid({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => using((arena) { + return RasterStyleLayerAndroid.fromNativeLayer( + jni.RasterLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ) + ..minZoom = minZoom + ..maxZoom = maxZoom + ..visible = visible + ..opacity = opacity + ..hueRotate = hueRotate + ..brightnessMin = brightnessMin + ..brightnessMax = brightnessMax + ..saturation = saturation + ..contrast = contrast + ..resampling = resampling + ..fadeDuration = fadeDuration; + }); + + /// Construct a [RasterStyleLayerAndroid] from a JNI layer. + RasterStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get opacity => + jLayer.getRasterOpacity().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterOpacity$1, + onValue: (value) => jni.PropertyFactory.rasterOpacity( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get hueRotate => + jLayer.getRasterHueRotate().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultHueRotate; + + @override + set hueRotate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterHueRotate$1, + onValue: (value) => jni.PropertyFactory.rasterHueRotate( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get brightnessMin => + jLayer.getRasterBrightnessMin().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultBrightnessMin; + + @override + set brightnessMin(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterBrightnessMin$1, + onValue: (value) => jni.PropertyFactory.rasterBrightnessMin( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get brightnessMax => + jLayer.getRasterBrightnessMax().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultBrightnessMax; + + @override + set brightnessMax(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterBrightnessMax$1, + onValue: (value) => jni.PropertyFactory.rasterBrightnessMax( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get saturation => + jLayer.getRasterSaturation().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultSaturation; + + @override + set saturation(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterSaturation$1, + onValue: (value) => jni.PropertyFactory.rasterSaturation( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get contrast => + jLayer.getRasterContrast().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultContrast; + + @override + set contrast(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterContrast$1, + onValue: (value) => jni.PropertyFactory.rasterContrast( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get resampling => + jLayer.getRasterResampling().toDartEnum( + values: RasterResampling.values, + releaseOriginal: true, + ) ?? + RasterStyleLayer.defaultResampling; + + @override + set resampling(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterResampling$1, + onValue: (value) => jni.PropertyFactory.rasterResampling( + property.value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get fadeDuration => + jLayer.getRasterFadeDuration().toDart(releaseOriginal: true) ?? + RasterStyleLayer.defaultFadeDuration; + + @override + set fadeDuration(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.rasterFadeDuration$1, + onValue: (value) => jni.PropertyFactory.rasterFadeDuration( + property.value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); +} diff --git a/packages/maplibre_android/lib/src/style/layers/style_layer.dart b/packages/maplibre_android/lib/src/style/layers/style_layer.dart new file mode 100644 index 000000000..b1c298f37 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/style_layer.dart @@ -0,0 +1,61 @@ +import 'dart:ui'; + +import 'package:flutter/painting.dart' show EdgeInsets; +import 'package:jni/jni.dart'; +import 'package:maplibre_android/src/extensions.dart'; +import 'package:maplibre_android/src/jni.g.dart' as jni; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; + +part 'background_style_layer.dart'; +part 'circle_style_layer.dart'; +part 'fill_extrusion_style_layer.dart'; +part 'fill_style_layer.dart'; +part 'heatmap_style_layer.dart'; +part 'hillshade_style_layer.dart'; +part 'line_style_layer.dart'; +part 'raster_style_layer.dart'; +part 'symbol_style_layer.dart'; + +/// Android implementation of [StyleLayer]. +abstract class StyleLayerAndroid + implements StyleLayer { + /// Construct an [StyleLayerAndroid] from a JNI layer. + StyleLayerAndroid.fromNativeLayer(this.jLayer) { + final finalizer = Finalizer((j) => j.release()); + finalizer.attach(this, jLayer, detach: this); + } + + /// The JNI layer instance. + final JLayer jLayer; + + @override + String get id => jLayer.getId().toDartString(releaseOriginal: true); + + @override + double get maxZoom => jLayer.getMaxZoom(); + + @override + set maxZoom(double value) => jLayer.setMaxZoom(value); + + @override + double get minZoom => jLayer.getMinZoom(); + + @override + set minZoom(double value) => jLayer.setMinZoom(value); + + @override + bool get visible => using((arena) { + final jProperty = jLayer.getVisibility()..releasedBy(arena); + final value = jProperty.getValue()?.toDartString(releaseOriginal: true); + return !(value == 'none'); + }); + + @override + set visible(bool newValue) => using((arena) { + final jni.PropertyValue? jProperty; + final stringValue = newValue ? 'visible' : 'none'; + final jValue = stringValue.toJString()..releasedBy(arena); + jProperty = jni.PropertyFactory.visibility(jValue); + jLayer.as(jni.Layer.type).setProperty(jProperty); + }); +} diff --git a/packages/maplibre_android/lib/src/style/layers/symbol_style_layer.dart b/packages/maplibre_android/lib/src/style/layers/symbol_style_layer.dart new file mode 100644 index 000000000..e6600a961 --- /dev/null +++ b/packages/maplibre_android/lib/src/style/layers/symbol_style_layer.dart @@ -0,0 +1,1335 @@ +part of 'style_layer.dart'; + +/// Android implementation of [SymbolStyleLayer]. +class SymbolStyleLayerAndroid extends StyleLayerAndroid + implements SymbolStyleLayer { + /// Factory for a [SymbolStyleLayerAndroid] instance. + factory SymbolStyleLayerAndroid({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + // required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + // required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + PropertyValue textAllowOverlap = + SymbolStyleLayer.defaultTextAllowOverlap, + }) => using((arena) { + final layer = SymbolStyleLayerAndroid.fromNativeLayer( + jni.SymbolLayer( + id.toJString()..releasedBy(arena), + sourceId.toJString()..releasedBy(arena), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + layer.sortKey = sortKey; + layer.placement = placement; + layer.spacing = spacing; + layer.avoidEdges = avoidEdges; + layer.zOrder = zOrder; + layer.iconAllowOverlap = iconAllowOverlap; + // layer.iconOverlap = iconOverlap; + layer.iconIgnorePlacement = iconIgnorePlacement; + layer.iconOptional = iconOptional; + layer.iconRotationAlignment = iconRotationAlignment; + layer.iconSize = iconSize; + layer.iconTextFit = iconTextFit; + layer.iconTextFitPadding = iconTextFitPadding; + layer.iconImage = iconImage; + layer.iconRotate = iconRotate; + layer.iconPadding = iconPadding; + layer.iconKeepUpright = iconKeepUpright; + layer.iconOffset = iconOffset; + layer.iconAnchor = iconAnchor; + layer.iconPitchAlignment = iconPitchAlignment; + layer.textPitchAlignment = textPitchAlignment; + layer.textRotationAlignment = textRotationAlignment; + layer.textField = textField; + layer.textFont = textFont; + layer.textSize = textSize; + layer.textMaxWidth = textMaxWidth; + layer.textLineHeight = textLineHeight; + layer.textLetterSpacing = textLetterSpacing; + layer.textJustify = textJustify; + layer.textRadialOffset = textRadialOffset; + layer.textVariableAnchor = textVariableAnchor; + layer.textVariableAnchorOffset = textVariableAnchorOffset; + layer.textAnchor = textAnchor; + layer.textMaxAngle = textMaxAngle; + layer.textWritingMode = textWritingMode; + layer.textRotate = textRotate; + layer.textPadding = textPadding; + layer.textKeepUpright = textKeepUpright; + layer.textTransform = textTransform; + layer.textOffset = textOffset; + layer.textAllowOverlap = textAllowOverlap; + // layer.textOverlap = textOverlap; + layer.textIgnorePlacement = textIgnorePlacement; + layer.textOptional = textOptional; + layer.iconOpacity = iconOpacity; + layer.iconColor = iconColor; + layer.iconHaloColor = iconHaloColor; + layer.iconHaloWidth = iconHaloWidth; + layer.iconHaloBlur = iconHaloBlur; + layer.iconTranslate = iconTranslate; + layer.iconTranslateAnchor = iconTranslateAnchor; + layer.textOpacity = textOpacity; + layer.textColor = textColor; + layer.textHaloColor = textHaloColor; + layer.textHaloWidth = textHaloWidth; + layer.textHaloBlur = textHaloBlur; + layer.textTranslate = textTranslate; + layer.textTranslateAnchor = textTranslateAnchor; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + return layer; + }); + + /// Construct a [SymbolStyleLayerAndroid] from a JNI layer. + SymbolStyleLayerAndroid.fromNativeLayer(super.jLayer) + : super.fromNativeLayer(); + + @override + String get sourceId => + jLayer.getSourceId().toDartString(releaseOriginal: true); + + @override + String? get sourceLayerId => + jLayer.getSourceLayer().toDartString(releaseOriginal: true); + + @override + set sourceLayerId(String value) => using((arena) { + jLayer.setSourceLayer(value.toJString()..releasedBy(arena)); + }); + + @override + Expression? get filter => using((arena) { + final jFilter = jLayer.getFilter()?..releasedBy(arena); + return jFilter?.toDart(releaseOriginal: true); + }); + + @override + set filter(Expression expression) => using((arena) { + final jFilter = expression.toJExpression(arena); + if (jFilter == null) return; + jLayer.setFilter(jFilter); + }); + + @override + PropertyValue? get sortKey => + jLayer.getSymbolSortKey().toDart(releaseOriginal: true); + + @override + set sortKey(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.symbolSortKey$1, + onValue: (value) => jni.PropertyFactory.symbolSortKey( + value.toJFloat()..releasedBy(arena), + ), + onNull: () => jni.PropertyFactory.symbolSortKey(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get placement => + jLayer.getSymbolPlacement().toDartEnum( + values: SymbolPlacement.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultPlacement; + + @override + set placement(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.symbolPlacement$1, + onValue: (value) => jni.PropertyFactory.symbolPlacement( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get spacing => + jLayer.getSymbolSpacing().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultSpacing; + + @override + set spacing(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.symbolSpacing$1, + onValue: (value) => jni.PropertyFactory.symbolSpacing( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get avoidEdges => + jLayer.getSymbolAvoidEdges().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultAvoidEdges; + + @override + set avoidEdges(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.symbolAvoidEdges$1, + onValue: (value) => jni.PropertyFactory.symbolAvoidEdges( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get zOrder => + jLayer.getSymbolZOrder().toDartEnum( + values: SymbolZOrder.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultZOrder; + + @override + set zOrder(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.symbolZOrder$1, + onValue: (value) => jni.PropertyFactory.symbolZOrder( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconAllowOverlap => + jLayer.getIconAllowOverlap().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconAllowOverlap; + + @override + set iconAllowOverlap(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconAllowOverlap$1, + onValue: (value) => jni.PropertyFactory.iconAllowOverlap( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconOverlap => throw UnsupportedError( + 'Getting iconOverlap is not supported on Android. ' + 'https://github.com/maplibre/maplibre-native/issues/251', + ); + + @override + set iconOverlap(PropertyValue value) => using((arena) { + throw UnsupportedError( + 'Setting iconOverlap is not supported on Android. ' + 'https://github.com/maplibre/maplibre-native/issues/251', + ); + }); + + @override + PropertyValue get iconIgnorePlacement => + jLayer.getIconIgnorePlacement().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconIgnorePlacement; + + @override + set iconIgnorePlacement(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconIgnorePlacement$1, + onValue: (value) => jni.PropertyFactory.iconIgnorePlacement( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconOptional => + jLayer.getIconOptional().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconOptional; + + @override + set iconOptional(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconOptional$1, + onValue: (value) => jni.PropertyFactory.iconOptional( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconRotationAlignment => + jLayer.getIconRotationAlignment().toDartEnum( + values: IconRotationAlignment.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultIconRotationAlignment; + + @override + set iconRotationAlignment(PropertyValue value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconRotationAlignment$1, + onValue: (value) => jni.PropertyFactory.iconRotationAlignment( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconSize => + jLayer.getIconSize().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconSize; + + @override + set iconSize(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconSize$1, + onValue: (value) => + jni.PropertyFactory.iconSize(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconTextFit => + jLayer.getIconTextFit().toDartEnum( + values: IconTextFit.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultIconTextFit; + + @override + set iconTextFit(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconTextFit$1, + onValue: (value) => jni.PropertyFactory.iconTextFit( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconTextFitPadding => + jLayer.getIconTextFitPadding().toDartEdgeInsets(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconTextFitPadding; + + @override + set iconTextFitPadding(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconTextFitPadding$1, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.iconTextFitPadding(floats); + }, + ); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get iconImage => + jLayer.getIconImage().toDart(releaseOriginal: true); + + @override + set iconImage(PropertyValue? property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconImage$1, + onValue: (value) => + jni.PropertyFactory.iconImage(value.toJString()..releasedBy(arena)), + onNull: () => jni.PropertyFactory.iconImage(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconRotate => + jLayer.getIconRotate().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconRotate; + + @override + set iconRotate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconRotate$1, + onValue: (value) => + jni.PropertyFactory.iconRotate(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconPadding => + jLayer.getIconPadding().toDartEdgeInsets(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconPadding; + + @override + set iconPadding(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconPadding$2, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.iconPadding$1(floats); + }, + ); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconKeepUpright => + jLayer.getIconKeepUpright().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconKeepUpright; + + @override + set iconKeepUpright(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconKeepUpright$1, + onValue: (value) => jni.PropertyFactory.iconKeepUpright( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconOffset => + jLayer.getIconOffset().toDartOffset(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconOffset; + + @override + set iconOffset(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconOffset$1, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.iconOffset(floats); + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconAnchor => + jLayer.getIconAnchor().toDartEnum( + values: IconAnchor.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultIconAnchor; + + @override + set iconAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconAnchor$1, + onValue: (value) => jni.PropertyFactory.iconAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconPitchAlignment => + jLayer.getIconPitchAlignment().toDartEnum( + values: IconPitchAlignment.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultIconPitchAlignment; + + @override + set iconPitchAlignment(PropertyValue value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconPitchAlignment$1, + onValue: (value) => jni.PropertyFactory.iconPitchAlignment( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textPitchAlignment => + jLayer.getTextPitchAlignment().toDartEnum( + values: TextPitchAlignment.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextPitchAlignment; + + @override + set textPitchAlignment(PropertyValue value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textPitchAlignment$1, + onValue: (value) => jni.PropertyFactory.textPitchAlignment( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textRotationAlignment => + jLayer.getTextRotationAlignment().toDartEnum( + values: TextRotationAlignment.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextRotationAlignment; + + @override + set textRotationAlignment(PropertyValue value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textRotationAlignment$1, + onValue: (value) => jni.PropertyFactory.textRotationAlignment( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textField => using((arena) { + final jProperty = jLayer.getTextField()..releasedBy(arena); + if (jProperty.isExpression()) { + final jExpression = jProperty.getExpression()?..releasedBy(arena); + final expression = jExpression!.toDart(releaseOriginal: true); + return PropertyValue.expression(expression); + } + // Android always returns a Formatted, we treat this as an Expression + throw Exception( + 'Getting textField as a literal value is not supported on Android.', + ); + }); + + @override + set textField(PropertyValue value) => using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textField$2, + onValue: (value) => + jni.PropertyFactory.textField(value.toJString()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue> get textFont => + jLayer.getTextFont().toDartStringList(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextFont; + + @override + set textFont(PropertyValue> property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textFont$1, + onValue: (value) { + final jArray = value.toJStringArray(arena); + return jni.PropertyFactory.textFont(jArray); + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textSize => + jLayer.getTextSize().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextSize; + + @override + set textSize(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textSize$1, + onValue: (value) => + jni.PropertyFactory.textSize(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textMaxWidth => + jLayer.getTextMaxWidth().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextMaxWidth; + + @override + set textMaxWidth(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textMaxWidth$1, + onValue: (value) => + jni.PropertyFactory.textMaxWidth(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textLineHeight => + jLayer.getTextLineHeight().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextLineHeight; + + @override + set textLineHeight(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textLineHeight$1, + onValue: (value) => jni.PropertyFactory.textLineHeight( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textLetterSpacing => + jLayer.getTextLetterSpacing().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextLetterSpacing; + + @override + set textLetterSpacing(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textLetterSpacing$1, + onValue: (value) => jni.PropertyFactory.textLetterSpacing( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textJustify => + jLayer.getTextJustify().toDartEnum( + values: TextJustify.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextJustify; + + @override + set textJustify(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textJustify$1, + onValue: (value) => jni.PropertyFactory.textJustify( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textRadialOffset => + jLayer.getTextRadialOffset().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextRadialOffset; + + @override + set textRadialOffset(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textRadialOffset$1, + onValue: (value) => jni.PropertyFactory.textRadialOffset( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue>? get textVariableAnchor => using((arena) { + final jProperty = jLayer.getTextVariableAnchor()..releasedBy(arena); + if (jProperty.isNull$1()) return null; + if (jProperty.isExpression()) { + final jExpression = jProperty.getExpression()?..releasedBy(arena); + final expression = jExpression?.toDart(releaseOriginal: true); + return expression == null ? null : PropertyValue.expression(expression); + } + final jArray = jProperty.getValue(); + if (jArray == null) return null; + final values = []; + for (var i = 0; i < jArray.length; i++) { + final entry = jArray[i]; + if (entry == null) continue; + final name = entry.toDartString(releaseOriginal: true); + final match = IconAnchor.values.firstWhere( + (e) => e.name == name, + orElse: () => SymbolStyleLayer.defaultTextAnchor.value as IconAnchor, + ); + values.add(match); + } + return PropertyValue.value(values); + }); + + @override + set textVariableAnchor(PropertyValue>? value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textVariableAnchor$1, + onValue: (value) { + final jArray = JArray(JString.nullableType, value.length); + for (var i = 0; i < value.length; i++) { + jArray[i] = value[i].name.toJString()..releasedBy(arena); + } + return jni.PropertyFactory.textVariableAnchor(jArray); + }, + onNull: () => jni.PropertyFactory.textVariableAnchor(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue>? get textVariableAnchorOffset => using(( + arena, + ) { + final jProperty = jLayer.getTextVariableAnchorOffset()..releasedBy(arena); + if (jProperty.isNull$1()) return null; + if (jProperty.isExpression()) { + final jExpression = jProperty.getExpression()?..releasedBy(arena); + final expression = jExpression?.toDart(releaseOriginal: true); + if (expression == null) return null; + return PropertyValue.expression(expression); + } + final jArray = jProperty.getValue()?..releasedBy(arena); + if (jArray == null) return null; + final values = {}; + for (var i = 0; i < jArray.length; i += 2) { + final entry0 = jArray[i]?..releasedBy(arena); + final entry1 = jArray[i + 1]?..releasedBy(arena); + if (entry0 == null || entry1 == null) continue; + final key = entry0.as(JString.type).toDartString(releaseOriginal: true); + final jOffset = entry1.as(JArray.type(JFloat.nullableType)); + final offset = jOffset.toOffset(releaseOriginal: true); + values[key] = offset; + } + return PropertyValue.value(values); + }); + + @override + set textVariableAnchorOffset(PropertyValue>? value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textVariableAnchorOffset$1, + onValue: (value) { + final jArray = JArray(JObject.nullableType, value.length * 2); + var i = 0; + for (final entry in value.entries) { + jArray[i++] = entry.key.toJString()..releasedBy(arena); + final jOffset = entry.value.toJFloatArray(arena); + jArray[i++] = jOffset..releasedBy(arena); + } + return jni.PropertyFactory.textVariableAnchorOffset(jArray); + }, + onNull: () => jni.PropertyFactory.textVariableAnchorOffset(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textAnchor => + jLayer.getTextAnchor().toDartEnum( + values: TextAnchor.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextAnchor; + + @override + set textAnchor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textAnchor$1, + onValue: (value) => jni.PropertyFactory.textAnchor( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textMaxAngle => + jLayer.getTextMaxAngle().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextMaxAngle; + + @override + set textMaxAngle(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textMaxAngle$1, + onValue: (value) => + jni.PropertyFactory.textMaxAngle(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue>? get textWritingMode => using((arena) { + final jProperty = jLayer.getTextWritingMode()..releasedBy(arena); + if (jProperty.isNull$1()) return null; + if (jProperty.isExpression()) { + final jExpression = jProperty.getExpression()?..releasedBy(arena); + final expression = jExpression?.toDart(releaseOriginal: true); + return expression == null ? null : PropertyValue.expression(expression); + } + final jArray = jProperty.getValue(); + if (jArray == null) return null; + final values = []; + for (var i = 0; i < jArray.length; i++) { + final entry = jArray[i]; + if (entry == null) continue; + final name = entry.toDartString(releaseOriginal: true); + final match = TextWritingMode.values.firstWhere( + (e) => e.name == name, + orElse: () => TextWritingMode.horizontal, + ); + values.add(match); + } + return PropertyValue.value(values); + }); + + @override + set textWritingMode(PropertyValue>? value) => + using((arena) { + final jProperty = value.apply( + arena: arena, + onExpression: jni.PropertyFactory.textWritingMode$1, + onValue: (value) { + final jArray = JArray(JString.nullableType, value.length); + for (var i = 0; i < value.length; i++) { + jArray[i] = value[i].name.toJString()..releasedBy(arena); + } + return jni.PropertyFactory.textWritingMode(jArray); + }, + onNull: () => jni.PropertyFactory.textWritingMode(null), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textRotate => + jLayer.getTextRotate().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextRotate; + + @override + set textRotate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textRotate$1, + onValue: (value) => + jni.PropertyFactory.textRotate(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textPadding => + jLayer.getTextPadding().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextPadding; + + @override + set textPadding(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textPadding$1, + onValue: (value) => + jni.PropertyFactory.textPadding(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textKeepUpright => + jLayer.getTextKeepUpright().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextKeepUpright; + + @override + set textKeepUpright(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textKeepUpright$1, + onValue: (value) => jni.PropertyFactory.textKeepUpright( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textTransform => + jLayer.getTextTransform().toDartEnum( + values: TextTransform.values, + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextTransform; + + @override + set textTransform(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textTransform$1, + onValue: (value) => jni.PropertyFactory.textTransform( + value.name.toJString()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textOffset => + jLayer.getTextOffset().toDartOffset(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextOffset; + + @override + set textOffset(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textOffset$1, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.textOffset(floats); + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textAllowOverlap => + jLayer.getTextAllowOverlap().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextAllowOverlap; + + @override + set textAllowOverlap(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textAllowOverlap$1, + onValue: (value) => jni.PropertyFactory.textAllowOverlap( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue? get textOverlap => throw UnsupportedError( + 'Getting textOverlap is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/251', + ); + + @override + set textOverlap(PropertyValue? value) { + throw UnsupportedError( + 'Setting textOverlap is not supported on Android ' + 'https://github.com/maplibre/maplibre-native/issues/251', + ); + } + + @override + PropertyValue get textIgnorePlacement => + jLayer.getTextIgnorePlacement().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextIgnorePlacement; + + @override + set textIgnorePlacement(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textIgnorePlacement$1, + onValue: (value) => jni.PropertyFactory.textIgnorePlacement( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textOptional => + jLayer.getTextOptional().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextOptional; + + @override + set textOptional(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textOptional$1, + onValue: (value) => jni.PropertyFactory.textOptional( + value.toJBoolean()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconOpacity => + jLayer.getIconOpacity().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconOpacity; + + @override + set iconOpacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconOpacity$1, + onValue: (value) => + jni.PropertyFactory.iconOpacity(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconColor => + jLayer.getIconColor().toDartColor(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconColor; + + @override + set iconColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconColor$2, + onValue: (value) => jni.PropertyFactory.iconColor(value.toARGB32()), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconHaloColor => + jLayer.getIconHaloColor().toDartColor(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconHaloColor; + + @override + set iconHaloColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconHaloColor$2, + onValue: (value) => jni.PropertyFactory.iconHaloColor(value.toARGB32()), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconHaloWidth => + jLayer.getIconHaloWidth().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconHaloWidth; + + @override + set iconHaloWidth(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconHaloWidth$1, + onValue: (value) => jni.PropertyFactory.iconHaloWidth( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconHaloBlur => + jLayer.getIconHaloBlur().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconHaloBlur; + + @override + set iconHaloBlur(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconHaloBlur$1, + onValue: (value) => + jni.PropertyFactory.iconHaloBlur(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconTranslate => + jLayer.getIconTranslate().toDartOffset(releaseOriginal: true) ?? + SymbolStyleLayer.defaultIconTranslate; + + @override + set iconTranslate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.iconTranslate$1, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.iconTranslate(floats); + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get iconTranslateAnchor => + jLayer.getIconTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultIconTranslateAnchor; + + @override + set iconTranslateAnchor(PropertyValue value) => + using((arena) { + final jni.PropertyValue? jProperty; + if (value.isExpression) { + final jExpression = value.expression.toJExpression(arena) + ?..releasedBy(arena); + jProperty = jni.PropertyFactory.iconTranslateAnchor$1(jExpression); + } else { + final jValue = value.value.name.toJString()..releasedBy(arena); + jProperty = jni.PropertyFactory.iconTranslateAnchor(jValue); + } + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textOpacity => + jLayer.getTextOpacity().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextOpacity; + + @override + set textOpacity(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textOpacity$1, + onValue: (value) => + jni.PropertyFactory.textOpacity(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textColor => + jLayer.getTextColor().toDartColor(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextColor; + + @override + set textColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textColor$2, + onValue: (value) => jni.PropertyFactory.textColor(value.toARGB32()), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textHaloColor => + jLayer.getTextHaloColor().toDartColor(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextHaloColor; + + @override + set textHaloColor(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textHaloColor$2, + onValue: (value) => jni.PropertyFactory.textHaloColor(value.toARGB32()), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textHaloWidth => + jLayer.getTextHaloWidth().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextHaloWidth; + + @override + set textHaloWidth(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textHaloWidth$1, + onValue: (value) => jni.PropertyFactory.textHaloWidth( + value.toJFloat()..releasedBy(arena), + ), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textHaloBlur => + jLayer.getTextHaloBlur().toDart(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextHaloBlur; + + @override + set textHaloBlur(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textHaloBlur$1, + onValue: (value) => + jni.PropertyFactory.textHaloBlur(value.toJFloat()..releasedBy(arena)), + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textTranslate => + jLayer.getTextTranslate().toDartOffset(releaseOriginal: true) ?? + SymbolStyleLayer.defaultTextTranslate; + + @override + set textTranslate(PropertyValue property) => using((arena) { + final jProperty = property.apply( + arena: arena, + onExpression: jni.PropertyFactory.textTranslate$1, + onValue: (value) { + final floats = value.toJFloatArray(arena); + return jni.PropertyFactory.textTranslate(floats); + }, + ); + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); + + @override + PropertyValue get textTranslateAnchor => + jLayer.getTextTranslateAnchor().toDartReferenceSpace( + releaseOriginal: true, + ) ?? + SymbolStyleLayer.defaultTextTranslateAnchor; + + @override + set textTranslateAnchor(PropertyValue value) => + using((arena) { + final jni.PropertyValue? jProperty; + if (value.isExpression) { + final jExpression = value.expression.toJExpression(arena) + ?..releasedBy(arena); + jProperty = jni.PropertyFactory.textTranslateAnchor$1(jExpression); + } else { + final jValue = value.value.name.toJString()..releasedBy(arena); + jProperty = jni.PropertyFactory.textTranslateAnchor(jValue); + } + jProperty?.releasedBy(arena); + jLayer.setProperty(jProperty); + }); +} diff --git a/packages/maplibre_android/lib/src/style_controller.dart b/packages/maplibre_android/lib/src/style_controller.dart index b914d494b..acc42513c 100644 --- a/packages/maplibre_android/lib/src/style_controller.dart +++ b/packages/maplibre_android/lib/src/style_controller.dart @@ -13,122 +13,7 @@ class StyleControllerAndroid extends StyleController { String? aboveLayerId, int? atIndex, }) async => using((arena) { - final jId = layer.id.toJString()..releasedBy(arena); - final prevLayer = _jStyle.getLayer(jId); - if (prevLayer != null) { - throw Exception( - 'A Layer with the id "${layer.id}" already exists in the map style.', - ); - } - - jni.Expression? jFilter; - if (layer.filter case final filter?) { - final jFilterString = jsonEncode(filter).toJString()..releasedBy(arena); - jFilter = jni.Expression$Converter.convert$2(jFilterString) - ?..releasedBy(arena); - } - JString? jSourceLayer; - if (layer is StyleLayerWithSource) { - if (layer.sourceLayerId case final String id) { - jSourceLayer = id.toJString()..releasedBy(arena); - } - } - - final jni.Layer jLayer; - switch (layer) { - case StyleLayerWithSource(): - final jSourceId = layer.sourceId.toJString()..releasedBy(arena); - switch (layer) { - case FillStyleLayer(): - final jFillLayer = jni.FillLayer(jId, jSourceId); - if (jFilter != null) jFillLayer.setFilter(jFilter); - if (jSourceLayer != null) jFillLayer.setSourceLayer(jSourceLayer); - jLayer = jFillLayer; - case CircleStyleLayer(): - final jCircleLayer = jni.CircleLayer(jId, jSourceId); - if (jFilter != null) jCircleLayer.setFilter(jFilter); - if (jSourceLayer != null) jCircleLayer.setSourceLayer(jSourceLayer); - jLayer = jCircleLayer; - case FillExtrusionStyleLayer(): - final jFillExtrusionLayer = jni.FillExtrusionLayer(jId, jSourceId); - if (jFilter != null) jFillExtrusionLayer.setFilter(jFilter); - if (jSourceLayer != null) { - jFillExtrusionLayer.setSourceLayer(jSourceLayer); - } - jLayer = jFillExtrusionLayer; - case HeatmapStyleLayer(): - final jHeatmapLayer = jni.HeatmapLayer(jId, jSourceId); - if (jFilter != null) jHeatmapLayer.setFilter(jFilter); - if (jSourceLayer != null) { - jHeatmapLayer.setSourceLayer(jSourceLayer); - } - jLayer = jHeatmapLayer; - case HillshadeStyleLayer(): - final jHillshadeLayer = jni.HillshadeLayer(jId, jSourceId); - if (jSourceLayer != null) { - jHillshadeLayer.setSourceLayer(jSourceLayer); - } - jLayer = jHillshadeLayer; - case LineStyleLayer(): - final jLineLayer = jni.LineLayer(jId, jSourceId); - if (jFilter != null) jLineLayer.setFilter(jFilter); - if (jSourceLayer != null) jLineLayer.setSourceLayer(jSourceLayer); - jLayer = jLineLayer; - case RasterStyleLayer(): - final jRasterLayer = jni.RasterLayer(jId, jSourceId); - if (jSourceLayer != null) jRasterLayer.setSourceLayer(jSourceLayer); - jLayer = jRasterLayer; - case SymbolStyleLayer(): - final jSymbolLayer = jni.SymbolLayer(jId, jSourceId); - if (jFilter != null) jSymbolLayer.setFilter(jFilter); - if (jSourceLayer != null) jSymbolLayer.setSourceLayer(jSourceLayer); - jLayer = jSymbolLayer; - default: - throw UnimplementedError( - 'The Layer is not supported: ${layer.runtimeType}', - ); - } - default: - switch (layer) { - case BackgroundStyleLayer(): - jLayer = jni.BackgroundLayer(jId); - default: - throw UnimplementedError( - 'The Layer is not supported: ${layer.runtimeType}', - ); - } - } - - jLayer.setMinZoom(layer.minZoom); - jLayer.setMaxZoom(layer.maxZoom); - - // paint and layout properties - final layoutEntries = layer.layout.entries.toList(growable: false); - final paintEntries = layer.paint.entries.toList(growable: false); - final props = JArray( - jni.PropertyValue.nullableType(JObject.nullableType), - layoutEntries.length + paintEntries.length, - )..releasedBy(arena); - for (var i = 0; i < paintEntries.length; i++) { - final entry = paintEntries[i]; - props[i] = jni.PaintPropertyValue( - entry.key.toJString()..releasedBy(arena), - entry.value.toJObject()..releasedBy(arena), - T: JObject.type, - )..releasedBy(arena); - } - for (var i = 0; i < layoutEntries.length; i++) { - final entry = layoutEntries[i]; - props[paintEntries.length + i] = jni.LayoutPropertyValue( - entry.key.toJString()..releasedBy(arena), - entry.value.toJObject()..releasedBy(arena), - T: JObject.type, - )..releasedBy(arena); - } - jLayer.releasedBy(arena); - jLayer.setProperties(props); - - // add to style + final jLayer = (layer as StyleLayerAndroid).jLayer; if (belowLayerId case final String belowId) { _jStyle.addLayerBelow(jLayer, belowId.toJString()..releasedBy(arena)); } else if (aboveLayerId case final String aboveId) { diff --git a/packages/maplibre_android/tool/jnigen.dart b/packages/maplibre_android/tool/jnigen.dart index a006fdd3e..4bfdac239 100644 --- a/packages/maplibre_android/tool/jnigen.dart +++ b/packages/maplibre_android/tool/jnigen.dart @@ -64,6 +64,8 @@ void main(List args) { 'org.maplibre.android.style.expressions.Expression', 'org.maplibre.android.style.layers', 'org.maplibre.android.style.sources', + 'org.maplibre.android.style.types.Formatted', + 'org.maplibre.android.style.types.FormattedSection', 'org.maplibre.geojson.Feature', 'com.google.gson.Gson', 'com.google.gson.JsonObject', diff --git a/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/Helpers.swift b/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/Helpers.swift index 7b9d664dd..2a241d60f 100644 --- a/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/Helpers.swift +++ b/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/Helpers.swift @@ -15,20 +15,6 @@ public class Helpers: NSObject { target.setValue(value, forKey: field) } - @objc public static func parsePredicate(raw: String) -> NSPredicate? { - if let data = raw.data(using: .utf8) { - do { - let json = try JSONSerialization.jsonObject(with: data, options: []) - return NSPredicate(mglJSONObject: json) - } catch let e { - print("Couldn't parse NSPredicate from JSON: \(e)") - return nil - } - } else { - return nil - } - } - @objc public static func parseExpression( propertyName: String, expression: String ) -> NSExpression? { @@ -114,6 +100,11 @@ public class Helpers: NSObject { @objc public static func zoomLevelToAltitude(zoomLevel: Double, pitch: CGFloat, latitude: Double, size: CGSize) -> Double { MLNAltitudeForZoomLevel(zoomLevel, pitch, latitude, size) } + + @objc public static func createUIEdgeInsetsNSExpression(padding: [Double]) -> NSExpression { + let edgeInsets = UIEdgeInsets(top: CGFloat(padding[0]), left: CGFloat(padding[1]), bottom: CGFloat(padding[2]), right: CGFloat(padding[3])) + return NSExpression(forConstantValue: edgeInsets) + } } @objc(OfflinePackProgressCallbacks) diff --git a/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/MapLibreIos.h b/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/MapLibreIos.h index b4be2aa59..8728475e3 100644 --- a/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/MapLibreIos.h +++ b/packages/maplibre_ios/ios/maplibre_ios/Sources/maplibre_ios/MapLibreIos.h @@ -280,6 +280,7 @@ typedef unsigned int swift_uint4 __attribute__((__ext_vector_type__(4))); #pragma clang diagnostic ignored "-Watimport-in-framework-header" #endif @import CoreFoundation; +@import Foundation; @import ObjectiveC; #endif @@ -319,7 +320,6 @@ SWIFT_PROTOCOL_NAMED("FlutterApi") @end @class NSString; -@class NSPredicate; @class NSExpression; @protocol OfflinePackProgressCallbacks; @class NSURL; @@ -327,12 +327,12 @@ SWIFT_PROTOCOL_NAMED("FlutterApi") SWIFT_CLASS_NAMED("Helpers") @interface Helpers : NSObject + (void)setValueWithTarget:(NSObject * _Nonnull)target field:(NSString * _Nonnull)field value:(NSObject * _Nonnull)value; -+ (NSPredicate * _Nullable)parsePredicateWithRaw:(NSString * _Nonnull)raw SWIFT_WARN_UNUSED_RESULT; + (NSExpression * _Nullable)parseExpressionWithPropertyName:(NSString * _Nonnull)propertyName expression:(NSString * _Nonnull)expression SWIFT_WARN_UNUSED_RESULT; + (void)createOfflinePackProgressListenerWithCallbacks:(id _Nonnull)callbacks; + (void)removeOfflinePackProgressListenerWithCallbacks:(id _Nonnull)callbacks; + (MLNTilePyramidOfflineRegion * _Nonnull)createTilePyramidOfflineRegionWithStyleURL:(NSURL * _Nullable)styleURL south:(double)south west:(double)west east:(double)east north:(double)north fromZoomLevel:(double)minZoom toZoomLevel:(double)maxZoom SWIFT_WARN_UNUSED_RESULT; + (double)zoomLevelToAltitudeWithZoomLevel:(double)zoomLevel pitch:(CGFloat)pitch latitude:(double)latitude size:(CGSize)size SWIFT_WARN_UNUSED_RESULT; ++ (NSExpression * _Nonnull)createUIEdgeInsetsNSExpressionWithPadding:(NSArray * _Nonnull)padding SWIFT_WARN_UNUSED_RESULT; - (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER; @end diff --git a/packages/maplibre_ios/lib/src/extensions.dart b/packages/maplibre_ios/lib/src/extensions.dart index 4e9dca53b..0a7231289 100644 --- a/packages/maplibre_ios/lib/src/extensions.dart +++ b/packages/maplibre_ios/lib/src/extensions.dart @@ -1,7 +1,8 @@ import 'dart:convert'; import 'dart:ffi' hide Size; -import 'package:flutter/cupertino.dart'; +import 'package:ffi/ffi.dart'; +import 'package:flutter/material.dart'; import 'package:maplibre_ios/src/maplibre_ffi.g.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:objective_c/objective_c.dart'; @@ -82,6 +83,12 @@ extension EdgeInsetsExt on EdgeInsets { ..right = right; return insets; } + + /// Convert an [EdgeInsets] to a [NSExpression] representing a padding array. + NSExpression toNSExpression() { + final nsArray = [top, left, bottom, right].toNSArray(); + return Helpers.createUIEdgeInsetsNSExpressionWithPadding(nsArray); + } } /// Internal extensions on [MLNCoordinateBounds]. @@ -348,7 +355,7 @@ extension MLNOfflinePackExt on MLNOfflinePack { extension NSNotificationExt on NSNotification { /// Get a [MLNOfflinePack] from an [NSNotification]. MLNOfflinePack? get offlinePack { - final obj = object; + final obj = this.object; if (obj == null) return null; if (!MLNOfflinePack.isA(obj)) return null; return MLNOfflinePack.as(obj); @@ -363,5 +370,201 @@ extension GeographicExt on Geographic { ..latitude = lat; } +/// Extension methods for the [Color] class. Not exported publicly. +extension ColorExt on Color { + /// Convert a [Color] to a [UIColor]. + UIColor toUIColor() => UIColor.colorWithRed(r, green: g, blue: b, alpha: a); +} + /// UTF8 Encoding const nsUTF8StringEncoding = 4; + +/// Internal extensions on [Expression]. Not exported publicly. +extension ExpressionExt on Expression { + /// Convert a [PropertyValue] to a [NSExpression]. + NSExpression toNSExpression() { + final nsArray = this.json.toNSArray(); + return NSExpression.expressionWithMLNJSONObject(nsArray); + } + + /// Convert a [PropertyValue] to a [NSPredicate]. + NSPredicate toNSPredicate() { + final nsArray = this.json.toNSArray(); + return NSPredicate.predicateWithMLNJSONObject(nsArray); + } +} + +/// Internal extensions on [List]. Not exported publicly. +extension DartListToNSArray on List { + /// Alternative to [DartListToNSArray] that allows [Object?]. + NSArray toNSArray() => + NSArray.of(map((e) => toObjCObject(e, convertOther: toObjCObject))); +} + +/// Extension to convert between [UIColor] and Dart's [Color]. +extension UIColorExt on UIColor { + /// Converts a [UIColor] to a Dart [Color]. + Color toDartColor() { + final rPtr = calloc.allocate(sizeOf()); + final gPtr = calloc.allocate(sizeOf()); + final bPtr = calloc.allocate(sizeOf()); + final aPtr = calloc.allocate(sizeOf()); + try { + final success = getRed(rPtr, green: gPtr, blue: bPtr, alpha: aPtr); + if (!success) { + debugPrint( + 'Failed to convert UIColor to Color. Falling back to transparent.', + ); + return Colors.transparent; + } + return Color.fromARGB( + (aPtr.value * 255).round(), + (rPtr.value * 255).round(), + (gPtr.value * 255).round(), + (bPtr.value * 255).round(), + ); + } finally { + calloc.free(rPtr); + calloc.free(gPtr); + calloc.free(bPtr); + calloc.free(aPtr); + } + } +} + +/// Internal extensions on [NSExpression]. Not exported publicly. +extension NSExpressionExt on NSExpression { + /// To [Expression] + Expression toExpression() { + final json = toDartObject( + mgl_jsonExpressionObject, + convertOther: toDartObject, + ); + return Expression.fromJson(json as List); + } + + /// Convert a [NSExpression] to a Dart [PropertyValue] of type [V]. + PropertyValue? toPropertyValue() { + if (expressionType == NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = constantValue; + if (ffiValue == null) return null; + final value = toDartObject(ffiValue) as V; + return PropertyValue.value(value); + } else { + return PropertyValue.expression(toExpression()); + } + } + + /// Convert a [NSExpression] to a Dart [PropertyValue] of type [Color]. + PropertyValue? toColorPropertyValue() { + if (expressionType == NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = constantValue; + if (ffiValue == null) return null; + final value = UIColor.as(ffiValue).toDartColor(); + return PropertyValue.value(value); + } else { + return PropertyValue.expression(toExpression()); + } + } + + /// Convert a [NSExpression] to a Dart [PropertyValue] of type [E]. + PropertyValue? toEnumPropertyValue(List enumValues) { + if (expressionType == NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = constantValue; + if (ffiValue == null) return null; + final value = NSString.as(ffiValue).toDartString(); + return PropertyValue.value( + enumValues.firstWhere( + (e) => e.name == value, + orElse: () => throw StateError('Invalid enum value: $value'), + ), + ); + } else { + return PropertyValue.expression(toExpression()); + } + } + + /// Convert a [NSExpression] to a Dart [PropertyValue] of type [Offset]. + PropertyValue? toOffsetPropertyValue() { + if (expressionType == NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = constantValue; + if (ffiValue == null) return null; + final nsArray = NSArray.as(ffiValue); + final offset = Offset( + NSNumber.as(nsArray.objectAtIndex(0)).doubleValue, + NSNumber.as(nsArray.objectAtIndex(1)).doubleValue, + ); + return PropertyValue.value(offset); + } else { + return PropertyValue.expression(toExpression()); + } + } + + /// Convert a [NSExpression] to a Dart [PropertyValue] of type [EdgeInsets]. + PropertyValue? toEdgeInsetsPropertyValue() { + if (expressionType == NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = constantValue; + if (ffiValue == null) return null; + final nsArray = NSArray.as(ffiValue); + final offset = EdgeInsets.only( + top: NSNumber.as(nsArray.objectAtIndex(0)).doubleValue, + left: NSNumber.as(nsArray.objectAtIndex(1)).doubleValue, + bottom: NSNumber.as(nsArray.objectAtIndex(2)).doubleValue, + right: NSNumber.as(nsArray.objectAtIndex(3)).doubleValue, + ); + return PropertyValue.value(offset); + } else { + return PropertyValue.expression(toExpression()); + } + } +} + +/// Internal extensions on [NSPredicate]. Not exported publicly. +extension NSPredicateExt on NSPredicate { + /// Convert a [NSPredicate] to a Dart [Expression]. + Expression toExpression() { + final json = toDartObject( + mgl_jsonExpressionObject, + convertOther: toDartObject, + ); + return Expression.fromJson(json as List); + } +} + +/// Internal extensions on [bool]. +extension BoolExt on bool { + /// Convert a [bool] to a [NSNumber]. + NSNumber toNSNumber() => NSNumberCreation.numberWithBool(this); +} + +/// Internal extensions on [PropertyValue]. +extension PropertyValueExt on PropertyValue { + /// Convert a [PropertyValue] to a [NSExpression]. + NSExpression toNSExpression() { + if (isExpression) { + return expression.toNSExpression(); + } else { + final value = this.value; + final ObjCObject objcObject; + switch (value) { + case Color(): + objcObject = value.toUIColor(); + case Offset(): + final nsArray = NSMutableArray.new$()..init(); + nsArray.addObject(value.dx.toNSNumber()); + nsArray.addObject(value.dy.toNSNumber()); + objcObject = nsArray; + case EdgeInsets(): + final nsArray = NSMutableArray.new$()..init(); + nsArray.addObject(value.top.toNSNumber()); + nsArray.addObject(value.left.toNSNumber()); + nsArray.addObject(value.bottom.toNSNumber()); + nsArray.addObject(value.right.toNSNumber()); + objcObject = nsArray; + default: + objcObject = value.toNSObject(); + } + return NSExpression.expressionForConstantValue(objcObject); + } + } +} diff --git a/packages/maplibre_ios/lib/src/map_state.dart b/packages/maplibre_ios/lib/src/map_state.dart index 1f8134f58..02ee2d5c2 100644 --- a/packages/maplibre_ios/lib/src/map_state.dart +++ b/packages/maplibre_ios/lib/src/map_state.dart @@ -1,11 +1,11 @@ import 'dart:async'; -import 'dart:convert'; import 'dart:ffi'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:maplibre_ios/src/extensions.dart'; import 'package:maplibre_ios/src/maplibre_ffi.g.dart'; +import 'package:maplibre_ios/src/style/layers/style_layer.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:objective_c/objective_c.dart'; diff --git a/packages/maplibre_ios/lib/src/maplibre_ffi.g.dart b/packages/maplibre_ios/lib/src/maplibre_ffi.g.dart index ce4f9fdb8..e9dabc58a 100644 --- a/packages/maplibre_ios/lib/src/maplibre_ffi.g.dart +++ b/packages/maplibre_ios/lib/src/maplibre_ffi.g.dart @@ -536,6 +536,9 @@ sealed class CGBlendMode { static const kCGBlendModePlusDarker = 26; static const kCGBlendModePlusLighter = 27;} +final class CGColor extends ffi.Opaque{ +} + final class CGContext extends ffi.Opaque{ } @@ -543,6 +546,27 @@ final class CGImage extends ffi.Opaque{ } +/// WARNING: CIColor is a stub. To generate bindings for this class, include +/// CIColor in your config's objc-interfaces list. +/// +/// CIColor +extension type CIColor._(objc.ObjCObject object$) implements objc.ObjCObject,objc.NSObject,objc.NSSecureCoding,objc.NSCopying { + /// Constructs a [CIColor] that points to the same underlying object as [other]. + CIColor.as(objc.ObjCObject other) : object$ = other { + objc.checkOsVersionInternal('CIColor', iOS: (false, (5, 0, 0))); + } + + /// Constructs a [CIColor] that wraps the given raw object pointer. + CIColor.fromPointer(ffi.Pointer other, + {bool retain = false, bool release = false}) : + object$ = objc.ObjCObject(other, retain: retain, release: release) { + objc.checkOsVersionInternal('CIColor', iOS: (false, (5, 0, 0))); + } + + +} + + /// WARNING: CIImage is a stub. To generate bindings for this class, include /// CIImage in your config's objc-interfaces list. /// @@ -920,6 +944,13 @@ _objc_msgSend_xtuoz7(_class_Helpers, _sel_createOfflinePackProgressListenerWithC } + /// createUIEdgeInsetsNSExpressionWithPadding: + static NSExpression createUIEdgeInsetsNSExpressionWithPadding(objc.NSArray padding) { + final $ret = _objc_msgSend_1sotr3r(_class_Helpers, _sel_createUIEdgeInsetsNSExpressionWithPadding_, padding.ref.pointer); + return NSExpression.fromPointer($ret, retain: true, release: true); + } + + /// new static Helpers new$() { final $ret = _objc_msgSend_151sglz(_class_Helpers, _sel_new); @@ -934,13 +965,6 @@ _objc_msgSend_xtuoz7(_class_Helpers, _sel_createOfflinePackProgressListenerWithC } - /// parsePredicateWithRaw: - static NSPredicate? parsePredicateWithRaw(objc.NSString raw) { - final $ret = _objc_msgSend_1sotr3r(_class_Helpers, _sel_parsePredicateWithRaw_, raw.ref.pointer); - return $ret.address == 0 ? null : NSPredicate.fromPointer($ret, retain: true, release: true); - } - - /// removeOfflinePackProgressListenerWithCallbacks: static void removeOfflinePackProgressListenerWithCallbacks(OfflinePackProgressCallbacks callbacks) { _objc_msgSend_xtuoz7(_class_Helpers, _sel_removeOfflinePackProgressListenerWithCallbacks_, callbacks.ref.pointer); @@ -977,10 +1001,33 @@ extension Helpers$Methods on Helpers { } +/// MLNAdditions +extension MLNAdditions on NSPredicate { + + /// An equivalent Foundation object that can be serialized as JSON. +/// +/// The Foundation object conforms to the +/// [MapLibre Style Spec](https://maplibre.org/maplibre-style-spec/expressions/). +/// See the +/// “[Predicates and Expressions](../predicates-and-expressions.html)” +/// guide for a correspondence of operators and types between the style +/// specification and the `NSPredicate` representation used by this SDK. +/// +/// You can use `NSJSONSerialization` to serialize the Foundation object as data to +/// write to a file. + objc.ObjCObject get mgl_jsonExpressionObject { + objc.checkOsVersionInternal('NSPredicate.mgl_jsonExpressionObject', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.pointer, _sel_mgl_jsonExpressionObject); + return objc.ObjCObject($ret, retain: true, release: true); + } + +} + + /// Methods for creating expressions that use Mapbox-specific functionality and for /// converting to and from the JSON format defined in the /// MapLibre Style Spec. -extension MLNAdditions on NSExpression { +extension MLNAdditions$1 on NSExpression { /// Returns a copy of the receiver localized into the given locale. /// @@ -21877,14 +21924,12 @@ sealed class NSExpressionType { static const NSConditionalExpressionType = 20;} -/// WARNING: NSPredicate is a stub. To generate bindings for this class, include -/// NSPredicate in your config's objc-interfaces list. -/// /// NSPredicate extension type NSPredicate._(objc.ObjCObject object$) implements objc.ObjCObject,objc.NSObject,objc.NSSecureCoding,objc.NSCopying { /// Constructs a [NSPredicate] that points to the same underlying object as [other]. NSPredicate.as(objc.ObjCObject other) : object$ = other { objc.checkOsVersionInternal('NSPredicate', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + assert(isA(object$)); } /// Constructs a [NSPredicate] that wraps the given raw object pointer. @@ -21892,9 +21937,167 @@ extension type NSPredicate._(objc.ObjCObject object$) implements objc.ObjCObject {bool retain = false, bool release = false}) : object$ = objc.ObjCObject(other, retain: retain, release: release) { objc.checkOsVersionInternal('NSPredicate', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [NSPredicate]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5(obj.ref.pointer, _sel_isKindOfClass_, _class_NSPredicate); + + /// alloc + static NSPredicate alloc() { + final $ret = _objc_msgSend_151sglz(_class_NSPredicate, _sel_alloc); + return NSPredicate.fromPointer($ret, retain: false, release: true); + } + + + /// allocWithZone: + static NSPredicate allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428(_class_NSPredicate, _sel_allocWithZone_, zone); + return NSPredicate.fromPointer($ret, retain: false, release: true); + } + + + /// new + static NSPredicate new$() { + final $ret = _objc_msgSend_151sglz(_class_NSPredicate, _sel_new); + return NSPredicate.fromPointer($ret, retain: false, release: true); + } + + + /// predicateFromMetadataQueryString: + static NSPredicate? predicateFromMetadataQueryString(objc.NSString queryString) { + objc.checkOsVersionInternal('NSPredicate.predicateFromMetadataQueryString:', iOS: (true, null), macOS: (false, (10, 9, 0))); + final $ret = _objc_msgSend_1sotr3r(_class_NSPredicate, _sel_predicateFromMetadataQueryString_, queryString.ref.pointer); + return $ret.address == 0 ? null : NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// predicateWithBlock: + static NSPredicate predicateWithBlock(objc.ObjCBlock?, objc.NSDictionary?)> block) { + objc.checkOsVersionInternal('NSPredicate.predicateWithBlock:', iOS: (false, (4, 0, 0)), macOS: (false, (10, 6, 0))); + final $ret = _objc_msgSend_nnxkei(_class_NSPredicate, _sel_predicateWithBlock_, block.ref.pointer); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// predicateWithFormat: + static NSPredicate predicateWithFormat(objc.NSString predicateFormat) { + objc.checkOsVersionInternal('NSPredicate.predicateWithFormat:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_1sotr3r(_class_NSPredicate, _sel_predicateWithFormat_, predicateFormat.ref.pointer); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// predicateWithFormat:argumentArray: + static NSPredicate predicateWithFormat$1(objc.NSString predicateFormat, {objc.NSArray? argumentArray}) { + objc.checkOsVersionInternal('NSPredicate.predicateWithFormat:argumentArray:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_15qeuct(_class_NSPredicate, _sel_predicateWithFormat_argumentArray_, predicateFormat.ref.pointer, argumentArray?.ref.pointer ?? ffi.nullptr); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// Returns a predicate equivalent to the given Foundation object deserialized +/// from JSON data. +/// +/// The Foundation object is interpreted according to the +/// [MapLibre Style Spec](https://maplibre.org/maplibre-style-spec/expressions/). +/// See the +/// “[Predicates and Expressions](../predicates-and-expressions.html)” +/// guide for a correspondence of operators and types between the style +/// specification and the `NSPredicate` representation used by this SDK. +/// +/// @param object A Foundation object deserialized from JSON data, for example +/// using `NSJSONSerialization`. +/// @return An initialized predicate equivalent to `object`, suitable for use +/// with the ``MLNVectorStyleLayer/predicate`` property. + static NSPredicate predicateWithMLNJSONObject(objc.ObjCObject object) { + objc.checkOsVersionInternal('NSPredicate.predicateWithMLNJSONObject:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_1sotr3r(_class_NSPredicate, _sel_predicateWithMLNJSONObject_, object.ref.pointer); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// predicateWithValue: + static NSPredicate predicateWithValue(bool value) { + objc.checkOsVersionInternal('NSPredicate.predicateWithValue:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_1t6aok9(_class_NSPredicate, _sel_predicateWithValue_, value); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + + + /// supportsSecureCoding + static bool getSupportsSecureCoding() { + return _objc_msgSend_91o635(_class_NSPredicate, _sel_supportsSecureCoding); + + } + /// Returns a new instance of NSPredicate constructed with the default `new` method. + NSPredicate() : this.as(new$().object$); + +} + +extension NSPredicate$Methods on NSPredicate { + + /// allowEvaluation + void allowEvaluation() { + objc.checkOsVersionInternal('NSPredicate.allowEvaluation', iOS: (false, (7, 0, 0)), macOS: (false, (10, 9, 0))); +_objc_msgSend_1pl9qdv(object$.ref.pointer, _sel_allowEvaluation); + + } + + + /// encodeWithCoder: + void encodeWithCoder(objc.NSCoder coder) { +_objc_msgSend_xtuoz7(object$.ref.pointer, _sel_encodeWithCoder_, coder.ref.pointer); + + } + + + /// evaluateWithObject: + bool evaluateWithObject(objc.ObjCObject? object) { + objc.checkOsVersionInternal('NSPredicate.evaluateWithObject:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + return _objc_msgSend_19nvye5(object$.ref.pointer, _sel_evaluateWithObject_, object?.ref.pointer ?? ffi.nullptr); + + } + + + /// evaluateWithObject:substitutionVariables: + bool evaluateWithObject$1(objc.ObjCObject? object, {objc.NSDictionary? substitutionVariables}) { + objc.checkOsVersionInternal('NSPredicate.evaluateWithObject:substitutionVariables:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 5, 0))); + return _objc_msgSend_1lsax7n(object$.ref.pointer, _sel_evaluateWithObject_substitutionVariables_, object?.ref.pointer ?? ffi.nullptr, substitutionVariables?.ref.pointer ?? ffi.nullptr); + } + /// init + NSPredicate init() { + objc.checkOsVersionInternal('NSPredicate.init', iOS: (false, (2, 0, 0)), macOS: (false, (10, 0, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.retainAndReturnPointer(), _sel_init); + return NSPredicate.fromPointer($ret, retain: false, release: true); + } + + + /// initWithCoder: + NSPredicate? initWithCoder(objc.NSCoder coder) { + final $ret = _objc_msgSend_1sotr3r(object$.ref.retainAndReturnPointer(), _sel_initWithCoder_, coder.ref.pointer); + return $ret.address == 0 ? null : NSPredicate.fromPointer($ret, retain: false, release: true); + } + + + /// predicateFormat + objc.NSString get predicateFormat { + objc.checkOsVersionInternal('NSPredicate.predicateFormat', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.pointer, _sel_predicateFormat); + return objc.NSString.fromPointer($ret, retain: true, release: true); + } + + + /// predicateWithSubstitutionVariables: + NSPredicate predicateWithSubstitutionVariables(objc.NSDictionary variables) { + objc.checkOsVersionInternal('NSPredicate.predicateWithSubstitutionVariables:', iOS: (false, (3, 0, 0)), macOS: (false, (10, 4, 0))); + final $ret = _objc_msgSend_1sotr3r(object$.ref.pointer, _sel_predicateWithSubstitutionVariables_, variables.ref.pointer); + return NSPredicate.fromPointer($ret, retain: true, release: true); + } + } @@ -22879,6 +23082,56 @@ extension ObjCBlock_bool_ffiVoid_MLNMapView_idMLNAnnotation$CallExtension on obj } +/// Construction methods for `objc.ObjCBlock?, objc.NSDictionary?)>`. +abstract final class ObjCBlock_bool_objcObjCObjectImpl_NSDictionary { + /// Returns a block that wraps the given raw block pointer. + static objc.ObjCBlock?, objc.NSDictionary?)> fromPointer(ffi.Pointer pointer, + {bool retain = false, bool release = false}) => + objc.ObjCBlock?, objc.NSDictionary?)>(pointer, retain: retain, release: release); + + /// Creates a block from a C function pointer. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + static objc.ObjCBlock?, objc.NSDictionary?)> fromFunctionPointer(ffi.Pointer arg0, ffi.Pointer arg1)>> ptr) => + objc.ObjCBlock?, objc.NSDictionary?)>(objc.newPointerBlock(_fnPtrCallable, ptr.cast()), + retain: false, release: true); + + /// Creates a block from a Dart function. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock?, objc.NSDictionary?)> fromFunction(bool Function(objc.ObjCObject? , objc.NSDictionary? ) fn, + {bool keepIsolateAlive = true}) => + objc.ObjCBlock?, objc.NSDictionary?)>(objc.newClosureBlock(_closureCallable, (ffi.Pointer arg0, ffi.Pointer arg1) => fn(arg0.address == 0 ? null : objc.ObjCObject(arg0, retain: true, release: true), arg1.address == 0 ? null : objc.NSDictionary.fromPointer(arg1, retain: true, release: true)), keepIsolateAlive), + retain: false, release: true); + + static bool _fnPtrTrampoline( + ffi.Pointer block, ffi.Pointer arg0, ffi.Pointer arg1) => + block.ref.target.cast arg0, ffi.Pointer arg1)>>() + .asFunction , ffi.Pointer )>()(arg0, arg1); + static ffi.Pointer _fnPtrCallable = ffi.Pointer.fromFunction< + ffi.Bool Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(_fnPtrTrampoline , false).cast(); + static bool _closureTrampoline( + ffi.Pointer block, ffi.Pointer arg0, ffi.Pointer arg1) => + (objc.getBlockClosure(block) as bool Function(ffi.Pointer , ffi.Pointer ))(arg0, arg1); + static ffi.Pointer _closureCallable = ffi.Pointer.fromFunction< + ffi.Bool Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(_closureTrampoline , false).cast(); +} + +/// Call operator for `objc.ObjCBlock?, objc.NSDictionary?)>`. +extension ObjCBlock_bool_objcObjCObjectImpl_NSDictionary$CallExtension on objc.ObjCBlock?, objc.NSDictionary?)> { + bool call(objc.ObjCObject? arg0, objc.NSDictionary? arg1) =>ref.pointer.ref.invoke.cast block, ffi.Pointer arg0, ffi.Pointer arg1)>>() + .asFunction , ffi.Pointer , ffi.Pointer )>()( + ref.pointer, arg0?.ref.pointer ?? ffi.nullptr, arg1?.ref.pointer ?? ffi.nullptr); +} + + /// Construction methods for `objc.ObjCBlock`. abstract final class ObjCBlock_ffiVoid { /// Returns a block that wraps the given raw block pointer. @@ -27350,14 +27603,12 @@ extension type UIButton._(objc.ObjCObject object$) implements objc.ObjCObject,UI } -/// WARNING: UIColor is a stub. To generate bindings for this class, include -/// UIColor in your config's objc-interfaces list. -/// /// UIColor extension type UIColor._(objc.ObjCObject object$) implements objc.ObjCObject,objc.NSObject,objc.NSSecureCoding,objc.NSCopying { /// Constructs a [UIColor] that points to the same underlying object as [other]. UIColor.as(objc.ObjCObject other) : object$ = other { objc.checkOsVersionInternal('UIColor', iOS: (false, (2, 0, 0))); + assert(isA(object$)); } /// Constructs a [UIColor] that wraps the given raw object pointer. @@ -27365,9 +27616,426 @@ extension type UIColor._(objc.ObjCObject object$) implements objc.ObjCObject,obj {bool retain = false, bool release = false}) : object$ = objc.ObjCObject(other, retain: retain, release: release) { objc.checkOsVersionInternal('UIColor', iOS: (false, (2, 0, 0))); + assert(isA(object$)); + } + + /// Returns whether [obj] is an instance of [UIColor]. + static bool isA(objc.ObjCObject obj) => _objc_msgSend_19nvye5(obj.ref.pointer, _sel_isKindOfClass_, _class_UIColor); + + /// alloc + static UIColor alloc() { + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_alloc); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// allocWithZone: + static UIColor allocWithZone(ffi.Pointer zone) { + final $ret = _objc_msgSend_1cwp428(_class_UIColor, _sel_allocWithZone_, zone); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// blackColor + static UIColor getBlackColor() { + objc.checkOsVersionInternal('UIColor.blackColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_blackColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// blueColor + static UIColor getBlueColor() { + objc.checkOsVersionInternal('UIColor.blueColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_blueColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// brownColor + static UIColor getBrownColor() { + objc.checkOsVersionInternal('UIColor.brownColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_brownColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// clearColor + static UIColor getClearColor() { + objc.checkOsVersionInternal('UIColor.clearColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_clearColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithCGColor: + static UIColor colorWithCGColor(ffi.Pointer cgColor) { + objc.checkOsVersionInternal('UIColor.colorWithCGColor:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1rsocyz(_class_UIColor, _sel_colorWithCGColor_, cgColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithCIColor: + static UIColor colorWithCIColor(CIColor ciColor) { + objc.checkOsVersionInternal('UIColor.colorWithCIColor:', iOS: (false, (5, 0, 0))); + final $ret = _objc_msgSend_1sotr3r(_class_UIColor, _sel_colorWithCIColor_, ciColor.ref.pointer); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithDisplayP3Red:green:blue:alpha: + static UIColor colorWithDisplayP3Red(double displayP3Red, {required double green,required double blue,required double alpha}) { + objc.checkOsVersionInternal('UIColor.colorWithDisplayP3Red:green:blue:alpha:', iOS: (false, (10, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(_class_UIColor, _sel_colorWithDisplayP3Red_green_blue_alpha_, displayP3Red, green, blue, alpha); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithHue:saturation:brightness:alpha: + static UIColor colorWithHue(double hue, {required double saturation,required double brightness,required double alpha}) { + objc.checkOsVersionInternal('UIColor.colorWithHue:saturation:brightness:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(_class_UIColor, _sel_colorWithHue_saturation_brightness_alpha_, hue, saturation, brightness, alpha); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithPatternImage: + static UIColor colorWithPatternImage(UIImage image) { + objc.checkOsVersionInternal('UIColor.colorWithPatternImage:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1sotr3r(_class_UIColor, _sel_colorWithPatternImage_, image.ref.pointer); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithRed:green:blue:alpha: + static UIColor colorWithRed(double red, {required double green,required double blue,required double alpha}) { + objc.checkOsVersionInternal('UIColor.colorWithRed:green:blue:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(_class_UIColor, _sel_colorWithRed_green_blue_alpha_, red, green, blue, alpha); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithRed:green:blue:alpha:exposure: + static UIColor colorWithRed$1(double red, {required double green,required double blue,required double alpha,required double exposure}) { + objc.checkOsVersionInternal('UIColor.colorWithRed:green:blue:alpha:exposure:', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_mvjtfi(_class_UIColor, _sel_colorWithRed_green_blue_alpha_exposure_, red, green, blue, alpha, exposure); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithRed:green:blue:alpha:linearExposure: + static UIColor colorWithRed$2(double red, {required double green,required double blue,required double alpha,required double linearExposure}) { + objc.checkOsVersionInternal('UIColor.colorWithRed:green:blue:alpha:linearExposure:', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_mvjtfi(_class_UIColor, _sel_colorWithRed_green_blue_alpha_linearExposure_, red, green, blue, alpha, linearExposure); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithWhite:alpha: + static UIColor colorWithWhite(double white, {required double alpha}) { + objc.checkOsVersionInternal('UIColor.colorWithWhite:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1n2vn5t(_class_UIColor, _sel_colorWithWhite_alpha_, white, alpha); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// cyanColor + static UIColor getCyanColor() { + objc.checkOsVersionInternal('UIColor.cyanColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_cyanColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// darkGrayColor + static UIColor getDarkGrayColor() { + objc.checkOsVersionInternal('UIColor.darkGrayColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_darkGrayColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// grayColor + static UIColor getGrayColor() { + objc.checkOsVersionInternal('UIColor.grayColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_grayColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// greenColor + static UIColor getGreenColor() { + objc.checkOsVersionInternal('UIColor.greenColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_greenColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// lightGrayColor + static UIColor getLightGrayColor() { + objc.checkOsVersionInternal('UIColor.lightGrayColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_lightGrayColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// magentaColor + static UIColor getMagentaColor() { + objc.checkOsVersionInternal('UIColor.magentaColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_magentaColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// new + static UIColor new$() { + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_new); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// orangeColor + static UIColor getOrangeColor() { + objc.checkOsVersionInternal('UIColor.orangeColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_orangeColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// purpleColor + static UIColor getPurpleColor() { + objc.checkOsVersionInternal('UIColor.purpleColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_purpleColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// redColor + static UIColor getRedColor() { + objc.checkOsVersionInternal('UIColor.redColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_redColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// supportsSecureCoding + static bool getSupportsSecureCoding() { + return _objc_msgSend_91o635(_class_UIColor, _sel_supportsSecureCoding); + + } + + + /// whiteColor + static UIColor getWhiteColor() { + objc.checkOsVersionInternal('UIColor.whiteColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_whiteColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// yellowColor + static UIColor getYellowColor() { + objc.checkOsVersionInternal('UIColor.yellowColor', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_151sglz(_class_UIColor, _sel_yellowColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + /// Returns a new instance of UIColor constructed with the default `new` method. + UIColor() : this.as(new$().object$); + +} + +extension UIColor$Methods on UIColor { + + /// CGColor + ffi.Pointer get CGColor$1 { + objc.checkOsVersionInternal('UIColor.CGColor', iOS: (false, (2, 0, 0))); + return _objc_msgSend_2u9jmz(object$.ref.pointer, _sel_CGColor); + + } + + + /// CIColor + CIColor get CIColor$1 { + objc.checkOsVersionInternal('UIColor.CIColor', iOS: (false, (5, 0, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.pointer, _sel_CIColor); + return CIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorByApplyingContentHeadroom: + UIColor colorByApplyingContentHeadroom(double contentHeadroom) { + objc.checkOsVersionInternal('UIColor.colorByApplyingContentHeadroom:', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_oa8mke(object$.ref.pointer, _sel_colorByApplyingContentHeadroom_, contentHeadroom); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// colorWithAlphaComponent: + UIColor colorWithAlphaComponent(double alpha) { + objc.checkOsVersionInternal('UIColor.colorWithAlphaComponent:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_oa8mke(object$.ref.pointer, _sel_colorWithAlphaComponent_, alpha); + return UIColor.fromPointer($ret, retain: true, release: true); + } + + + /// encodeWithCoder: + void encodeWithCoder(objc.NSCoder coder) { +_objc_msgSend_xtuoz7(object$.ref.pointer, _sel_encodeWithCoder_, coder.ref.pointer); + + } + + + /// getHue:saturation:brightness:alpha: + bool getHue(ffi.Pointer hue, {required ffi.Pointer saturation,required ffi.Pointer brightness,required ffi.Pointer alpha}) { + objc.checkOsVersionInternal('UIColor.getHue:saturation:brightness:alpha:', iOS: (false, (5, 0, 0))); + return _objc_msgSend_xmvde7(object$.ref.pointer, _sel_getHue_saturation_brightness_alpha_, hue, saturation, brightness, alpha); + + } + + + /// getRed:green:blue:alpha: + bool getRed(ffi.Pointer red, {required ffi.Pointer green,required ffi.Pointer blue,required ffi.Pointer alpha}) { + objc.checkOsVersionInternal('UIColor.getRed:green:blue:alpha:', iOS: (false, (5, 0, 0))); + return _objc_msgSend_xmvde7(object$.ref.pointer, _sel_getRed_green_blue_alpha_, red, green, blue, alpha); + + } + + + /// getWhite:alpha: + bool getWhite(ffi.Pointer white, {required ffi.Pointer alpha}) { + objc.checkOsVersionInternal('UIColor.getWhite:alpha:', iOS: (false, (5, 0, 0))); + return _objc_msgSend_kcpn4z(object$.ref.pointer, _sel_getWhite_alpha_, white, alpha); + + } + + + /// init + UIColor init() { + objc.checkOsVersionInternal('UIColor.init', iOS: (false, (2, 0, 0)), macOS: (false, (10, 0, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.retainAndReturnPointer(), _sel_init); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithCGColor: + UIColor initWithCGColor(ffi.Pointer cgColor) { + objc.checkOsVersionInternal('UIColor.initWithCGColor:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1rsocyz(object$.ref.retainAndReturnPointer(), _sel_initWithCGColor_, cgColor); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithCIColor: + UIColor initWithCIColor(CIColor ciColor) { + objc.checkOsVersionInternal('UIColor.initWithCIColor:', iOS: (false, (5, 0, 0))); + final $ret = _objc_msgSend_1sotr3r(object$.ref.retainAndReturnPointer(), _sel_initWithCIColor_, ciColor.ref.pointer); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithCoder: + UIColor? initWithCoder(objc.NSCoder coder) { + final $ret = _objc_msgSend_1sotr3r(object$.ref.retainAndReturnPointer(), _sel_initWithCoder_, coder.ref.pointer); + return $ret.address == 0 ? null : UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithDisplayP3Red:green:blue:alpha: + UIColor initWithDisplayP3Red(double displayP3Red, {required double green,required double blue,required double alpha}) { + objc.checkOsVersionInternal('UIColor.initWithDisplayP3Red:green:blue:alpha:', iOS: (false, (10, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(object$.ref.retainAndReturnPointer(), _sel_initWithDisplayP3Red_green_blue_alpha_, displayP3Red, green, blue, alpha); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithHue:saturation:brightness:alpha: + UIColor initWithHue(double hue, {required double saturation,required double brightness,required double alpha}) { + objc.checkOsVersionInternal('UIColor.initWithHue:saturation:brightness:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(object$.ref.retainAndReturnPointer(), _sel_initWithHue_saturation_brightness_alpha_, hue, saturation, brightness, alpha); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithPatternImage: + UIColor initWithPatternImage(UIImage image) { + objc.checkOsVersionInternal('UIColor.initWithPatternImage:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1sotr3r(object$.ref.retainAndReturnPointer(), _sel_initWithPatternImage_, image.ref.pointer); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithRed:green:blue:alpha: + UIColor initWithRed(double red, {required double green,required double blue,required double alpha}) { + objc.checkOsVersionInternal('UIColor.initWithRed:green:blue:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_q2wq4h(object$.ref.retainAndReturnPointer(), _sel_initWithRed_green_blue_alpha_, red, green, blue, alpha); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithRed:green:blue:alpha:exposure: + UIColor initWithRed$1(double red, {required double green,required double blue,required double alpha,required double exposure}) { + objc.checkOsVersionInternal('UIColor.initWithRed:green:blue:alpha:exposure:', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_mvjtfi(object$.ref.retainAndReturnPointer(), _sel_initWithRed_green_blue_alpha_exposure_, red, green, blue, alpha, exposure); + return UIColor.fromPointer($ret, retain: false, release: true); } + /// initWithRed:green:blue:alpha:linearExposure: + UIColor initWithRed$2(double red, {required double green,required double blue,required double alpha,required double linearExposure}) { + objc.checkOsVersionInternal('UIColor.initWithRed:green:blue:alpha:linearExposure:', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_mvjtfi(object$.ref.retainAndReturnPointer(), _sel_initWithRed_green_blue_alpha_linearExposure_, red, green, blue, alpha, linearExposure); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// initWithWhite:alpha: + UIColor initWithWhite(double white, {required double alpha}) { + objc.checkOsVersionInternal('UIColor.initWithWhite:alpha:', iOS: (false, (2, 0, 0))); + final $ret = _objc_msgSend_1n2vn5t(object$.ref.retainAndReturnPointer(), _sel_initWithWhite_alpha_, white, alpha); + return UIColor.fromPointer($ret, retain: false, release: true); + } + + + /// linearExposure + double get linearExposure { + objc.checkOsVersionInternal('UIColor.linearExposure', iOS: (false, (26, 0, 0))); + return objc.useMsgSendVariants ? _objc_msgSend_1ukqyt8Fpret(object$.ref.pointer, _sel_linearExposure) : _objc_msgSend_1ukqyt8(object$.ref.pointer, _sel_linearExposure); + + } + + + /// set + void set() { + objc.checkOsVersionInternal('UIColor.set', iOS: (false, (2, 0, 0))); +_objc_msgSend_1pl9qdv(object$.ref.pointer, _sel_set); + + } + + + /// setFill + void setFill() { + objc.checkOsVersionInternal('UIColor.setFill', iOS: (false, (2, 0, 0))); +_objc_msgSend_1pl9qdv(object$.ref.pointer, _sel_setFill); + + } + + + /// setStroke + void setStroke() { + objc.checkOsVersionInternal('UIColor.setStroke', iOS: (false, (2, 0, 0))); +_objc_msgSend_1pl9qdv(object$.ref.pointer, _sel_setStroke); + + } + + + /// standardDynamicRangeColor + UIColor get standardDynamicRangeColor { + objc.checkOsVersionInternal('UIColor.standardDynamicRangeColor', iOS: (false, (26, 0, 0))); + final $ret = _objc_msgSend_151sglz(object$.ref.pointer, _sel_standardDynamicRangeColor); + return UIColor.fromPointer($ret, retain: true, release: true); + } + } @@ -31082,7 +31750,9 @@ late final _class_MLNVectorStyleLayer = objc.getClass("MLNVectorStyleLayer"); late final _class_MLNVectorTileSource = objc.getClass("MLNVectorTileSource"); late final _class_MapLibreRegistry = objc.getClass("MapLibreRegistry"); late final _class_NSExpression = objc.getClass("NSExpression"); +late final _class_NSPredicate = objc.getClass("NSPredicate"); late final _class_UIAction = objc.getClass("UIAction"); +late final _class_UIColor = objc.getClass("UIColor"); late final _class_UIHoverStyle = objc.getClass("UIHoverStyle"); late final _class_UIImage = objc.getClass("UIImage"); late final _class_UIMenu = objc.getClass("UIMenu"); @@ -31149,6 +31819,7 @@ final _objc_msgSend_1mhd1pt = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_1mpyy6yStret = objc.msgSendStretPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_1mz4wgw = objc.msgSendPointer.cast , ffi.Pointer , ffi.Long )>>().asFunction , ffi.Pointer , int )>(); +final _objc_msgSend_1n2vn5t = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Double , ffi.Double )>>().asFunction Function(ffi.Pointer , ffi.Pointer , double , double )>(); final _objc_msgSend_1nomli1 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.UnsignedLong , ffi.Pointer> )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , int , ffi.Pointer> )>(); final _objc_msgSend_1o8rmw3 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Long )>>().asFunction Function(ffi.Pointer , ffi.Pointer , int )>(); final _objc_msgSend_1o8sa9u = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , objc.CGRect , ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , objc.CGRect , ffi.Pointer , ffi.Pointer )>(); @@ -31158,6 +31829,7 @@ final _objc_msgSend_1pbhom5 = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); final _objc_msgSend_1qddrus = objc.msgSendPointer.cast , ffi.Pointer , ffi.UnsignedLong , ffi.Bool )>>().asFunction , ffi.Pointer , int , bool )>(); final _objc_msgSend_1qgnjih = objc.msgSendPointer.cast , ffi.Pointer , ffi.Long )>>().asFunction , ffi.Pointer , int )>(); +final _objc_msgSend_1rsocyz = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_1rz5npq = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.UnsignedLong )>>().asFunction Function(ffi.Pointer , ffi.Pointer , int )>(); final _objc_msgSend_1s40ged = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Double , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , double , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_1s56lr9 = objc.msgSendPointer.cast , ffi.Pointer , ffi.Bool )>>().asFunction , ffi.Pointer , bool )>(); @@ -31165,6 +31837,7 @@ final _objc_msgSend_1sbdo65 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_1sq2aut = objc.msgSendPointer.cast , ffi.Pointer , ffi.UnsignedLong , ffi.Pointer )>>().asFunction , ffi.Pointer , int , ffi.Pointer )>(); final _objc_msgSend_1swtepj = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); +final _objc_msgSend_1t6aok9 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Bool )>>().asFunction Function(ffi.Pointer , ffi.Pointer , bool )>(); final _objc_msgSend_1tczmpv = objc.msgSendPointer.cast , ffi.Pointer , ffi.Double )>>().asFunction , ffi.Pointer , double )>(); final _objc_msgSend_1tczmpvFpret = objc.msgSendFpretPointer.cast , ffi.Pointer , ffi.Double )>>().asFunction , ffi.Pointer , double )>(); final _objc_msgSend_1ts4niw = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); @@ -31198,6 +31871,7 @@ final _objc_msgSend_2nhnqw = objc.msgSendPointer.cast , ffi.Pointer , objc.CGRect , ffi.Pointer )>>().asFunction , ffi.Pointer , objc.CGRect , ffi.Pointer )>(); final _objc_msgSend_2olghrStret = objc.msgSendStretPointer.cast , ffi.Pointer , ffi.Pointer , objc.CGRect , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , objc.CGRect , ffi.Pointer )>(); final _objc_msgSend_2tjjtl = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); +final _objc_msgSend_2u9jmz = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_2xggvt = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.Double , ffi.Long )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , double , int )>(); final _objc_msgSend_3l1tu1 = objc.msgSendPointer.cast , ffi.Pointer , objc.CGPoint , ffi.Pointer )>>().asFunction , ffi.Pointer , objc.CGPoint , ffi.Pointer )>(); final _objc_msgSend_3l1tu1Stret = objc.msgSendStretPointer.cast , ffi.Pointer , ffi.Pointer , objc.CGPoint , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , objc.CGPoint , ffi.Pointer )>(); @@ -31251,6 +31925,7 @@ final _objc_msgSend_iy8iz6 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , MLNCoordinateQuad , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer , MLNCoordinateQuad , ffi.Pointer )>(); final _objc_msgSend_k7jknj = objc.msgSendPointer.cast , ffi.Pointer , ffi.UnsignedLong )>>().asFunction , ffi.Pointer , int )>(); final _objc_msgSend_k9iunc = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); +final _objc_msgSend_kcpn4z = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_knxebs = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); final _objc_msgSend_kvi515 = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Long , ffi.Long , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , int , int , ffi.Pointer )>(); final _objc_msgSend_kzdfm1 = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); @@ -31262,6 +31937,7 @@ final _objc_msgSend_mabicuFpret = objc.msgSendFpretPointer.cast , ffi.Pointer , ffi.Int64 , ffi.Pointer )>>().asFunction , ffi.Pointer , int , ffi.Pointer )>(); final _objc_msgSend_mus1wv = objc.msgSendPointer.cast , ffi.Pointer , CLLocationCoordinate2D , ffi.Pointer )>>().asFunction , ffi.Pointer , CLLocationCoordinate2D , ffi.Pointer )>(); final _objc_msgSend_mus1wvStret = objc.msgSendStretPointer.cast , ffi.Pointer , ffi.Pointer , CLLocationCoordinate2D , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , CLLocationCoordinate2D , ffi.Pointer )>(); +final _objc_msgSend_mvjtfi = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Double , ffi.Double , ffi.Double , ffi.Double , ffi.Double )>>().asFunction Function(ffi.Pointer , ffi.Pointer , double , double , double , double , double )>(); final _objc_msgSend_na2nx0 = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Bool , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , bool , ffi.Pointer )>(); final _objc_msgSend_nnxkei = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_o762yo = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); @@ -31272,6 +31948,7 @@ final _objc_msgSend_oftvaStret = objc.msgSendStretPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_pisvbv = objc.msgSendPointer.cast , ffi.Pointer , ffi.UnsignedLong )>>().asFunction , ffi.Pointer , int )>(); final _objc_msgSend_pov02z = objc.msgSendPointer.cast , ffi.Pointer , ffi.Long , ffi.Pointer )>>().asFunction , ffi.Pointer , int , ffi.Pointer )>(); +final _objc_msgSend_q2wq4h = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , ffi.Double , ffi.Double , ffi.Double , ffi.Double )>>().asFunction Function(ffi.Pointer , ffi.Pointer , double , double , double , double )>(); final _objc_msgSend_q9b4dz = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.UnsignedLong , ffi.Bool )>>().asFunction , ffi.Pointer , ffi.Pointer , int , bool )>(); final _objc_msgSend_qgt66z = objc.msgSendPointer.cast , ffi.Pointer , CGAffineTransform )>>().asFunction , ffi.Pointer , CGAffineTransform )>(); final _objc_msgSend_qj4fey = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); @@ -31296,6 +31973,7 @@ final _objc_msgSend_w7r098Fpret = objc.msgSendFpretPointer.cast Function(ffi.Pointer , ffi.Pointer , objc.CGPoint )>>().asFunction Function(ffi.Pointer , ffi.Pointer , objc.CGPoint )>(); final _objc_msgSend_x3m0f9 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer , CLLocationCoordinate2D , ffi.Double , ffi.Double , ffi.Double )>>().asFunction Function(ffi.Pointer , ffi.Pointer , CLLocationCoordinate2D , double , double , double )>(); final _objc_msgSend_xjcq8x = objc.msgSendPointer.cast , ffi.Pointer )>>().asFunction , ffi.Pointer )>(); +final _objc_msgSend_xmvde7 = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_xoapar = objc.msgSendPointer.cast , ffi.Pointer , ffi.UnsignedLong )>>().asFunction , ffi.Pointer , int )>(); final _objc_msgSend_xtuoz7 = objc.msgSendPointer.cast , ffi.Pointer , ffi.Pointer )>>().asFunction , ffi.Pointer , ffi.Pointer )>(); final _objc_msgSend_y1izi1 = objc.msgSendPointer.cast Function(ffi.Pointer , ffi.Pointer )>>().asFunction Function(ffi.Pointer , ffi.Pointer )>(); @@ -31308,7 +31986,9 @@ late final _protocol_FlutterApi = objc.getProtocol("FlutterApi"); late final _protocol_MLNFeature = objc.getProtocol("MLNFeature"); late final _protocol_MLNMapViewDelegate = objc.getProtocol("MLNMapViewDelegate"); late final _protocol_OfflinePackProgressCallbacks = objc.getProtocol("OfflinePackProgressCallbacks"); +late final _sel_CGColor = objc.registerName("CGColor"); late final _sel_CGImage = objc.registerName("CGImage"); +late final _sel_CIColor = objc.registerName("CIColor"); late final _sel_CIImage = objc.registerName("CIImage"); late final _sel_URL = objc.registerName("URL"); late final _sel_accessories = objc.registerName("accessories"); @@ -31371,8 +32051,11 @@ late final _sel_backgroundPatternTransition = objc.registerName("backgroundPatte late final _sel_baselineOffsetFromBottom = objc.registerName("baselineOffsetFromBottom"); late final _sel_beamWithPreferredLength_axis_ = objc.registerName("beamWithPreferredLength:axis:"); late final _sel_becomeFirstResponder = objc.registerName("becomeFirstResponder"); +late final _sel_blackColor = objc.registerName("blackColor"); +late final _sel_blueColor = objc.registerName("blueColor"); late final _sel_bounds = objc.registerName("bounds"); late final _sel_brightness = objc.registerName("brightness"); +late final _sel_brownColor = objc.registerName("brownColor"); late final _sel_buildMenuWithBuilder_ = objc.registerName("buildMenuWithBuilder:"); late final _sel_calibratedLatency = objc.registerName("calibratedLatency"); late final _sel_camera = objc.registerName("camera"); @@ -31420,9 +32103,21 @@ late final _sel_circleTranslationAnchor = objc.registerName("circleTranslationAn late final _sel_circleTranslationTransition = objc.registerName("circleTranslationTransition"); late final _sel_clearActionJournalLog = objc.registerName("clearActionJournalLog"); late final _sel_clearAmbientCacheWithCompletionHandler_ = objc.registerName("clearAmbientCacheWithCompletionHandler:"); +late final _sel_clearColor = objc.registerName("clearColor"); late final _sel_collection = objc.registerName("collection"); late final _sel_collisionBoundingPath = objc.registerName("collisionBoundingPath"); late final _sel_collisionBoundsType = objc.registerName("collisionBoundsType"); +late final _sel_colorByApplyingContentHeadroom_ = objc.registerName("colorByApplyingContentHeadroom:"); +late final _sel_colorWithAlphaComponent_ = objc.registerName("colorWithAlphaComponent:"); +late final _sel_colorWithCGColor_ = objc.registerName("colorWithCGColor:"); +late final _sel_colorWithCIColor_ = objc.registerName("colorWithCIColor:"); +late final _sel_colorWithDisplayP3Red_green_blue_alpha_ = objc.registerName("colorWithDisplayP3Red:green:blue:alpha:"); +late final _sel_colorWithHue_saturation_brightness_alpha_ = objc.registerName("colorWithHue:saturation:brightness:alpha:"); +late final _sel_colorWithPatternImage_ = objc.registerName("colorWithPatternImage:"); +late final _sel_colorWithRed_green_blue_alpha_ = objc.registerName("colorWithRed:green:blue:alpha:"); +late final _sel_colorWithRed_green_blue_alpha_exposure_ = objc.registerName("colorWithRed:green:blue:alpha:exposure:"); +late final _sel_colorWithRed_green_blue_alpha_linearExposure_ = objc.registerName("colorWithRed:green:blue:alpha:linearExposure:"); +late final _sel_colorWithWhite_alpha_ = objc.registerName("colorWithWhite:alpha:"); late final _sel_compassView = objc.registerName("compassView"); late final _sel_compassViewMargins = objc.registerName("compassViewMargins"); late final _sel_compassViewPosition = objc.registerName("compassViewPosition"); @@ -31447,9 +32142,12 @@ late final _sel_copy_ = objc.registerName("copy:"); late final _sel_countOfBytesCompleted = objc.registerName("countOfBytesCompleted"); late final _sel_createOfflinePackProgressListenerWithCallbacks_ = objc.registerName("createOfflinePackProgressListenerWithCallbacks:"); late final _sel_createTilePyramidOfflineRegionWithStyleURL_south_west_east_north_fromZoomLevel_toZoomLevel_ = objc.registerName("createTilePyramidOfflineRegionWithStyleURL:south:west:east:north:fromZoomLevel:toZoomLevel:"); +late final _sel_createUIEdgeInsetsNSExpressionWithPadding_ = objc.registerName("createUIEdgeInsetsNSExpressionWithPadding:"); late final _sel_currentEDRHeadroom = objc.registerName("currentEDRHeadroom"); late final _sel_currentMode = objc.registerName("currentMode"); late final _sel_cut_ = objc.registerName("cut:"); +late final _sel_cyanColor = objc.registerName("cyanColor"); +late final _sel_darkGrayColor = objc.registerName("darkGrayColor"); late final _sel_databasePath = objc.registerName("databasePath"); late final _sel_databaseURL = objc.registerName("databaseURL"); late final _sel_debugMask = objc.registerName("debugMask"); @@ -31488,6 +32186,8 @@ late final _sel_effectWithPreview_ = objc.registerName("effectWithPreview:"); late final _sel_effectiveUserInterfaceLayoutDirection = objc.registerName("effectiveUserInterfaceLayoutDirection"); late final _sel_enableRenderingStatsView_ = objc.registerName("enableRenderingStatsView:"); late final _sel_encodeWithCoder_ = objc.registerName("encodeWithCoder:"); +late final _sel_evaluateWithObject_ = objc.registerName("evaluateWithObject:"); +late final _sel_evaluateWithObject_substitutionVariables_ = objc.registerName("evaluateWithObject:substitutionVariables:"); late final _sel_export_ = objc.registerName("export:"); late final _sel_expressionBlock = objc.registerName("expressionBlock"); late final _sel_expressionForAggregate_ = objc.registerName("expressionForAggregate:"); @@ -31574,7 +32274,12 @@ late final _sel_geometryTypeVariableExpression = objc.registerName("geometryType late final _sel_getActionJournalLog = objc.registerName("getActionJournalLog"); late final _sel_getActionJournalLogFiles = objc.registerName("getActionJournalLogFiles"); late final _sel_getFlutterApiWithViewId_ = objc.registerName("getFlutterApiWithViewId:"); +late final _sel_getHue_saturation_brightness_alpha_ = objc.registerName("getHue:saturation:brightness:alpha:"); late final _sel_getMapWithViewId_ = objc.registerName("getMapWithViewId:"); +late final _sel_getRed_green_blue_alpha_ = objc.registerName("getRed:green:blue:alpha:"); +late final _sel_getWhite_alpha_ = objc.registerName("getWhite:alpha:"); +late final _sel_grayColor = objc.registerName("grayColor"); +late final _sel_greenColor = objc.registerName("greenColor"); late final _sel_hasBaseline = objc.registerName("hasBaseline"); late final _sel_heading = objc.registerName("heading"); late final _sel_heatmapColor = objc.registerName("heatmapColor"); @@ -31664,19 +32369,23 @@ late final _sel_images = objc.registerName("images"); late final _sel_includesIdeographicGlyphs = objc.registerName("includesIdeographicGlyphs"); late final _sel_increaseSize_ = objc.registerName("increaseSize:"); late final _sel_init = objc.registerName("init"); +late final _sel_initWithCGColor_ = objc.registerName("initWithCGColor:"); late final _sel_initWithCGImage_ = objc.registerName("initWithCGImage:"); late final _sel_initWithCGImage_scale_orientation_ = objc.registerName("initWithCGImage:scale:orientation:"); +late final _sel_initWithCIColor_ = objc.registerName("initWithCIColor:"); late final _sel_initWithCIImage_ = objc.registerName("initWithCIImage:"); late final _sel_initWithCIImage_scale_orientation_ = objc.registerName("initWithCIImage:scale:orientation:"); late final _sel_initWithCoder_ = objc.registerName("initWithCoder:"); late final _sel_initWithContentsOfFile_ = objc.registerName("initWithContentsOfFile:"); late final _sel_initWithData_ = objc.registerName("initWithData:"); late final _sel_initWithData_scale_ = objc.registerName("initWithData:scale:"); +late final _sel_initWithDisplayP3Red_green_blue_alpha_ = objc.registerName("initWithDisplayP3Red:green:blue:alpha:"); late final _sel_initWithExpressionType_ = objc.registerName("initWithExpressionType:"); late final _sel_initWithFrame_ = objc.registerName("initWithFrame:"); late final _sel_initWithFrame_options_ = objc.registerName("initWithFrame:options:"); late final _sel_initWithFrame_styleJSON_ = objc.registerName("initWithFrame:styleJSON:"); late final _sel_initWithFrame_styleURL_ = objc.registerName("initWithFrame:styleURL:"); +late final _sel_initWithHue_saturation_brightness_alpha_ = objc.registerName("initWithHue:saturation:brightness:alpha:"); late final _sel_initWithIdentifier_ = objc.registerName("initWithIdentifier:"); late final _sel_initWithIdentifier_URL_options_ = objc.registerName("initWithIdentifier:URL:options:"); late final _sel_initWithIdentifier_configurationURLString_ = objc.registerName("initWithIdentifier:configurationURLString:"); @@ -31689,7 +32398,12 @@ late final _sel_initWithIdentifier_shape_options_ = objc.registerName("initWithI late final _sel_initWithIdentifier_shapes_options_ = objc.registerName("initWithIdentifier:shapes:options:"); late final _sel_initWithIdentifier_source_ = objc.registerName("initWithIdentifier:source:"); late final _sel_initWithIdentifier_tileURLTemplates_options_ = objc.registerName("initWithIdentifier:tileURLTemplates:options:"); +late final _sel_initWithPatternImage_ = objc.registerName("initWithPatternImage:"); +late final _sel_initWithRed_green_blue_alpha_ = objc.registerName("initWithRed:green:blue:alpha:"); +late final _sel_initWithRed_green_blue_alpha_exposure_ = objc.registerName("initWithRed:green:blue:alpha:exposure:"); +late final _sel_initWithRed_green_blue_alpha_linearExposure_ = objc.registerName("initWithRed:green:blue:alpha:linearExposure:"); late final _sel_initWithStyleURL_bounds_fromZoomLevel_toZoomLevel_ = objc.registerName("initWithStyleURL:bounds:fromZoomLevel:toZoomLevel:"); +late final _sel_initWithWhite_alpha_ = objc.registerName("initWithWhite:alpha:"); late final _sel_insertLayer_aboveLayer_ = objc.registerName("insertLayer:aboveLayer:"); late final _sel_insertLayer_atIndex_ = objc.registerName("insertLayer:atIndex:"); late final _sel_insertLayer_belowLayer_ = objc.registerName("insertLayer:belowLayer:"); @@ -31729,6 +32443,7 @@ late final _sel_layoutSublayersOfLayer_ = objc.registerName("layoutSublayersOfLa late final _sel_leavesOfCluster_offset_limit_ = objc.registerName("leavesOfCluster:offset:limit:"); late final _sel_leftExpression = objc.registerName("leftExpression"); late final _sel_light = objc.registerName("light"); +late final _sel_lightGrayColor = objc.registerName("lightGrayColor"); late final _sel_lineBlur = objc.registerName("lineBlur"); late final _sel_lineBlurTransition = objc.registerName("lineBlurTransition"); late final _sel_lineCap = objc.registerName("lineCap"); @@ -31758,11 +32473,13 @@ late final _sel_lineTranslationAnchor = objc.registerName("lineTranslationAnchor late final _sel_lineTranslationTransition = objc.registerName("lineTranslationTransition"); late final _sel_lineWidth = objc.registerName("lineWidth"); late final _sel_lineWidthTransition = objc.registerName("lineWidthTransition"); +late final _sel_linearExposure = objc.registerName("linearExposure"); late final _sel_localizeLabelsIntoLocale_ = objc.registerName("localizeLabelsIntoLocale:"); late final _sel_locationManager = objc.registerName("locationManager"); late final _sel_logoView = objc.registerName("logoView"); late final _sel_logoViewMargins = objc.registerName("logoViewMargins"); late final _sel_logoViewPosition = objc.registerName("logoViewPosition"); +late final _sel_magentaColor = objc.registerName("magentaColor"); late final _sel_mainScreen = objc.registerName("mainScreen"); late final _sel_makeTextWritingDirectionLeftToRight_ = objc.registerName("makeTextWritingDirectionLeftToRight:"); late final _sel_makeTextWritingDirectionRightToLeft_ = objc.registerName("makeTextWritingDirectionRightToLeft:"); @@ -31870,6 +32587,7 @@ late final _sel_onSecondaryTapWithScreenLocation_ = objc.registerName("onSeconda late final _sel_onTapWithScreenLocation_ = objc.registerName("onTapWithScreenLocation:"); late final _sel_operand = objc.registerName("operand"); late final _sel_options = objc.registerName("options"); +late final _sel_orangeColor = objc.registerName("orangeColor"); late final _sel_overlays = objc.registerName("overlays"); late final _sel_overscanCompensation = objc.registerName("overscanCompensation"); late final _sel_overscanCompensationInsets = objc.registerName("overscanCompensationInsets"); @@ -31877,7 +32595,6 @@ late final _sel_packs = objc.registerName("packs"); late final _sel_panScrollingMode = objc.registerName("panScrollingMode"); late final _sel_parentFocusEnvironment = objc.registerName("parentFocusEnvironment"); late final _sel_parseExpressionWithPropertyName_expression_ = objc.registerName("parseExpressionWithPropertyName:expression:"); -late final _sel_parsePredicateWithRaw_ = objc.registerName("parsePredicateWithRaw:"); late final _sel_pasteAndGo_ = objc.registerName("pasteAndGo:"); late final _sel_pasteAndMatchStyle_ = objc.registerName("pasteAndMatchStyle:"); late final _sel_pasteAndSearch_ = objc.registerName("pasteAndSearch:"); @@ -31890,6 +32607,14 @@ late final _sel_potentialEDRHeadroom = objc.registerName("potentialEDRHeadroom") late final _sel_predefinedStyle_ = objc.registerName("predefinedStyle:"); late final _sel_predefinedStyles = objc.registerName("predefinedStyles"); late final _sel_predicate = objc.registerName("predicate"); +late final _sel_predicateFormat = objc.registerName("predicateFormat"); +late final _sel_predicateFromMetadataQueryString_ = objc.registerName("predicateFromMetadataQueryString:"); +late final _sel_predicateWithBlock_ = objc.registerName("predicateWithBlock:"); +late final _sel_predicateWithFormat_ = objc.registerName("predicateWithFormat:"); +late final _sel_predicateWithFormat_argumentArray_ = objc.registerName("predicateWithFormat:argumentArray:"); +late final _sel_predicateWithMLNJSONObject_ = objc.registerName("predicateWithMLNJSONObject:"); +late final _sel_predicateWithSubstitutionVariables_ = objc.registerName("predicateWithSubstitutionVariables:"); +late final _sel_predicateWithValue_ = objc.registerName("predicateWithValue:"); late final _sel_preferredElementSize = objc.registerName("preferredElementSize"); late final _sel_preferredFocusEnvironments = objc.registerName("preferredFocusEnvironments"); late final _sel_preferredFocusedView = objc.registerName("preferredFocusedView"); @@ -31909,6 +32634,7 @@ late final _sel_preview = objc.registerName("preview"); late final _sel_print_ = objc.registerName("print:"); late final _sel_progress = objc.registerName("progress"); late final _sel_providerForDeferredMenuElement_ = objc.registerName("providerForDeferredMenuElement:"); +late final _sel_purpleColor = objc.registerName("purpleColor"); late final _sel_putResourceWithUrl_data_modified_expires_etag_mustRevalidate_ = objc.registerName("putResourceWithUrl:data:modified:expires:etag:mustRevalidate:"); late final _sel_rasterBrightnessMax = objc.registerName("rasterBrightnessMax"); late final _sel_rasterBrightnessMin = objc.registerName("rasterBrightnessMin"); @@ -31924,6 +32650,7 @@ late final _sel_rasterResampling = objc.registerName("rasterResampling"); late final _sel_rasterResamplingMode = objc.registerName("rasterResamplingMode"); late final _sel_rasterSaturation = objc.registerName("rasterSaturation"); late final _sel_rasterSaturationTransition = objc.registerName("rasterSaturationTransition"); +late final _sel_redColor = objc.registerName("redColor"); late final _sel_referenceDisplayModeStatus = objc.registerName("referenceDisplayModeStatus"); late final _sel_region = objc.registerName("region"); late final _sel_regionDidChangeWithReasonWithMapView_reason_animated_ = objc.registerName("regionDidChangeWithReasonWithMapView:reason:animated:"); @@ -31974,6 +32701,7 @@ late final _sel_selectedElements = objc.registerName("selectedElements"); late final _sel_selectedImage = objc.registerName("selectedImage"); late final _sel_semanticContentAttribute = objc.registerName("semanticContentAttribute"); late final _sel_sender = objc.registerName("sender"); +late final _sel_set = objc.registerName("set"); late final _sel_setAccessories_ = objc.registerName("setAccessories:"); late final _sel_setAltitude_ = objc.registerName("setAltitude:"); late final _sel_setAnchorRotateOrZoomGesturesToCenterCoordinate_ = objc.registerName("setAnchorRotateOrZoomGesturesToCenterCoordinate:"); @@ -32041,6 +32769,7 @@ late final _sel_setDisplayPreferences_ = objc.registerName("setDisplayPreference late final _sel_setDynamicNavigationCameraAnimationDuration_ = objc.registerName("setDynamicNavigationCameraAnimationDuration:"); late final _sel_setEffect_ = objc.registerName("setEffect:"); late final _sel_setEnabled_ = objc.registerName("setEnabled:"); +late final _sel_setFill = objc.registerName("setFill"); late final _sel_setFillAntialias_ = objc.registerName("setFillAntialias:"); late final _sel_setFillAntialiased_ = objc.registerName("setFillAntialiased:"); late final _sel_setFillColorTransition_ = objc.registerName("setFillColorTransition:"); @@ -32229,6 +32958,7 @@ late final _sel_setShowsUserLocation_ = objc.registerName("setShowsUserLocation: late final _sel_setSourceLayerIdentifier_ = objc.registerName("setSourceLayerIdentifier:"); late final _sel_setSources_ = objc.registerName("setSources:"); late final _sel_setState_ = objc.registerName("setState:"); +late final _sel_setStroke = objc.registerName("setStroke"); late final _sel_setStyleJSON_ = objc.registerName("setStyleJSON:"); late final _sel_setStyleURL_ = objc.registerName("setStyleURL:"); late final _sel_setSubtitle_ = objc.registerName("setSubtitle:"); @@ -32342,6 +33072,7 @@ late final _sel_sourceIdentifier = objc.registerName("sourceIdentifier"); late final _sel_sourceLayerIdentifier = objc.registerName("sourceLayerIdentifier"); late final _sel_sourceWithIdentifier_ = objc.registerName("sourceWithIdentifier:"); late final _sel_sources = objc.registerName("sources"); +late final _sel_standardDynamicRangeColor = objc.registerName("standardDynamicRangeColor"); late final _sel_state = objc.registerName("state"); late final _sel_style = objc.registerName("style"); late final _sel_styleJSON = objc.registerName("styleJSON"); @@ -32461,6 +33192,8 @@ late final _sel_visibleFeaturesInRect_ = objc.registerName("visibleFeaturesInRec late final _sel_visibleFeaturesInRect_inStyleLayersWithIdentifiers_ = objc.registerName("visibleFeaturesInRect:inStyleLayersWithIdentifiers:"); late final _sel_visibleFeaturesInRect_inStyleLayersWithIdentifiers_predicate_ = objc.registerName("visibleFeaturesInRect:inStyleLayersWithIdentifiers:predicate:"); late final _sel_wantsSoftwareDimming = objc.registerName("wantsSoftwareDimming"); +late final _sel_whiteColor = objc.registerName("whiteColor"); +late final _sel_yellowColor = objc.registerName("yellowColor"); late final _sel_zoomLevel = objc.registerName("zoomLevel"); late final _sel_zoomLevelForExpandingCluster_ = objc.registerName("zoomLevelForExpandingCluster:"); late final _sel_zoomLevelToAltitudeWithZoomLevel_pitch_latitude_size_ = objc.registerName("zoomLevelToAltitudeWithZoomLevel:pitch:latitude:size:"); diff --git a/packages/maplibre_ios/lib/src/maplibre_plugin.dart b/packages/maplibre_ios/lib/src/maplibre_plugin.dart index 143bdae4f..9056ad67f 100644 --- a/packages/maplibre_ios/lib/src/maplibre_plugin.dart +++ b/packages/maplibre_ios/lib/src/maplibre_plugin.dart @@ -1,6 +1,8 @@ +import 'package:flutter/painting.dart'; import 'package:maplibre_ios/src/map_state.dart'; import 'package:maplibre_ios/src/offline_manager.dart'; import 'package:maplibre_ios/src/permission_manager.dart'; +import 'package:maplibre_ios/src/style/layers/style_layer.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; /// iOS implementation of the federated MapLibre plugin. @@ -26,4 +28,437 @@ final class MapLibrePlugin extends MapLibrePlatform { @override bool get userLocationIsSupported => true; + + /// Create a platform specific [BackgroundStyleLayer] object. + @override + BackgroundStyleLayer createBackgroundStyleLayer({ + required String id, + required bool visible, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => BackgroundStyleLayerIos( + id: id, + visible: visible, + color: color, + pattern: pattern, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [CircleStyleLayer] object. + @override + CircleStyleLayer createCircleStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => CircleStyleLayerIos( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + translate: translate, + translateAnchor: translateAnchor, + sortKey: sortKey, + radius: radius, + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + /// Create a platform specific [ColorReliefStyleLayer] object. + @override + ColorReliefStyleLayer createColorReliefStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => throw UnsupportedError( + 'ColorReliefStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [FillExtrusionStyleLayer] object. + @override + FillExtrusionStyleLayer createFillExtrusionStyleLayer({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => FillExtrusionStyleLayerIos( + id: id, + sourceId: sourceId, + minZoom: minZoom, + maxZoom: maxZoom, + visible: visible, + filter: filter, + sourceLayerId: sourceLayerId, + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + /// Create a platform specific [FillStyleLayer] object. + @override + FillStyleLayer createFillStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) => FillStyleLayerIos( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + ); + + /// Create a platform specific [HeatmapStyleLayer] object. + @override + HeatmapStyleLayer createHeatmapStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => HeatmapStyleLayerIos( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + /// Create a platform specific [HillshadeStyleLayer] object. + @override + HillshadeStyleLayer createHillshadeStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) => HillshadeStyleLayerIos( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + illuminationDirection: illuminationDirection, + // illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + // method: method, + ); + + /// Create a platform specific [LineStyleLayer] object. + @override + LineStyleLayer createLineStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => LineStyleLayerIos( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + opacity: opacity, + color: color, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + cap: cap, + ); + + /// Create a platform specific [RasterStyleLayer] object. + @override + RasterStyleLayer createRasterStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => RasterStyleLayerIos( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + /// Create a platform specific [SymbolStyleLayer] object. + @override + SymbolStyleLayer createSymbolStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? + textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) => SymbolStyleLayerIos( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); } diff --git a/packages/maplibre_ios/lib/src/style/layers/background_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/background_style_layer.dart new file mode 100644 index 000000000..421708b7c --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/background_style_layer.dart @@ -0,0 +1,81 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [BackgroundStyleLayer]. +class BackgroundStyleLayerIos extends StyleLayerIos + implements BackgroundStyleLayer { + /// Default constructor for a [BackgroundStyleLayerIos] instance. + factory BackgroundStyleLayerIos({ + required String id, + required double minZoom, + required double maxZoom, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required bool visible, + }) { + return BackgroundStyleLayerIos.fromNativeLayer( + MLNBackgroundStyleLayer.new$()..initWithIdentifier(id.toNSString()), + ) + ..minZoom = minZoom + ..maxZoom = maxZoom + ..color = color + ..opacity = opacity + ..visible = visible + ..pattern = pattern; + } + + /// Construct a [BackgroundStyleLayerIos] from a ObjC layer. + BackgroundStyleLayerIos.fromNativeLayer(super.ffiLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get color => + ffiLayer.backgroundColor.toColorPropertyValue() ?? + BackgroundStyleLayer.defaultColor; + + @override + set color(PropertyValue property) { + if (property.isExpression) { + ffiLayer.backgroundColor = property.expression.toNSExpression(); + } else { + ffiLayer.backgroundColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get opacity => + ffiLayer.backgroundOpacity.toPropertyValue() ?? + BackgroundStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.backgroundOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.backgroundOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue? get pattern => + ffiLayer.backgroundPattern.toPropertyValue(); + + @override + set pattern(PropertyValue? property) { + if (property == null) { + throw UnsupportedError( + 'Removing the pattern property is not supported on iOS', + ); + } else if (property.isExpression) { + ffiLayer.backgroundPattern = property.expression.toNSExpression(); + } else { + ffiLayer.backgroundPattern = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } +} diff --git a/packages/maplibre_ios/lib/src/style/layers/circle_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/circle_style_layer.dart new file mode 100644 index 000000000..507c70dee --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/circle_style_layer.dart @@ -0,0 +1,269 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [CircleStyleLayer]. +class CircleStyleLayerIos extends StyleLayerIos + implements CircleStyleLayer { + /// Default constructor for a [CircleStyleLayerIos] instance. + factory CircleStyleLayerIos({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required PropertyValue blur, + required PropertyValue color, + required Expression? filter, + required PropertyValue opacity, + required PropertyValue pitchAlignment, + required PropertyValue pitchScale, + required PropertyValue radius, + required PropertyValue? sortKey, + required String? sourceLayerId, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + required PropertyValue strokeWidth, + required PropertyValue translate, + required PropertyValue translateAnchor, + required bool visible, + }) { + final layer = CircleStyleLayerIos.fromNativeLayer( + MLNCircleStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.blur = blur; + layer.color = color; + if (filter != null) layer.filter = filter; + layer.opacity = opacity; + layer.pitchAlignment = pitchAlignment; + layer.pitchScale = pitchScale; + layer.radius = radius; + layer.sortKey = sortKey; + layer.sourceLayerId = sourceLayerId; + layer.strokeColor = strokeColor; + layer.strokeOpacity = strokeOpacity; + layer.strokeWidth = strokeWidth; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.visible = visible; + if (sourceLayerId != null) { + layer.ffiLayer.sourceLayerIdentifier = sourceLayerId.toNSString(); + } + return layer; + } + + /// Construct a [CircleStyleLayerIos] from a ObjC layer. + CircleStyleLayerIos.fromNativeLayer(super.ffiLayer) : super.fromNativeLayer(); + + @override + PropertyValue get blur => + ffiLayer.circleBlur.toPropertyValue() ?? CircleStyleLayer.defaultBlur; + + @override + set blur(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleBlur = property.expression.toNSExpression(); + } else { + ffiLayer.circleBlur = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get color => + ffiLayer.circleColor.toColorPropertyValue() ?? + CircleStyleLayer.defaultColor; + + @override + set color(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleColor = property.expression.toNSExpression(); + } else { + ffiLayer.circleColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + PropertyValue get opacity => + ffiLayer.circleOpacity.toPropertyValue() ?? + CircleStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.circleOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get pitchAlignment => + ffiLayer.circlePitchAlignment.toEnumPropertyValue(ReferenceSpace.values)!; + + @override + set pitchAlignment(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circlePitchAlignment = property.expression.toNSExpression(); + } else { + ffiLayer.circlePitchAlignment = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get pitchScale => + ffiLayer.circlePitchScale.toEnumPropertyValue(ReferenceSpace.values)!; + + @override + set pitchScale(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circlePitchScale = property.expression.toNSExpression(); + } else { + ffiLayer.circlePitchScale = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get radius => + ffiLayer.circleRadius.toPropertyValue() ?? CircleStyleLayer.defaultRadius; + + @override + set radius(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleRadius = property.expression.toNSExpression(); + } else { + ffiLayer.circleRadius = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue? get sortKey => + ffiLayer.circleSortKey.toPropertyValue(); + + @override + set sortKey(PropertyValue? property) { + if (property == null) { + ffiLayer.circleSortKey = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.circleSortKey = property.expression.toNSExpression(); + } else { + ffiLayer.circleSortKey = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? property) { + ffiLayer.sourceLayerIdentifier = property?.toNSString(); + } + + @override + PropertyValue get strokeColor => + ffiLayer.circleStrokeColor.toColorPropertyValue() ?? + CircleStyleLayer.defaultStrokeColor; + + @override + set strokeColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleStrokeColor = property.expression.toNSExpression(); + } else { + ffiLayer.circleStrokeColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get strokeOpacity => + ffiLayer.circleStrokeOpacity.toPropertyValue() ?? + CircleStyleLayer.defaultStrokeOpacity; + + @override + set strokeOpacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleStrokeOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.circleStrokeOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get strokeWidth => + ffiLayer.circleStrokeWidth.toPropertyValue() ?? + CircleStyleLayer.defaultStrokeWidth; + + @override + set strokeWidth(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleStrokeWidth = property.expression.toNSExpression(); + } else { + ffiLayer.circleStrokeWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get translate => + ffiLayer.circleTranslate.toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleTranslate = property.expression.toNSExpression(); + } else { + ffiLayer.circleTranslate = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get translateAnchor => + ffiLayer.circleTranslateAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.circleTranslateAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.circleTranslateAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/fill_extrusion_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/fill_extrusion_style_layer.dart new file mode 100644 index 000000000..b7c5c6517 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/fill_extrusion_style_layer.dart @@ -0,0 +1,206 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [FillExtrusionStyleLayer]. +class FillExtrusionStyleLayerIos + extends StyleLayerIos + implements FillExtrusionStyleLayer { + /// Default constructor for a [FillExtrusionStyleLayerIos] instance. + factory FillExtrusionStyleLayerIos({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) { + final layer = FillExtrusionStyleLayerIos.fromNativeLayer( + MLNFillExtrusionStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + layer.opacity = opacity; + layer.color = color; + if (pattern != null) layer.pattern = pattern; + layer.height = height; + layer.base = base; + layer.verticalGradient = verticalGradient; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + return layer; + } + + /// Construct a [FillExtrusionStyleLayerIos] from a ObjC layer. + FillExtrusionStyleLayerIos.fromNativeLayer(super.ffiLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get opacity => + ffiLayer.fillExtrusionOpacity.toPropertyValue() ?? + FillExtrusionStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get color => + ffiLayer.fillExtrusionColor.toColorPropertyValue() ?? + FillExtrusionStyleLayer.defaultColor; + + @override + set color(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionColor = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue? get pattern => + ffiLayer.fillExtrusionPattern.toPropertyValue(); + + @override + set pattern(PropertyValue? property) { + if (property == null) { + throw UnsupportedError( + 'Removing the pattern property is not supported on iOS', + ); + } else if (property.isExpression) { + ffiLayer.fillExtrusionPattern = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionPattern = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } + + @override + PropertyValue get height => + ffiLayer.fillExtrusionHeight.toPropertyValue() ?? + FillExtrusionStyleLayer.defaultHeight; + + @override + set height(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionHeight = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionHeight = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get base => + ffiLayer.fillExtrusionBase.toPropertyValue() ?? + FillExtrusionStyleLayer.defaultBase; + + @override + set base(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionBase = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionBase = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get verticalGradient => + ffiLayer.fillExtrusionHasVerticalGradient.toPropertyValue() ?? + FillExtrusionStyleLayer.defaultVerticalGradient; + + @override + set verticalGradient(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionHasVerticalGradient = property.expression + .toNSExpression(); + } else { + ffiLayer.fillExtrusionHasVerticalGradient = + NSExpression.expressionForConstantValue(property.value.toNSNumber()); + } + } + + @override + PropertyValue get translate => + ffiLayer.fillExtrusionTranslation.toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionTranslation = property.expression.toNSExpression(); + } else { + ffiLayer.fillExtrusionTranslation = + NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get translateAnchor => + ffiLayer.fillExtrusionTranslationAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillExtrusionTranslationAnchor = property.expression + .toNSExpression(); + } else { + ffiLayer.fillExtrusionTranslationAnchor = + NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? value) { + ffiLayer.sourceLayerIdentifier = value?.toNSString(); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/fill_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/fill_style_layer.dart new file mode 100644 index 000000000..d7893985f --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/fill_style_layer.dart @@ -0,0 +1,198 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [FillStyleLayer]. +class FillStyleLayerIos extends StyleLayerIos + implements FillStyleLayer { + /// Default constructor for a [FillStyleLayerIos] instance. + factory FillStyleLayerIos({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) { + final layer = FillStyleLayerIos.fromNativeLayer( + MLNFillStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + if (sortKey != null) layer.sortKey = sortKey; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.antialias = antialias; + layer.color = color; + layer.opacity = opacity; + layer.outlineColor = outlineColor; + if (pattern != null) layer.pattern = pattern; + return layer; + } + + /// Construct a [FillStyleLayerIos] from a ObjC layer. + FillStyleLayerIos.fromNativeLayer(super.ffiLayer) : super.fromNativeLayer(); + + @override + PropertyValue get antialias => + ffiLayer.fillAntialias.toPropertyValue() ?? + FillStyleLayer.defaultAntialias; + + @override + set antialias(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillAntialias = property.expression.toNSExpression(); + } else { + ffiLayer.fillAntialias = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get color => + ffiLayer.fillColor.toColorPropertyValue() ?? FillStyleLayer.defaultColor; + + @override + set color(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillColor = property.expression.toNSExpression(); + } else { + ffiLayer.fillColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get opacity => + ffiLayer.fillOpacity.toPropertyValue() ?? FillStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.fillOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get outlineColor => + ffiLayer.fillOutlineColor.toColorPropertyValue() ?? + FillStyleLayer.defaultOutlineColor; + + @override + set outlineColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillOutlineColor = property.expression.toNSExpression(); + } else { + ffiLayer.fillOutlineColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue? get pattern => ffiLayer.fillPattern.toPropertyValue(); + + @override + set pattern(PropertyValue? property) { + if (property == null) { + throw UnsupportedError( + 'Removing the pattern property is not supported on iOS', + ); + } else if (property.isExpression) { + ffiLayer.fillPattern = property.expression.toNSExpression(); + } else { + ffiLayer.fillPattern = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } + + @override + PropertyValue get translate => + ffiLayer.fillTranslation.toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillTranslation = property.expression.toNSExpression(); + } else { + ffiLayer.fillTranslation = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get translateAnchor => + ffiLayer.fillTranslationAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.fillTranslationAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.fillTranslationAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue? get sortKey => ffiLayer.fillSortKey.toPropertyValue(); + + @override + set sortKey(PropertyValue? property) { + if (property == null) { + ffiLayer.fillSortKey = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.fillSortKey = property.expression.toNSExpression(); + } else { + ffiLayer.fillSortKey = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? value) { + ffiLayer.sourceLayerIdentifier = value?.toNSString(); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/heatmap_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/heatmap_style_layer.dart new file mode 100644 index 000000000..b40343a44 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/heatmap_style_layer.dart @@ -0,0 +1,144 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [HeatmapStyleLayer]. +class HeatmapStyleLayerIos extends StyleLayerIos + implements HeatmapStyleLayer { + /// Default constructor for a [HeatmapStyleLayerIos] instance. + factory HeatmapStyleLayerIos({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) { + final layer = HeatmapStyleLayerIos.fromNativeLayer( + MLNHeatmapStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + layer.radius = radius; + layer.weight = weight; + layer.intensity = intensity; + layer.color = color; + layer.opacity = opacity; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + return layer; + } + + /// Construct a [HeatmapStyleLayerIos] from a ObjC layer. + HeatmapStyleLayerIos.fromNativeLayer(super.ffiLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get radius => + ffiLayer.heatmapRadius.toPropertyValue() ?? + HeatmapStyleLayer.defaultRadius; + + @override + set radius(PropertyValue property) { + if (property.isExpression) { + ffiLayer.heatmapRadius = property.expression.toNSExpression(); + } else { + ffiLayer.heatmapRadius = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get weight => + ffiLayer.heatmapWeight.toPropertyValue() ?? + HeatmapStyleLayer.defaultWeight; + + @override + set weight(PropertyValue property) { + if (property.isExpression) { + ffiLayer.heatmapWeight = property.expression.toNSExpression(); + } else { + ffiLayer.heatmapWeight = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get intensity => + ffiLayer.heatmapIntensity.toPropertyValue() ?? + HeatmapStyleLayer.defaultIntensity; + + @override + set intensity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.heatmapIntensity = property.expression.toNSExpression(); + } else { + ffiLayer.heatmapIntensity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue? get color => + ffiLayer.heatmapColor.toColorPropertyValue(); + + @override + set color(PropertyValue? property) { + if (property == null) { + ffiLayer.heatmapColor = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.heatmapColor = property.expression.toNSExpression(); + } else { + ffiLayer.heatmapColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get opacity => + ffiLayer.heatmapOpacity.toPropertyValue() ?? + HeatmapStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.heatmapOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.heatmapOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? value) { + ffiLayer.sourceLayerIdentifier = value?.toNSString(); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/hillshade_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/hillshade_style_layer.dart new file mode 100644 index 000000000..e21e04a6b --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/hillshade_style_layer.dart @@ -0,0 +1,177 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [HillshadeStyleLayer]. +class HillshadeStyleLayerIos extends StyleLayerIos + implements HillshadeStyleLayer { + /// Default constructor for a [HillshadeStyleLayerIos] instance. + factory HillshadeStyleLayerIos({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + // required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + // required PropertyValue method, + }) { + final layer = HillshadeStyleLayerIos.fromNativeLayer( + MLNHillshadeStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + layer.illuminationDirection = illuminationDirection; + layer.illuminationAnchor = illuminationAnchor; + layer.exaggeration = exaggeration; + layer.shadowColor = shadowColor; + layer.highlightColor = highlightColor; + layer.accentColor = accentColor; + return layer; + } + + /// Construct a [HillshadeStyleLayerIos] from a ObjC layer. + HillshadeStyleLayerIos.fromNativeLayer(super.ffiLayer) + : super.fromNativeLayer(); + + @override + PropertyValue get illuminationDirection => + throw UnimplementedError(); + + @override + set illuminationDirection(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeIlluminationDirection = property.expression + .toNSExpression(); + } else { + final value = property.value; + final nsValue = value.isArray + ? value.array.map((e) => e.toNSNumber()).toNSObject() + : value.number.toNSNumber(); + ffiLayer.hillshadeIlluminationDirection = + NSExpression.expressionForConstantValue(nsValue); + } + } + + @override + PropertyValue get illuminationAltitude => throw UnsupportedError( + 'Getting illuminationAltitude is not supported on iOS ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + + @override + set illuminationAltitude(PropertyValue property) { + throw UnsupportedError( + 'Setting illuminationAltitude is not supported on iOS ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + } + + @override + PropertyValue get illuminationAnchor => + ffiLayer.hillshadeIlluminationAnchor.toEnumPropertyValue( + IlluminationAnchor.values, + ) ?? + HillshadeStyleLayer.defaultIlluminationAnchor; + + @override + set illuminationAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeIlluminationAnchor = property.expression + .toNSExpression(); + } else { + ffiLayer.hillshadeIlluminationAnchor = + NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get exaggeration => + ffiLayer.hillshadeExaggeration.toPropertyValue() ?? + HillshadeStyleLayer.defaultExaggeration; + + @override + set exaggeration(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeExaggeration = property.expression.toNSExpression(); + } else { + ffiLayer.hillshadeExaggeration = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get shadowColor => + ffiLayer.hillshadeShadowColor.toColorPropertyValue() ?? + HillshadeStyleLayer.defaultShadowColor; + + @override + set shadowColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeShadowColor = property.expression.toNSExpression(); + } else { + ffiLayer.hillshadeShadowColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get highlightColor => + ffiLayer.hillshadeHighlightColor.toColorPropertyValue() ?? + HillshadeStyleLayer.defaultHighlightColor; + + @override + set highlightColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeHighlightColor = property.expression.toNSExpression(); + } else { + ffiLayer.hillshadeHighlightColor = + NSExpression.expressionForConstantValue(property.value.toUIColor()); + } + } + + @override + PropertyValue get accentColor => + ffiLayer.hillshadeAccentColor.toColorPropertyValue() ?? + HillshadeStyleLayer.defaultAccentColor; + + @override + set accentColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.hillshadeAccentColor = property.expression.toNSExpression(); + } else { + ffiLayer.hillshadeAccentColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get method => throw UnsupportedError( + 'Getting method is not supported on iOS ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + + @override + set method(PropertyValue property) { + throw UnsupportedError( + 'Setting method is not supported on iOS ' + 'https://github.com/maplibre/maplibre-native/issues/3396', + ); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/line_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/line_style_layer.dart new file mode 100644 index 000000000..e65bb5f33 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/line_style_layer.dart @@ -0,0 +1,353 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [LineStyleLayer]. +class LineStyleLayerIos extends StyleLayerIos + implements LineStyleLayer { + /// Default constructor for a [LineStyleLayerIos] instance. + factory LineStyleLayerIos({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) { + final layer = LineStyleLayerIos.fromNativeLayer( + MLNLineStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + layer.sortKey = sortKey; + layer.translate = translate; + layer.translateAnchor = translateAnchor; + layer.cap = cap; + layer.join = join; + layer.miterLimit = miterLimit; + layer.roundLimit = roundLimit; + layer.opacity = opacity; + layer.color = color; + layer.width = width; + layer.gapWidth = gapWidth; + layer.offset = offset; + layer.blur = blur; + layer.dashArray = dashArray; + if (pattern != null) layer.pattern = pattern; + layer.gradient = gradient; + return layer; + } + + /// Construct a [LineStyleLayerIos] from a ObjC layer. + LineStyleLayerIos.fromNativeLayer(super.ffiLayer) : super.fromNativeLayer(); + + @override + PropertyValue get cap => + ffiLayer.lineCap.toEnumPropertyValue(LineCap.values) ?? + LineStyleLayer.defaultCap; + + @override + set cap(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineCap = property.expression.toNSExpression(); + } else { + ffiLayer.lineCap = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get join => + ffiLayer.lineJoin.toEnumPropertyValue(LineJoin.values) ?? + LineStyleLayer.defaultJoin; + + @override + set join(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineJoin = property.expression.toNSExpression(); + } else { + ffiLayer.lineJoin = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get miterLimit => + ffiLayer.lineMiterLimit.toPropertyValue() ?? + LineStyleLayer.defaultMiterLimit; + + @override + set miterLimit(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineMiterLimit = property.expression.toNSExpression(); + } else { + ffiLayer.lineMiterLimit = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get roundLimit => + ffiLayer.lineRoundLimit.toPropertyValue() ?? + LineStyleLayer.defaultRoundLimit; + + @override + set roundLimit(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineRoundLimit = property.expression.toNSExpression(); + } else { + ffiLayer.lineRoundLimit = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get opacity => + ffiLayer.lineOpacity.toPropertyValue() ?? LineStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.lineOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get color => + ffiLayer.lineColor.toColorPropertyValue() ?? LineStyleLayer.defaultColor; + + @override + set color(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineColor = property.expression.toNSExpression(); + } else { + ffiLayer.lineColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get width => + ffiLayer.lineWidth.toPropertyValue() ?? LineStyleLayer.defaultWidth; + + @override + set width(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineWidth = property.expression.toNSExpression(); + } else { + ffiLayer.lineWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get gapWidth => + ffiLayer.lineGapWidth.toPropertyValue() ?? LineStyleLayer.defaultGapWidth; + + @override + set gapWidth(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineGapWidth = property.expression.toNSExpression(); + } else { + ffiLayer.lineGapWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get offset => + ffiLayer.lineOffset.toPropertyValue() ?? LineStyleLayer.defaultOffset; + + @override + set offset(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineOffset = property.expression.toNSExpression(); + } else { + ffiLayer.lineOffset = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get blur => + ffiLayer.lineBlur.toPropertyValue() ?? LineStyleLayer.defaultBlur; + + @override + set blur(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineBlur = property.expression.toNSExpression(); + } else { + ffiLayer.lineBlur = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue>? get dashArray { + final expression = ffiLayer.lineDashPattern; + if (expression.expressionType == + NSExpressionType.NSConstantValueExpressionType) { + final ffiValue = expression.constantValue; + if (ffiValue == null) return null; + final raw = toDartObject(ffiValue, convertOther: toDartObject); + final list = (raw as List) + .cast() + .map((e) => e.toDouble()) + .toList(growable: false); + return PropertyValue.value(list); + } + return PropertyValue.expression(expression.toExpression()); + } + + @override + set dashArray(PropertyValue>? property) { + if (property == null) { + ffiLayer.lineDashPattern = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.lineDashPattern = property.expression.toNSExpression(); + } else { + ffiLayer.lineDashPattern = NSExpression.expressionForConstantValue( + property.value.map((e) => e).toList().toNSArray(), + ); + } + } + + @override + PropertyValue? get pattern => ffiLayer.linePattern.toPropertyValue(); + + @override + set pattern(PropertyValue? property) { + if (property == null) { + throw UnsupportedError( + 'Removing the pattern property is not supported on iOS', + ); + } else if (property.isExpression) { + ffiLayer.linePattern = property.expression.toNSExpression(); + } else { + ffiLayer.linePattern = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } + + @override + PropertyValue? get gradient => + ffiLayer.lineGradient.toColorPropertyValue(); + + @override + set gradient(PropertyValue? property) { + if (property == null) { + ffiLayer.lineGradient = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.lineGradient = property.expression.toNSExpression(); + } else { + ffiLayer.lineGradient = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get translate => + ffiLayer.lineTranslation.toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineTranslation = property.expression.toNSExpression(); + } else { + ffiLayer.lineTranslation = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get translateAnchor => + ffiLayer.lineTranslationAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.lineTranslationAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.lineTranslationAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue? get sortKey => ffiLayer.lineSortKey.toPropertyValue(); + + @override + set sortKey(PropertyValue? property) { + if (property == null) { + ffiLayer.lineSortKey = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.lineSortKey = property.expression.toNSExpression(); + } else { + ffiLayer.lineSortKey = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? value) { + ffiLayer.sourceLayerIdentifier = value?.toNSString(); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/raster_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/raster_style_layer.dart new file mode 100644 index 000000000..f5075ed87 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/raster_style_layer.dart @@ -0,0 +1,176 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [RasterStyleLayer]. +class RasterStyleLayerIos extends StyleLayerIos + implements RasterStyleLayer { + /// Default constructor for a [RasterStyleLayerIos] instance. + factory RasterStyleLayerIos({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) { + final layer = RasterStyleLayerIos.fromNativeLayer( + MLNRasterStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + layer.opacity = opacity; + layer.hueRotate = hueRotate; + layer.brightnessMin = brightnessMin; + layer.brightnessMax = brightnessMax; + layer.saturation = saturation; + layer.contrast = contrast; + layer.resampling = resampling; + layer.fadeDuration = fadeDuration; + return layer; + } + + /// Construct a [RasterStyleLayerIos] from a ObjC layer. + RasterStyleLayerIos.fromNativeLayer(super.ffiLayer) : super.fromNativeLayer(); + + @override + PropertyValue get opacity => + ffiLayer.rasterOpacity.toPropertyValue() ?? + RasterStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.rasterOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get hueRotate => + ffiLayer.rasterHueRotation.toPropertyValue() ?? + RasterStyleLayer.defaultHueRotate; + + @override + set hueRotate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterHueRotation = property.expression.toNSExpression(); + } else { + ffiLayer.rasterHueRotation = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get brightnessMin => + ffiLayer.minimumRasterBrightness.toPropertyValue() ?? + RasterStyleLayer.defaultBrightnessMin; + + @override + set brightnessMin(PropertyValue property) { + if (property.isExpression) { + ffiLayer.minimumRasterBrightness = property.expression.toNSExpression(); + } else { + ffiLayer.minimumRasterBrightness = + NSExpression.expressionForConstantValue(property.value.toNSNumber()); + } + } + + @override + PropertyValue get brightnessMax => + ffiLayer.maximumRasterBrightness.toPropertyValue() ?? + RasterStyleLayer.defaultBrightnessMax; + + @override + set brightnessMax(PropertyValue property) { + if (property.isExpression) { + ffiLayer.maximumRasterBrightness = property.expression.toNSExpression(); + } else { + ffiLayer.maximumRasterBrightness = + NSExpression.expressionForConstantValue(property.value.toNSNumber()); + } + } + + @override + PropertyValue get saturation => + ffiLayer.rasterSaturation.toPropertyValue() ?? + RasterStyleLayer.defaultSaturation; + + @override + set saturation(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterSaturation = property.expression.toNSExpression(); + } else { + ffiLayer.rasterSaturation = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get contrast => + ffiLayer.rasterContrast.toPropertyValue() ?? + RasterStyleLayer.defaultContrast; + + @override + set contrast(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterContrast = property.expression.toNSExpression(); + } else { + ffiLayer.rasterContrast = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get resampling => + ffiLayer.rasterResamplingMode.toEnumPropertyValue( + RasterResampling.values, + ) ?? + RasterStyleLayer.defaultResampling; + + @override + set resampling(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterResamplingMode = property.expression.toNSExpression(); + } else { + ffiLayer.rasterResamplingMode = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get fadeDuration => + ffiLayer.rasterFadeDuration.toPropertyValue() ?? + RasterStyleLayer.defaultFadeDuration; + + @override + set fadeDuration(PropertyValue property) { + if (property.isExpression) { + ffiLayer.rasterFadeDuration = property.expression.toNSExpression(); + } else { + ffiLayer.rasterFadeDuration = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style/layers/style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/style_layer.dart new file mode 100644 index 000000000..64f0ece34 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/style_layer.dart @@ -0,0 +1,46 @@ +import 'package:flutter/painting.dart'; +import 'package:maplibre_ios/src/extensions.dart'; +import 'package:maplibre_ios/src/maplibre_ffi.g.dart'; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; +import 'package:objective_c/objective_c.dart'; + +part 'background_style_layer.dart'; +part 'circle_style_layer.dart'; +part 'fill_extrusion_style_layer.dart'; +part 'fill_style_layer.dart'; +part 'heatmap_style_layer.dart'; +part 'hillshade_style_layer.dart'; +part 'line_style_layer.dart'; +part 'raster_style_layer.dart'; +part 'symbol_style_layer.dart'; + +/// iOS implementation of [StyleLayer]. +abstract class StyleLayerIos + implements StyleLayer { + /// Construct an [StyleLayerIos] from a ObjC layer. + StyleLayerIos.fromNativeLayer(this.ffiLayer); + + /// The ObjC layer instance. + final FfiLayer ffiLayer; + + @override + String get id => ffiLayer.identifier.toString(); + + @override + double get maxZoom => ffiLayer.maximumZoomLevel; + + @override + set maxZoom(double value) => ffiLayer.maximumZoomLevel = value; + + @override + double get minZoom => ffiLayer.minimumZoomLevel; + + @override + set minZoom(double value) => ffiLayer.minimumZoomLevel = value; + + @override + bool get visible => ffiLayer.isVisible; + + @override + set visible(bool value) => ffiLayer.isVisible = value; +} diff --git a/packages/maplibre_ios/lib/src/style/layers/symbol_style_layer.dart b/packages/maplibre_ios/lib/src/style/layers/symbol_style_layer.dart new file mode 100644 index 000000000..7989a2481 --- /dev/null +++ b/packages/maplibre_ios/lib/src/style/layers/symbol_style_layer.dart @@ -0,0 +1,1116 @@ +part of 'style_layer.dart'; + +/// iOS implementation of [SymbolStyleLayer]. +class SymbolStyleLayerIos extends StyleLayerIos + implements SymbolStyleLayer { + /// Default constructor for a [SymbolStyleLayerIos] instance. + factory SymbolStyleLayerIos({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) { + final layer = SymbolStyleLayerIos.fromNativeLayer( + MLNSymbolStyleLayer.new$()..initWithIdentifier( + id.toNSString(), + source: MLNSource()..initWithIdentifier(sourceId.toNSString()), + ), + ); + + layer.minZoom = minZoom; + layer.maxZoom = maxZoom; + layer.visible = visible; + if (filter != null) layer.filter = filter; + if (sourceLayerId != null) layer.sourceLayerId = sourceLayerId; + layer.sortKey = sortKey; + layer.placement = placement; + layer.spacing = spacing; + layer.avoidEdges = avoidEdges; + layer.zOrder = zOrder; + layer.iconAllowOverlap = iconAllowOverlap; + layer.iconOverlap = iconOverlap; + layer.iconIgnorePlacement = iconIgnorePlacement; + layer.iconOptional = iconOptional; + layer.iconRotationAlignment = iconRotationAlignment; + layer.iconSize = iconSize; + layer.iconTextFit = iconTextFit; + layer.iconTextFitPadding = iconTextFitPadding; + layer.iconImage = iconImage; + layer.iconRotate = iconRotate; + layer.iconPadding = iconPadding; + layer.iconKeepUpright = iconKeepUpright; + layer.iconOffset = iconOffset; + layer.iconAnchor = iconAnchor; + layer.iconPitchAlignment = iconPitchAlignment; + layer.textPitchAlignment = textPitchAlignment; + layer.textRotationAlignment = textRotationAlignment; + layer.textField = textField; + layer.textFont = textFont; + layer.textSize = textSize; + layer.textMaxWidth = textMaxWidth; + layer.textLineHeight = textLineHeight; + layer.textLetterSpacing = textLetterSpacing; + layer.textJustify = textJustify; + layer.textRadialOffset = textRadialOffset; + layer.textVariableAnchor = textVariableAnchor; + layer.textVariableAnchorOffset = textVariableAnchorOffset; + layer.textAnchor = textAnchor; + layer.textMaxAngle = textMaxAngle; + layer.textWritingMode = textWritingMode; + layer.textRotate = textRotate; + layer.textPadding = textPadding; + layer.textKeepUpright = textKeepUpright; + layer.textTransform = textTransform; + layer.textOffset = textOffset; + layer.textAllowOverlap = textAllowOverlap; + layer.textOverlap = textOverlap; + layer.textIgnorePlacement = textIgnorePlacement; + layer.textOptional = textOptional; + layer.iconOpacity = iconOpacity; + layer.iconColor = iconColor; + layer.iconHaloColor = iconHaloColor; + layer.iconHaloWidth = iconHaloWidth; + layer.iconHaloBlur = iconHaloBlur; + layer.iconTranslate = iconTranslate; + layer.iconTranslateAnchor = iconTranslateAnchor; + layer.textOpacity = textOpacity; + layer.textColor = textColor; + layer.textHaloColor = textHaloColor; + layer.textHaloWidth = textHaloWidth; + layer.textHaloBlur = textHaloBlur; + layer.textTranslate = textTranslate; + layer.textTranslateAnchor = textTranslateAnchor; + return layer; + } + + /// Construct a [SymbolStyleLayerIos] from a ObjC layer. + SymbolStyleLayerIos.fromNativeLayer(super.ffiLayer) : super.fromNativeLayer(); + + @override + PropertyValue get placement => + ffiLayer.symbolPlacement.toEnumPropertyValue(SymbolPlacement.values) ?? + SymbolStyleLayer.defaultPlacement; + + @override + set placement(PropertyValue property) { + if (property.isExpression) { + ffiLayer.symbolPlacement = property.expression.toNSExpression(); + } else { + ffiLayer.symbolPlacement = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get spacing => + ffiLayer.symbolSpacing.toPropertyValue() ?? + SymbolStyleLayer.defaultSpacing; + + @override + set spacing(PropertyValue property) { + if (property.isExpression) { + ffiLayer.symbolSpacing = property.expression.toNSExpression(); + } else { + ffiLayer.symbolSpacing = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get avoidEdges => + ffiLayer.symbolAvoidsEdges.toPropertyValue() ?? + SymbolStyleLayer.defaultAvoidEdges; + + @override + set avoidEdges(PropertyValue property) { + if (property.isExpression) { + ffiLayer.symbolAvoidsEdges = property.expression.toNSExpression(); + } else { + ffiLayer.symbolAvoidsEdges = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get zOrder => + ffiLayer.symbolZOrder.toEnumPropertyValue(SymbolZOrder.values) ?? + SymbolStyleLayer.defaultZOrder; + + @override + set zOrder(PropertyValue property) { + if (property.isExpression) { + ffiLayer.symbolZOrder = property.expression.toNSExpression(); + } else { + ffiLayer.symbolZOrder = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue? get sortKey => + ffiLayer.symbolSortKey.toPropertyValue(); + + @override + set sortKey(PropertyValue? property) { + if (property == null) { + ffiLayer.symbolSortKey = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.symbolSortKey = property.expression.toNSExpression(); + } else { + ffiLayer.symbolSortKey = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconAllowOverlap => + ffiLayer.iconAllowsOverlap.toPropertyValue() ?? + SymbolStyleLayer.defaultIconAllowOverlap; + + @override + set iconAllowOverlap(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconAllowsOverlap = property.expression.toNSExpression(); + } else { + ffiLayer.iconAllowsOverlap = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconOverlap => + ffiLayer.iconAllowOverlap.toEnumPropertyValue(SymbolOverlap.values) ?? + SymbolStyleLayer.defaultIconOverlap; + + @override + set iconOverlap(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconAllowOverlap = property.expression.toNSExpression(); + } else { + ffiLayer.iconAllowOverlap = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get iconIgnorePlacement => + ffiLayer.iconIgnoresPlacement.toPropertyValue() ?? + SymbolStyleLayer.defaultIconIgnorePlacement; + + @override + set iconIgnorePlacement(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconIgnoresPlacement = property.expression.toNSExpression(); + } else { + ffiLayer.iconIgnoresPlacement = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconOptional => + ffiLayer.isIconOptional.toPropertyValue() ?? + SymbolStyleLayer.defaultIconOptional; + + @override + set iconOptional(PropertyValue property) { + if (property.isExpression) { + ffiLayer.isIconOptional = property.expression.toNSExpression(); + } else { + ffiLayer.isIconOptional = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconRotationAlignment => + ffiLayer.iconRotationAlignment.toEnumPropertyValue( + IconRotationAlignment.values, + ) ?? + SymbolStyleLayer.defaultIconRotationAlignment; + + @override + set iconRotationAlignment(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconRotationAlignment = property.expression.toNSExpression(); + } else { + ffiLayer.iconRotationAlignment = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get iconSize => + ffiLayer.iconScale.toPropertyValue() ?? SymbolStyleLayer.defaultIconSize; + + @override + set iconSize(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconScale = property.expression.toNSExpression(); + } else { + ffiLayer.iconScale = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconTextFit => + ffiLayer.iconTextFit.toEnumPropertyValue(IconTextFit.values) ?? + SymbolStyleLayer.defaultIconTextFit; + + @override + set iconTextFit(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconTextFit = property.expression.toNSExpression(); + } else { + ffiLayer.iconTextFit = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get iconTextFitPadding => + ffiLayer.iconTextFitPadding.toEdgeInsetsPropertyValue() ?? + SymbolStyleLayer.defaultIconTextFitPadding; + + @override + set iconTextFitPadding(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconTextFitPadding = property.expression.toNSExpression(); + } else { + ffiLayer.iconTextFitPadding = property.value.toNSExpression(); + } + } + + @override + PropertyValue? get iconImage => + ffiLayer.iconImageName.toPropertyValue(); + + @override + set iconImage(PropertyValue? property) { + if (property == null) { + ffiLayer.iconImageName = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.iconImageName = property.expression.toNSExpression(); + } else { + ffiLayer.iconImageName = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } + + @override + PropertyValue get iconRotate => + ffiLayer.iconRotation.toPropertyValue() ?? + SymbolStyleLayer.defaultIconRotate; + + @override + set iconRotate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconRotation = property.expression.toNSExpression(); + } else { + ffiLayer.iconRotation = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconPadding => + ffiLayer.iconPadding.toEdgeInsetsPropertyValue() ?? + SymbolStyleLayer.defaultIconPadding; + + @override + set iconPadding(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconPadding = property.expression.toNSExpression(); + } else { + ffiLayer.iconPadding = property.value.toNSExpression(); + } + } + + @override + PropertyValue get iconKeepUpright => + ffiLayer.keepsIconUpright.toPropertyValue() ?? + SymbolStyleLayer.defaultIconKeepUpright; + + @override + set iconKeepUpright(PropertyValue property) { + if (property.isExpression) { + ffiLayer.keepsIconUpright = property.expression.toNSExpression(); + } else { + ffiLayer.keepsIconUpright = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconOffset => + ffiLayer.iconOffset.toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultIconOffset; + + @override + set iconOffset(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconOffset = property.expression.toNSExpression(); + } else { + ffiLayer.iconOffset = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get iconAnchor => + ffiLayer.iconAnchor.toEnumPropertyValue(IconAnchor.values) ?? + SymbolStyleLayer.defaultIconAnchor; + + @override + set iconAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.iconAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get iconPitchAlignment => + ffiLayer.iconPitchAlignment.toEnumPropertyValue( + IconPitchAlignment.values, + ) ?? + SymbolStyleLayer.defaultIconPitchAlignment; + + @override + set iconPitchAlignment(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconPitchAlignment = property.expression.toNSExpression(); + } else { + ffiLayer.iconPitchAlignment = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textPitchAlignment => + ffiLayer.textPitchAlignment.toEnumPropertyValue( + TextPitchAlignment.values, + ) ?? + SymbolStyleLayer.defaultTextPitchAlignment; + + @override + set textPitchAlignment(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textPitchAlignment = property.expression.toNSExpression(); + } else { + ffiLayer.textPitchAlignment = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textRotationAlignment => + ffiLayer.textRotationAlignment.toEnumPropertyValue( + TextRotationAlignment.values, + ) ?? + SymbolStyleLayer.defaultTextRotationAlignment; + + @override + set textRotationAlignment(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textRotationAlignment = property.expression.toNSExpression(); + } else { + ffiLayer.textRotationAlignment = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textField => + ffiLayer.text.toPropertyValue() ?? SymbolStyleLayer.defaultTextField; + + @override + set textField(PropertyValue property) { + if (property.isExpression) { + ffiLayer.text = property.expression.toNSExpression(); + } else { + ffiLayer.text = NSExpression.expressionForConstantValue( + property.value.toNSString(), + ); + } + } + + @override + PropertyValue> get textFont => throw UnimplementedError(); + + @override + set textFont(PropertyValue> property) { + if (property.isExpression) { + ffiLayer.textFontNames = property.expression.toNSExpression(); + } else { + ffiLayer.textFontNames = NSExpression.expressionForConstantValue( + property.value + .map((e) => e) + .toList(growable: false) + .toNSArray(), + ); + } + } + + @override + PropertyValue get textSize => + ffiLayer.textFontSize.toPropertyValue() ?? + SymbolStyleLayer.defaultTextSize; + + @override + set textSize(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textFontSize = property.expression.toNSExpression(); + } else { + ffiLayer.textFontSize = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textMaxWidth => + ffiLayer.maximumTextWidth.toPropertyValue() ?? + SymbolStyleLayer.defaultTextMaxWidth; + + @override + set textMaxWidth(PropertyValue property) { + if (property.isExpression) { + ffiLayer.maximumTextWidth = property.expression.toNSExpression(); + } else { + ffiLayer.maximumTextWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textLineHeight => + ffiLayer.textLineHeight.toPropertyValue() ?? + SymbolStyleLayer.defaultTextLineHeight; + + @override + set textLineHeight(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textLineHeight = property.expression.toNSExpression(); + } else { + ffiLayer.textLineHeight = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textLetterSpacing => + ffiLayer.textLetterSpacing.toPropertyValue() ?? + SymbolStyleLayer.defaultTextLetterSpacing; + + @override + set textLetterSpacing(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textLetterSpacing = property.expression.toNSExpression(); + } else { + ffiLayer.textLetterSpacing = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textJustify => + ffiLayer.textJustification.toEnumPropertyValue(TextJustify.values) ?? + SymbolStyleLayer.defaultTextJustify; + + @override + set textJustify(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textJustification = property.expression.toNSExpression(); + } else { + ffiLayer.textJustification = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textRadialOffset => + ffiLayer.textRadialOffset.toPropertyValue() ?? + SymbolStyleLayer.defaultTextRadialOffset; + + @override + set textRadialOffset(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textRadialOffset = property.expression.toNSExpression(); + } else { + ffiLayer.textRadialOffset = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue>? get textVariableAnchor => + throw UnimplementedError(); + + @override + set textVariableAnchor(PropertyValue>? property) { + if (property == null) { + ffiLayer.textVariableAnchor = NSExpression.expressionForConstantValue( + null, + ); + } else if (property.isExpression) { + ffiLayer.textVariableAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.textVariableAnchor = NSExpression.expressionForConstantValue( + property.value + .map((e) => e.name) + .toList(growable: false) + .toNSArray(), + ); + } + } + + @override + PropertyValue>? get textVariableAnchorOffset => + throw UnimplementedError(); + + @override + set textVariableAnchorOffset(PropertyValue>? property) { + if (property == null) { + ffiLayer.textVariableAnchorOffset = + NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.textVariableAnchorOffset = property.expression.toNSExpression(); + } else { + throw UnimplementedError(); + } + } + + @override + PropertyValue get textAnchor => + ffiLayer.textAnchor.toEnumPropertyValue(TextAnchor.values) ?? + SymbolStyleLayer.defaultTextAnchor; + + @override + set textAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.textAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textMaxAngle => + ffiLayer.maximumTextAngle.toPropertyValue() ?? + SymbolStyleLayer.defaultTextMaxAngle; + + @override + set textMaxAngle(PropertyValue property) { + if (property.isExpression) { + ffiLayer.maximumTextAngle = property.expression.toNSExpression(); + } else { + ffiLayer.maximumTextAngle = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue>? get textWritingMode => + throw UnimplementedError(); + + @override + set textWritingMode(PropertyValue>? property) { + if (property == null) { + ffiLayer.textWritingModes = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.textWritingModes = property.expression.toNSExpression(); + } else { + ffiLayer.textWritingModes = NSExpression.expressionForConstantValue( + property.value + .map((e) => e.name) + .toList(growable: false) + .toNSArray(), + ); + } + } + + @override + PropertyValue get textRotate => + ffiLayer.textRotation.toPropertyValue() ?? + SymbolStyleLayer.defaultTextRotate; + + @override + set textRotate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textRotation = property.expression.toNSExpression(); + } else { + ffiLayer.textRotation = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textPadding => + ffiLayer.textPadding.toPropertyValue() ?? + SymbolStyleLayer.defaultTextPadding; + + @override + set textPadding(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textPadding = property.expression.toNSExpression(); + } else { + ffiLayer.textPadding = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textKeepUpright => + ffiLayer.keepsTextUpright.toPropertyValue() ?? + SymbolStyleLayer.defaultTextKeepUpright; + + @override + set textKeepUpright(PropertyValue property) { + if (property.isExpression) { + ffiLayer.keepsTextUpright = property.expression.toNSExpression(); + } else { + ffiLayer.keepsTextUpright = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textTransform => + ffiLayer.textTransform.toEnumPropertyValue(TextTransform.values) ?? + SymbolStyleLayer.defaultTextTransform; + + @override + set textTransform(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textTransform = property.expression.toNSExpression(); + } else { + ffiLayer.textTransform = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textOffset => + ffiLayer.textOffset.toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultTextOffset; + + @override + set textOffset(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textOffset = property.expression.toNSExpression(); + } else { + ffiLayer.textOffset = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get textAllowOverlap => + ffiLayer.textAllowsOverlap.toPropertyValue() ?? + SymbolStyleLayer.defaultTextAllowOverlap; + + @override + set textAllowOverlap(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textAllowsOverlap = property.expression.toNSExpression(); + } else { + ffiLayer.textAllowsOverlap = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue? get textOverlap => + ffiLayer.textAllowOverlap.toEnumPropertyValue(SymbolOverlap.values); + + @override + set textOverlap(PropertyValue? property) { + if (property == null) { + ffiLayer.textAllowOverlap = NSExpression.expressionForConstantValue(null); + } else if (property.isExpression) { + ffiLayer.textAllowOverlap = property.expression.toNSExpression(); + } else { + ffiLayer.textAllowOverlap = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textIgnorePlacement => + ffiLayer.textIgnoresPlacement.toPropertyValue() ?? + SymbolStyleLayer.defaultTextIgnorePlacement; + + @override + set textIgnorePlacement(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textIgnoresPlacement = property.expression.toNSExpression(); + } else { + ffiLayer.textIgnoresPlacement = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textOptional => + ffiLayer.isTextOptional.toPropertyValue() ?? + SymbolStyleLayer.defaultTextOptional; + + @override + set textOptional(PropertyValue property) { + if (property.isExpression) { + ffiLayer.isTextOptional = property.expression.toNSExpression(); + } else { + ffiLayer.isTextOptional = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconOpacity => + ffiLayer.iconOpacity.toPropertyValue() ?? + SymbolStyleLayer.defaultIconOpacity; + + @override + set iconOpacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.iconOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconColor => + ffiLayer.iconColor.toColorPropertyValue() ?? + SymbolStyleLayer.defaultIconColor; + + @override + set iconColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconColor = property.expression.toNSExpression(); + } else { + ffiLayer.iconColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get iconHaloColor => + ffiLayer.iconHaloColor.toColorPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloColor; + + @override + set iconHaloColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconHaloColor = property.expression.toNSExpression(); + } else { + ffiLayer.iconHaloColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get iconHaloWidth => + ffiLayer.iconHaloWidth.toPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloWidth; + + @override + set iconHaloWidth(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconHaloWidth = property.expression.toNSExpression(); + } else { + ffiLayer.iconHaloWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconHaloBlur => + ffiLayer.iconHaloBlur.toPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloBlur; + + @override + set iconHaloBlur(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconHaloBlur = property.expression.toNSExpression(); + } else { + ffiLayer.iconHaloBlur = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get iconTranslate => + ffiLayer.iconTranslation.toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultIconTranslate; + + @override + set iconTranslate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconTranslation = property.expression.toNSExpression(); + } else { + ffiLayer.iconTranslation = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get iconTranslateAnchor => + ffiLayer.iconTranslationAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + SymbolStyleLayer.defaultIconTranslateAnchor; + + @override + set iconTranslateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.iconTranslationAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.iconTranslationAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + PropertyValue get textOpacity => + ffiLayer.textOpacity.toPropertyValue() ?? + SymbolStyleLayer.defaultTextOpacity; + + @override + set textOpacity(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textOpacity = property.expression.toNSExpression(); + } else { + ffiLayer.textOpacity = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textColor => + ffiLayer.textColor.toColorPropertyValue() ?? + SymbolStyleLayer.defaultTextColor; + + @override + set textColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textColor = property.expression.toNSExpression(); + } else { + ffiLayer.textColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get textHaloColor => + ffiLayer.textHaloColor.toColorPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloColor; + + @override + set textHaloColor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textHaloColor = property.expression.toNSExpression(); + } else { + ffiLayer.textHaloColor = NSExpression.expressionForConstantValue( + property.value.toUIColor(), + ); + } + } + + @override + PropertyValue get textHaloWidth => + ffiLayer.textHaloWidth.toPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloWidth; + + @override + set textHaloWidth(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textHaloWidth = property.expression.toNSExpression(); + } else { + ffiLayer.textHaloWidth = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textHaloBlur => + ffiLayer.textHaloBlur.toPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloBlur; + + @override + set textHaloBlur(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textHaloBlur = property.expression.toNSExpression(); + } else { + ffiLayer.textHaloBlur = NSExpression.expressionForConstantValue( + property.value.toNSNumber(), + ); + } + } + + @override + PropertyValue get textTranslate => + ffiLayer.textTranslation.toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultTextTranslate; + + @override + set textTranslate(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textTranslation = property.expression.toNSExpression(); + } else { + ffiLayer.textTranslation = NSExpression.expressionForConstantValue( + [property.value.dx, property.value.dy].toNSArray(), + ); + } + } + + @override + PropertyValue get textTranslateAnchor => + ffiLayer.textTranslationAnchor.toEnumPropertyValue( + ReferenceSpace.values, + ) ?? + SymbolStyleLayer.defaultTextTranslateAnchor; + + @override + set textTranslateAnchor(PropertyValue property) { + if (property.isExpression) { + ffiLayer.textTranslationAnchor = property.expression.toNSExpression(); + } else { + ffiLayer.textTranslationAnchor = NSExpression.expressionForConstantValue( + property.value.name.toNSString(), + ); + } + } + + @override + Expression? get filter => ffiLayer.predicate?.toExpression(); + + @override + set filter(Expression expression) { + ffiLayer.predicate = expression.toNSPredicate(); + } + + @override + String? get sourceLayerId => ffiLayer.sourceLayerIdentifier?.toDartString(); + + @override + set sourceLayerId(String? value) { + ffiLayer.sourceLayerIdentifier = value?.toNSString(); + } + + @override + String get sourceId => ffiLayer.sourceIdentifier!.toDartString(); +} diff --git a/packages/maplibre_ios/lib/src/style_controller.dart b/packages/maplibre_ios/lib/src/style_controller.dart index 51c749983..8a95f30c0 100644 --- a/packages/maplibre_ios/lib/src/style_controller.dart +++ b/packages/maplibre_ios/lib/src/style_controller.dart @@ -20,87 +20,7 @@ class StyleControllerIos extends StyleController { String? aboveLayerId, int? atIndex, }) async { - final ffiId = layer.id.toNSString(); - final prevStyleLayer = _ffiStyle.layerWithIdentifier(ffiId); - if (prevStyleLayer != null) { - throw Exception( - 'A Layer with the id "${layer.id}" already exists in the map style.', - ); - } - - MLNStyleLayer? ffiLayer; - switch (layer) { - case BackgroundStyleLayer(): - ffiLayer = MLNBackgroundStyleLayer.new$() - ..initWithIdentifier(ffiId) - ..backgroundColor = NSExpression.expressionWithFormat( - layer.color.toHexString().toNSString(), - ); - case StyleLayerWithSource(): - final ffiSource = _ffiStyle.sourceWithIdentifier( - layer.sourceId.toNSString(), - ); - if (ffiSource == null) { - throw Exception('Source "${layer.sourceId}" does not exist.'); - } - switch (layer) { - case FillStyleLayer(): - ffiLayer = MLNFillStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case CircleStyleLayer(): - ffiLayer = MLNCircleStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case FillExtrusionStyleLayer(): - ffiLayer = MLNFillExtrusionStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case HeatmapStyleLayer(): - ffiLayer = MLNHeatmapStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case HillshadeStyleLayer(): - ffiLayer = MLNHillshadeStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case LineStyleLayer(): - ffiLayer = MLNLineStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case RasterStyleLayer(): - ffiLayer = MLNRasterStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - case SymbolStyleLayer(): - ffiLayer = MLNSymbolStyleLayer.new$() - ..initWithIdentifier(ffiId, source: ffiSource); - } - } - - if (ffiLayer == null) { - throw UnimplementedError( - 'The Layer is not supported: ${layer.runtimeType}', - ); - } - ffiLayer.minimumZoomLevel = layer.minZoom; - ffiLayer.maximumZoomLevel = layer.maxZoom; - ffiLayer.setProperties(layer.paint); - ffiLayer.setProperties(layer.layout); - if (layer case StyleLayerWithSource()) { - if (layer.sourceLayerId case final sourceLayerId?) { - final ffiVectorLayer = MLNVectorStyleLayer.as(ffiLayer); - if (!MLNVectorStyleLayer.isA(ffiLayer)) { - throw Exception( - 'sourceLayerId is only applicable for vector style layers.', - ); - } - ffiVectorLayer.sourceLayerIdentifier = sourceLayerId.toNSString(); - } - if (layer.filter case final filter?) { - if (!MLNVectorStyleLayer.isA(ffiLayer)) { - throw Exception('filter is only applicable for vector style layers.'); - } - final expression = jsonEncode(filter).toNSString(); - final ffiPredicate = Helpers.parsePredicateWithRaw(expression); - final ffiVectorLayer = MLNVectorStyleLayer.as(ffiLayer); - ffiVectorLayer.predicate = ffiPredicate; - } - } - + final ffiLayer = (layer as StyleLayerIos).ffiLayer; if (belowLayerId case final String id) { final belowLayer = _ffiStyle.layerWithIdentifier(id.toNSString()); if (belowLayer == null) { diff --git a/packages/maplibre_ios/tool/ffigen.dart b/packages/maplibre_ios/tool/ffigen.dart index da072406f..dee6ecffe 100644 --- a/packages/maplibre_ios/tool/ffigen.dart +++ b/packages/maplibre_ios/tool/ffigen.dart @@ -28,8 +28,7 @@ void main(List args) { // '-F$frameworkPath', '-I${frameworkPath}MapLibre.framework/Headers', '-Fios/.build/MapLibre.xcframework/ios-arm64/', '-Iios/.build/MapLibre.xcframework/ios-arm64/MapLibre.framework/Headers', - '-isysroot', - '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk', + '-isysroot', iosSdkUri.toFilePath(), ], include: (header) { const include = { @@ -54,6 +53,7 @@ void main(List args) { 'MLNVectorStyleLayer.h', 'MLNAttributionInfo.h', 'NSExpression+MLNAdditions.h', + 'NSPredicate+MLNAdditions.h', 'MLNOfflineStorage.h', 'MLNOfflinePack.h', 'MLNOfflineRegion.h', @@ -76,6 +76,7 @@ void main(List args) { 'NSString', 'CLLocationCoordinate2D', 'NSAttributedString', + 'UIColor', 'UIImage', 'UIScreen', 'UIAction', @@ -88,6 +89,7 @@ void main(List args) { 'UITableViewHeaderFooterView', 'UIViewConfigurationState', 'NSExpression', + 'NSPredicate', 'Helpers', 'MapLibreRegistry', 'Extensions', diff --git a/packages/maplibre_platform_interface/lib/expressions.dart b/packages/maplibre_platform_interface/lib/expressions.dart new file mode 100644 index 000000000..3c43254e0 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/expressions.dart @@ -0,0 +1 @@ +export 'package:maplibre_platform_interface/src/style/expressions/expressions.dart'; diff --git a/packages/maplibre_platform_interface/lib/maplibre_platform_interface.dart b/packages/maplibre_platform_interface/lib/maplibre_platform_interface.dart index 5daa2bd1e..1d8043964 100644 --- a/packages/maplibre_platform_interface/lib/maplibre_platform_interface.dart +++ b/packages/maplibre_platform_interface/lib/maplibre_platform_interface.dart @@ -16,6 +16,7 @@ export 'src/options/map_options.dart'; export 'src/permission_manager.dart'; export 'src/platform_interface.dart'; export 'src/queried_layer.dart'; +export 'src/style/property_value.dart'; export 'src/style/style.dart'; export 'src/style_controller.dart'; export 'src/utils.dart'; diff --git a/packages/maplibre_platform_interface/lib/src/layer/layer.dart b/packages/maplibre_platform_interface/lib/src/layer/layer.dart index 1e6683e04..559629dba 100644 --- a/packages/maplibre_platform_interface/lib/src/layer/layer.dart +++ b/packages/maplibre_platform_interface/lib/src/layer/layer.dart @@ -25,12 +25,6 @@ abstract class Layer> { /// Get a unique layer id. String getLayerId(int index) => 'maplibre-layer-$index'; - /// Build the paint properties. - Map getPaint(); - - /// Build the layout properties. - Map getLayout(); - /// Add the annotation layer to the map. StyleLayer createStyleLayer(int index); diff --git a/packages/maplibre_platform_interface/lib/src/platform_interface.dart b/packages/maplibre_platform_interface/lib/src/platform_interface.dart index d97956bff..3c22fe730 100644 --- a/packages/maplibre_platform_interface/lib/src/platform_interface.dart +++ b/packages/maplibre_platform_interface/lib/src/platform_interface.dart @@ -1,9 +1,10 @@ +import 'package:flutter/painting.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:plugin_platform_interface/plugin_platform_interface.dart'; /// The interface that federated plugin implementations of MapLibre can /// implement. -abstract class MapLibrePlatform extends PlatformInterface { +class MapLibrePlatform extends PlatformInterface { /// Constructs a MapLibrePlatform. MapLibrePlatform() : super(token: _token); @@ -48,6 +49,256 @@ abstract class MapLibrePlatform extends PlatformInterface { /// Return whether user location is supported on the current platform. bool get userLocationIsSupported => false; + + /// Create a platform specific [BackgroundStyleLayer] object. + BackgroundStyleLayer createBackgroundStyleLayer({ + required String id, + required bool visible, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => throw UnsupportedError( + 'BackgroundStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [CircleStyleLayer] object. + CircleStyleLayer createCircleStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => throw UnsupportedError( + 'CircleStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [ColorReliefStyleLayer] object. + ColorReliefStyleLayer createColorReliefStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => throw UnsupportedError( + 'ColorReliefStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [FillExtrusionStyleLayer] object. + FillExtrusionStyleLayer createFillExtrusionStyleLayer({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => throw UnsupportedError( + 'FillExtrusionStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [FillStyleLayer] object. + FillStyleLayer createFillStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) => throw UnsupportedError( + 'FillStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [HeatmapStyleLayer] object. + HeatmapStyleLayer createHeatmapStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => throw UnsupportedError( + 'HeatmapStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [HillshadeStyleLayer] object. + HillshadeStyleLayer createHillshadeStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) => throw UnsupportedError( + 'HillshadeStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [LineStyleLayer] object. + LineStyleLayer createLineStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => throw UnsupportedError( + 'LineStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [RasterStyleLayer] object. + RasterStyleLayer createRasterStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => throw UnsupportedError( + 'RasterStyleLayer is not supported on this platform.', + ); + + /// Create a platform specific [SymbolStyleLayer] object. + SymbolStyleLayer createSymbolStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? + textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) => throw UnsupportedError( + 'SymbolStyleLayer is not supported on this platform.', + ); } /// A stub implementation of the MapLibrePlatform interface. diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/color.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/color.dart new file mode 100644 index 000000000..ad7206eb3 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/color.dart @@ -0,0 +1,30 @@ +part of 'expressions.dart'; + +/// Returns a four-element array containing the input color's red, green, blue, and alpha components, in that order. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#to-rgba +Expression> toRgba(Expression color) => + Expression.fromJson(['to-rgba', color]); + +/// Creates a color value from red, green, and blue components, which must +/// range between 0 and 255, and an alpha component of 1. If any component is +/// out of range, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#rgb +Expression rgb( + Expression red, + Expression green, + Expression blue, +) => Expression.fromJson(['rgb', red, green, blue]); + +/// Creates a color value from red, green, blue components, which must range +/// between 0 and 255, and an alpha component which must range between zero and +/// one. If any component is out of range, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#rgba +Expression rgba({ + required Expression red, + required Expression green, + required Expression blue, + required Expression alpha, +}) => Expression.fromJson(['rgba', red, green, blue, alpha]); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/color_relief.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/color_relief.dart new file mode 100644 index 000000000..3baf816ae --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/color_relief.dart @@ -0,0 +1,8 @@ +part of 'expressions.dart'; + +/// Gets the elevation of a pixel (in meters above the vertical datum reference +/// of the raster-dem tiles) from a [RasterDemSource]. Can only be used in the +/// [ColorReliefStyleLayer.color]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#elevation +Expression elevation() => const Expression.fromJson(['elevation']); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/decision.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/decision.dart new file mode 100644 index 000000000..fdb6349f2 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/decision.dart @@ -0,0 +1,200 @@ +part of 'expressions.dart'; + +/// Selects the first output whose corresponding test condition evaluates to +/// true, or the fallback value otherwise. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#case +Expression case_({ + required Map, Expression> cases, + required Expression fallback, +}) => Expression.fromJson([ + 'case', + ...cases.entries.expand((e) => [e.key, e.value]), + fallback, +]); + +/// Selects the output whose label value matches the input value, or the fallback value if no match is found. The input can be any expression (e.g. ["get", "building_type"]). Each label must be either: +/// - a single literal value; or +/// - an array of literal values, whose values must be all strings or all numbers (e.g. [100, 101] or ["c", "b"]). The input matches if any of the values in the array matches, similar to the "in" operator. +/// Each label must be unique. If the input type does not match the type of the labels, the result will be the fallback value. +Expression match({ + required Expression input, + required Map cases, + required Object fallback, +}) { + assert( + cases.keys.every((k) => k is String || k is num), + 'All keys in the cases map must be either String or num', + ); + assert( + cases.values.every((v) => v is T || v is Expression), + 'All values in the cases map must be either of type T or Expression', + ); + assert( + fallback is T || fallback is Expression, + 'Fallback value must be either of type T or Expression', + ); + return Expression.fromJson([ + 'match', + input, + ...cases.entries.expand((e) => [e.key, e.value]), + fallback, + ]); +} + +/// Evaluates each expression in turn until the first non-null value is obtained, and returns that value. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#coalesce +Expression coalesce(List> expressions) => + Expression.fromJson(['coalesce', ...expressions]); + +/// Returns `true` if the input values are equal, `false` otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_1 +Expression equal(Object input1, Object input2, {String? collator}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '==', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if the input values are not equal, `false` otherwise. The comparison is strictly typed: values of different runtime types are always considered unequal. Cases where the types are known to be different at parse time are considered invalid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_2 +Expression notEqual(Object input1, Object input2, {String? collator}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '!=', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if the first input is strictly greater than the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_3 +Expression greaterThan(Object input1, Object input2, {String? collator}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '>', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if the first input is strictly less than the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_4 +Expression lessThan(Object input1, Object input2, {String? collator}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '<', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if the first input is greater than or equal to the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_5 +Expression greaterThanOrEqual( + Object input1, + Object input2, { + String? collator, +}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '>=', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if the first input is less than or equal to the second, `false` otherwise. The arguments are required to be either both strings or both numbers; if during evaluation they are not, expression evaluation produces an error. Cases where this constraint is known not to hold at parse time are considered in valid and will produce a parse error. Accepts an optional [collator] argument to control locale-dependent string comparisons. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_6 +Expression lessThanOrEqual( + Object input1, + Object input2, { + String? collator, +}) { + assert( + (input1 is String && input2 is String) || + (input1 is num && input2 is num) || + (input1.runtimeType == input2.runtimeType), + 'Input values must be of the same type or both be strings or numbers', + ); + return Expression.fromJson([ + '<=', + input1, + input2, + if (collator != null) {'collator': collator}, + ]); +} + +/// Returns `true` if all the inputs are `true`, `false` otherwise. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to `false`, the result is `false` and no further input expressions are evaluated. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#all +Expression all(List inputs) => + Expression.fromJson(['all', ...inputs]); + +/// Returns `true` if any of the inputs are `true`, `false` otherwise. The inputs are evaluated in order, and evaluation is short-circuiting: once an input expression evaluates to `true`, the result is `true` and no further input expressions are evaluated. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#any +Expression any_(List inputs) => + Expression.fromJson(['any', ...inputs]); + +/// Logical negation. Returns `true` if the input is `false`, and `false` if the input is `true`. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#! +Expression not(Expression input) => Expression.fromJson(['!', input]); + +/// Returns `true` if the evaluated feature is fully contained inside a boundary of the input geometry, `false` otherwise. The input value can be a valid GeoJSON of type Polygon, MultiPolygon, Feature, or FeatureCollection. Supported features for evaluation: +/// - [Point]: Returns false if a point is on the boundary or falls outside the boundary. +/// - [LineString]: Returns false if any part of a line falls outside the boundary, the line intersects the boundary, or a line's endpoint is on the boundary. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#within +Expression within(SimpleGeometry input) => Expression.fromJson([ + 'within', + switch (input) { + final Point point => point.toText(), + final LineString lineString => lineString.toText(), + _ => ArgumentError.value( + input, + 'input', + 'Unsupported geometry type. Only Point and LineString are supported.', + ), + }, +]); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/expressions.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/expressions.dart new file mode 100644 index 000000000..7dad4ccd6 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/expressions.dart @@ -0,0 +1,20 @@ +import 'dart:ui'; + +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; + +part 'color.dart'; +part 'color_relief.dart'; +part 'decision.dart'; +part 'feature_data.dart'; +part 'heatmap.dart'; +part 'lookup.dart'; +part 'math.dart'; +part 'ramps_scales_curves.dart'; +part 'string.dart'; +part 'types.dart'; +part 'variable_binding.dart'; +part 'zoom.dart'; + +/// Base class for expressions. +extension type const Expression.fromJson(List json) + implements List {} diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/feature_data.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/feature_data.dart new file mode 100644 index 000000000..ea54b7588 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/feature_data.dart @@ -0,0 +1,45 @@ +part of 'expressions.dart'; + +/// Gets the feature properties object. Note that in some cases, it may be more efficient to use `["get", "property_name"]` directly. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#properties +Expression> properties() => + const Expression.fromJson(['properties']); + +/// Retrieves a property value from the current feature's state. Returns null if +/// the requested property is not present on the feature's state. A feature's +/// state is not part of the GeoJSON or vector tile data, and must be set +/// programmatically on each feature. When source.promoteId is not provided, +/// features are identified by their id attribute, which must be an integer or a +/// string that can be cast to an integer. When source.promoteId is provided, +/// features are identified by their promoteId property, which may be a number, +/// string, or any primitive data type. Note that ["feature-state"] can only be +/// used with paint properties that support data-driven styling. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#feature-state +Expression featureState(String propertyName) => + Expression.fromJson(['feature-state', propertyName]); + +/// Returns the feature's simple geometry type: [Point], [LineString], or [Polygon] as [String]. +/// [MultiPoint], [MultiLineString], and [MultiPolygon] are returned as [Point], [LineString], and [Polygon], respectively. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#geometry-type +Expression geometryType() => + const Expression.fromJson(['geometry-type']); + +/// Gets the feature's id, if it has one. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#id +Expression id_() => const Expression.fromJson(['id']); + +/// Gets the progress along a gradient line. +/// Can only be used in the [LineStyleLayer.gradient] property. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#line-progress +Expression lineProgress() => const Expression.fromJson(['line-progress']); + +/// Gets the value of a cluster property accumulated so far. Can only be used in the clusterProperties option of a clustered GeoJSON source. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#accumulated +Expression accumulated() => + const Expression.fromJson(['accumulated']); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/heatmap.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/heatmap.dart new file mode 100644 index 000000000..98b4161a0 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/heatmap.dart @@ -0,0 +1,9 @@ +part of 'expressions.dart'; + +/// Gets the kernel density estimation of a pixel in a heatmap layer, which is +/// a relative measure of how many data points are crowded around a particular +/// pixel. Can only be used in the [HeatmapStyleLayer.color] property. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#heatmap-density +Expression heatmapDensity() => + const Expression.fromJson(['heatmap-density']); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/lookup.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/lookup.dart new file mode 100644 index 000000000..0a729d5d0 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/lookup.dart @@ -0,0 +1,145 @@ +part of 'expressions.dart'; + +/// Retrieves an item from an array. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#at +Expression at({ + required Object index, + required Expression list, +}) { + assert( + index is num || index is Expression, + 'index must be a number or an expression that evaluates to a number', + ); + return Expression.fromJson(['at', index, list]); +} + +/// Determines whether an item exists in an array or a substring exists in a +/// string. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#in +Expression in_({ + required Object item, + required Object list, +}) { + assert( + item is num || + item is String || + item is Expression || + item is Expression, + 'item must be a number, a string, or an expression that evaluates to a number or a string', + ); + assert( + list is String || + list is Expression || + list is List || + list is Expression, + 'list must be a string, an array, or an expression that evaluates to a string or an array', + ); + return Expression.fromJson(['in', item, list]); +} + +/// Returns the first position at which an item can be found in an array or a +/// substring can be found in a string, or -1 if the input cannot be found. +/// Accepts an optional index from where to begin the search. In a string, a +/// UTF-16 surrogate pair counts as a single position. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#index-of +Expression indexOf({ + required Object item, + required Object list, + Object? fromIndex, +}) { + assert( + item is num || + item is String || + item is Expression || + item is Expression, + 'item must be a number, a string, or an expression that evaluates to a ' + 'number or a string', + ); + assert( + list is String || + list is Expression || + list is List || + list is Expression, + 'list must be a string, an array, or an expression that evaluates to a ' + 'string or an array', + ); + assert( + fromIndex == null || fromIndex is num || fromIndex is Expression, + 'fromIndex must be null, a number, or an expression that evaluates to a ' + 'number', + ); + return Expression.fromJson(['index-of', item, list, ?fromIndex]); +} + +/// Returns a subarray from an array or a substring from a string from a +/// specified start index, or between a start index and an end index if set. +/// The return value is inclusive of the start index but not of the end index. +/// In a string, a UTF-16 surrogate pair counts as a single position. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#slice +Expression slice({ + required Object input, + required Object start, + Object? end, +}) { + assert( + input is String || + input is Expression || + input is List || + input is Expression, + 'input must be a string, an array, or an expression that evaluates to a ' + 'string or an array', + ); + assert( + start is num || start is Expression, + 'start must be a number or an expression that evaluates to a number', + ); + assert( + end == null || end is num || end is Expression, + 'end must be null, a number, or an expression that evaluates to a number', + ); + return Expression.fromJson(['slice', input, start, ?end]); +} + +/// Retrieves a property value from global state that can be set with +/// platform-specific APIs. Defaults can be provided using the `state` +/// root property. Returns null if no value nor default value is set for the +/// retrieved property. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#global-state +Expression globalState(String propertyName) => + Expression.fromJson(['global-state', propertyName]); + +/// Retrieves a property value from the current feature's properties, or from +/// another object if a second argument is provided. Returns null if the +/// requested property is missing. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#get +Expression get(String propertyName, {Map? object}) => + Expression.fromJson(['get', propertyName, ?object]); + +/// Tests for the presence of a property value in the current feature's +/// properties, or from another object if a second argument is provided. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#has +Expression has(String propertyName, {Map? object}) => + Expression.fromJson(['has', propertyName, ?object]); + +/// Gets the length of an array or string. In a string, a UTF-16 surrogate pair +/// counts as a single position. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#length +Expression length(Object input) { + assert( + input is String || + input is Expression || + input is List || + input is Expression, + 'input must be a string, an array, or an expression that evaluates to a ' + 'string or an array', + ); + return Expression.fromJson(['length', input]); +} diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/math.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/math.dart new file mode 100644 index 000000000..9b6bc70ba --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/math.dart @@ -0,0 +1,169 @@ +part of 'expressions.dart'; + +/// Returns the mathematical constant `ln(2)`. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#ln2 +Expression ln2_() => const Expression.fromJson(['ln2']); + +/// Returns the mathematical constant `pi`. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#pi +Expression pi_() => const Expression.fromJson(['pi']); + +/// Returns the mathematical constant `e`. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#e +Expression e_() => const Expression.fromJson(['e']); + +/// Returns the sum of the inputs. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_8 +Expression sum(List> inputs) => + Expression.fromJson(['sum', ...inputs]); + +/// Returns the product of the inputs. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_9 +Expression product(List> inputs) => + Expression.fromJson(['product', ...inputs]); + +/// For two inputs, returns the result of subtracting the second input from the first. For a single input, returns the result of subtracting it from 0. +/// - [input1]: number- The number from which to subtract [input2]. +/// - [input2]: number- The number to subtract from [input1]. If not set, the result is the negation of [input1]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#- +Expression subtract(Expression input1, [Expression? input2]) => + Expression.fromJson(['subtract', input1, ?input2]); + +/// Returns the result of floating point division of the first input by the second. +/// [dividend]: number- The dividend. +/// [input2]: number- The divisor. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_10 +Expression divide(Expression dividend, Expression divisor) => + Expression.fromJson(['divide', dividend, divisor]); + +/// Returns the remainder after integer division of the first input by the second. +/// [dividend]: number- The dividend. +/// [divisor]: number- The divisor. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#_11 +Expression mod_(Expression dividend, Expression divisor) => + Expression.fromJson(['mod', dividend, divisor]); + +/// Returns the result of raising the first input to the power specified by the second. +/// [base]: number- The base. +/// [exponent]: number- The exponent. +Expression pow_(Expression base, Expression exponent) => + Expression.fromJson(['pow', base, exponent]); + +/// Returns the square root of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#sqrt +Expression sqrt_(Expression input) => + Expression.fromJson(['sqrt', input]); + +/// Returns the base 10 logarithm of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#log10 +Expression log10_(Expression input) => + Expression.fromJson(['log10', input]); + +/// Returns the natural logarithm of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#ln +Expression ln_(Expression input) => + Expression.fromJson(['ln', input]); + +/// Returns the base 2 logarithm of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#log2 +Expression log2_(Expression input) => + Expression.fromJson(['log2', input]); + +/// Returns the sine of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#sin +Expression sin_(Expression angle) => + Expression.fromJson(['sin', angle]); + +/// Returns the cosine of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#cos +Expression cos_(Expression angle) => + Expression.fromJson(['cos', angle]); + +/// Returns the tangent of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#tan +Expression tan_(Expression angle) => + Expression.fromJson(['tan', angle]); + +/// Returns the arcsine of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#asin +Expression asin_(Expression input) => + Expression.fromJson(['asin', input]); + +/// Returns the arccosine of the [input]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#acos +Expression acos_(Expression input) => + Expression.fromJson(['acos', input]); + +/// Returns the arctangent of the [input]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#atan +Expression atan_(Expression input) => + Expression.fromJson(['atan', input]); + +/// Returns the minimum value of the [inputs]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#min +Expression min(List> inputs) => + Expression.fromJson(['min', ...inputs]); + +/// Returns the maximum value of the [inputs]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#max +Expression max(List> inputs) => + Expression.fromJson(['max', ...inputs]); + +/// Rounds the [input] to the nearest integer. Halfway values are rounded away from zero. For example, `["round", -1.5]` evaluates to -2. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#round +Expression round(Expression input) => + Expression.fromJson(['round', input]); + +/// Returns the absolute value of the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#abs +Expression abs(Expression input) => + Expression.fromJson(['abs', input]); + +/// Returns the smallest integer that is greater than or equal to the [input]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#ceil +Expression ceil(Expression input) => + Expression.fromJson(['ceil', input]); + +/// Returns the largest integer that is less than or equal to the [input]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#floor +Expression floor(Expression input) => + Expression.fromJson(['floor', input]); + +/// Returns the shortest distance in meters between the evaluated feature and the input [geometry]. +/// The input value can be a valid GeoJSON of type [Point], [MultiPoint], [LineString], [MultiLineString], [Polygon], [MultiPolygon], [Feature], or [FeatureCollection]. Distance values returned may vary in precision due to loss in precision from encoding geometries, particularly below zoom level 13. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#distance +Expression distance(Object? geometry) => Expression.fromJson([ + 'distance', + switch (geometry) { + final SimpleGeometry geometry => geometry.toText(), + final FeatureObject geometry => geometry.toText(), + final Map geometry => geometry, + final List geometry => geometry, + _ => ArgumentError.value(geometry), + }, +]); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/ramps_scales_curves.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/ramps_scales_curves.dart new file mode 100644 index 000000000..56eb01b42 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/ramps_scales_curves.dart @@ -0,0 +1,88 @@ +part of 'expressions.dart'; + +/// Produces discrete, stepped results by evaluating a piecewise-constant function defined by pairs of input and output values ("stops"). The [input] may be any numeric [Expression] (e.g., `["get", "population"]`). [stopInputs] must be numeric [literal]s in strictly ascending order. +/// +/// Returns the output value of the stop just less than the [input], or the first output if the [input] is less than the first stop. +/// - [input]: number- Any numeric expression. +/// - [output0]: any- The result when the input is less than the first stop. +/// - [stopInputs]: number literal- The value of the i-th stop against which the input is compared. +/// - [stopOutputs]: any- The result when the i-th stop is the last stop less than the input. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#step +Expression step({ + required Expression input, + required Expression output0, + required List stopInputs, + required List> stopOutputs, +}) => Expression.fromJson([ + 'step', + [ + input, + output0, + for (int i = 0; i < stopInputs.length; i++) ...[ + stopInputs[i], + stopOutputs[i], + ], + ], +]); + +/// Produces continuous, smooth results by interpolating between pairs of input +/// and output values ("stops"). The input may be any numeric expression +/// (e.g., ["get", "population"]). Stop inputs must be numeric literals in +/// strictly ascending order. The output type must be number, [List], +/// color, [List], or projection. +/// - [type]: interpolation- The interpolation type. +/// - [input]: number- Any numeric expression. +/// - [stopInputs]: number literal- The value of the i-th stop against which the input is compared. +/// - [stopOutputs]: number | [List] | color | [List] | projection- The output value corresponding to the i-th stop. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#interpolate +Expression interpolate({ + required InterpolationType type, + required Expression input, + required List stopInputs, + required List stopOutputs, +}) => Expression.fromJson([ + 'interpolate', + [type, input, ...stopInputs, ...stopOutputs], +]); + +/// Produces continuous, smooth results by interpolating between pairs of input +/// and output values ("stops"). Works like [interpolate], but the output type +/// must be color or [List], and the interpolation is performed in the +/// Hue-Chroma-Luminance color space. +/// - [type]: interpolation- The interpolation type. +/// - [input]: number +/// - [stopInputs]: number literal +/// - [stopOutputs]: color | [List] +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#interpolate-hcl +Expression interpolateHcl({ + required InterpolationType type, + required Expression input, + required List stopInputs, + required List stopOutputs, +}) => Expression.fromJson([ + 'interpolate-hcl', + [type, input, ...stopInputs, ...stopOutputs], +]); + +/// Produces continuous, smooth results by interpolating between pairs of input +/// and output values ("stops"). Works like interpolate, but the output type +/// must be color or [List], and the interpolation is performed in the +/// CIELAB color space. +/// - [type]: interpolation- The interpolation type. +/// - [input]: number +/// - [stopInputs]: number literal +/// - [stopOutputs]: color | [List] +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#interpolate-lab +Expression interpolateLab({ + required InterpolationType type, + required Expression input, + required List stopInputs, + required List stopOutputs, +}) => Expression.fromJson([ + 'interpolate-lab', + [type, input, ...stopInputs, ...stopOutputs], +]); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/string.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/string.dart new file mode 100644 index 000000000..0dc3e871e --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/string.dart @@ -0,0 +1,63 @@ +part of 'expressions.dart'; + +/// Returns true if the [input] string is expected to render legibly. +/// Returns `false` if the [input] string contains sections that cannot be +/// rendered without potential loss of meaning (e.g. Indic scripts that require +/// complex text shaping, or right-to-left scripts if the mapbox-gl-rtl-text +/// plugin is not in use in MapLibre GL JS). +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#is-supported-script +Expression isSupportedScript(Expression input) => + Expression.fromJson(['is-supported-script', input]); + +/// Returns the input string converted to uppercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#upcase +Expression upcase(Expression input) => + Expression.fromJson(['upcase', input]); + +/// Returns the input string converted to lowercase. Follows the Unicode Default Case Conversion algorithm and the locale-insensitive case mappings in the Unicode Character Database. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#downcase +Expression downcase(Expression input) => + Expression.fromJson(['downcase', input]); + +/// Returns a string consisting of the concatenation of the inputs. Each input +/// is converted to a string as if by [toString]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#concat +Expression concat(List inputs) => + Expression.fromJson(['concat', ...inputs]); + +/// Returns the IETF language tag of the locale being used by the provided +/// [collator]. This can be used to determine the default system locale, or to +/// determine if a requested locale was successfully loaded. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#resolved-locale +Expression resolvedLocale(Expression collator) => + Expression.fromJson(['resolved-locale', collator]); + +/// Returns an array of substrings formed by splitting an input string by a +/// separator string. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#split +Expression> split_( + Expression input, + Expression separator, +) => Expression.fromJson(['split', input, separator]); + +/// Returns a string formed by concatenating the elements of the input array, +/// inserting a separator between each element. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#join +Expression join(List input, Object separator) { + assert( + input.every((element) => element is Expression || element is String), + 'Input must be a list of Expressions or Strings', + ); + assert( + separator is Expression || separator is String, + 'Separator must be an Expression or a String', + ); + return Expression.fromJson(['join', input, separator]); +} diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/types.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/types.dart new file mode 100644 index 000000000..49814f164 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/types.dart @@ -0,0 +1,288 @@ +part of 'expressions.dart'; + +/// Provides a literal array or object value. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#literal +Expression literal(T value) => + Expression.fromJson(['literal', value]); + +/// Asserts that the input is an array (optionally with a specific item type +/// and length). If, when the input expression is evaluated, it is not of the +/// asserted type or length, then this assertion will cause the whole +/// expression to be aborted. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#array +Expression> array( + Expression> expression, { + ArrayType? type, + int? length, +}) => Expression.fromJson(['array', ?type?.name, ?length, expression]); + +/// The asserted items type in an [array] expression. +enum ArrayType { + /// An array of numbers. + double('number'), + + /// An array of strings. + string('string'), + + /// An array of booleans. + bool('boolean'); + + const ArrayType(this.name); + + /// The name of the array type. + final String name; +} + +/// Returns a string describing the type of the given value. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#typeof +Expression typeOf(Expression expression) => + Expression.fromJson(['typeof', expression]); + +/// Asserts that the input value is a string. If multiple values are provided, +/// each one is evaluated in order until a string is obtained. If none of the +/// inputs are strings, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#string +Expression string(List inputs) { + return Expression.fromJson(['string', ...inputs]); +} + +/// Asserts that the input value is a number. If multiple values are provided, +/// each one is evaluated in order until a number is obtained. If none of the +/// inputs are numbers, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#number +Expression number(List inputs) { + return Expression.fromJson(['number', ...inputs]); +} + +/// Asserts that the input value is a boolean. If multiple values are provided, +/// each one is evaluated in order until a boolean is obtained. If none of the +/// inputs are booleans, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#boolean +Expression boolean(List inputs) { + return Expression.fromJson(['boolean', ...inputs]); +} + +/// Asserts that the input value is a [Map]. If multiple values are provided, +/// each one is evaluated in order until an object is obtained. If none of the +/// inputs are objects, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#object +Expression> object(List inputs) { + return Expression.fromJson(['object', ...inputs]); +} + +/// Returns a `collator` for use in locale-dependent comparison operations. Use +/// `resolved-locale` to test the results of locale fallback behavior. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#collator +Expression collator({ + Object? caseSensitive, + Object? diacriticSensitive, + Object? locale, +}) { + assert( + caseSensitive == null || + caseSensitive is bool || + caseSensitive is Expression, + 'caseSensitive must be a bool or an Expression', + ); + assert( + diacriticSensitive == null || + diacriticSensitive is bool || + diacriticSensitive is Expression, + 'diacriticSensitive must be a bool or an Expression', + ); + assert( + locale == null || locale is String || locale is Expression, + 'locale must be a String or an Expression', + ); + return Expression.fromJson([ + 'collator', + { + 'case-sensitive': ?caseSensitive, + 'diacritic-sensitive': ?diacriticSensitive, + if (locale != null) + 'locale': switch (locale) { + final Locale locale => locale.toString(), + final Expression expr => expr.json, + _ => throw StateError('Unreachable'), + }, + }, + ]); +} + +/// Returns a `formatted` string for displaying mixed-format text in the +/// [SymbolStyleLayer.textField] property. The input may contain a string +/// literal or [Expression], including an [image]. Strings may be +/// followed by a style override object. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#format +Expression format(Map overrides) => + Expression.fromJson([ + 'format', + for (final entry in overrides.entries) ...[ + switch (entry.key) { + final String str => str, + final Expression expr => expr.json, + _ => throw ArgumentError.value( + entry.key, + 'overrides', + 'Keys must be either String or Expression', + ), + }, + entry.value.json, + ], + ]); + +/// An object defining style overrides for a section of text in a [format] +/// expression. +extension type StyleOverrides.fromJson(Map json) { + /// Create a new [StyleOverrides] object. + /// + /// - [textFont]: Overrides the font stack specified by the root + /// layout property. + /// - [textColor]: Overrides the color specified by the root paint + /// property. + /// - [fontScale]: Applies a scaling factor on text-size as specified + /// by the root layout property. + /// - [verticalAlign]: Aligns a vertical text section or image in relation to + /// the row it belongs to. Refer to the design proposal for more details. + StyleOverrides({ + String? textFont, + Color? textColor, + double? fontScale, + VerticalAlign? verticalAlign, + }) : json = { + 'text-font': ?textFont, + 'text-color': ?textColor?.toHexString(), + 'font-scale': ?fontScale, + 'vertical-align': ?verticalAlign?.name, + }; +} + +/// The vertical alignment options for text sections in a [format] expression. +enum VerticalAlign { + /// Align the bottom of this section with the bottom of other sections. + /// + /// ![Preview](https://github.com/user-attachments/assets/0474a2fd-a4b2-417c-9187-7a13a28695bc) + bottom, + + /// Align the center of this section with the center of other sections. + /// + /// ![Preview](https://github.com/user-attachments/assets/92237455-be6d-4c5d-b8f6-8127effc1950) + center, + + /// Align the top of this section with the top of other sections. + /// + /// ![Preview](https://github.com/user-attachments/assets/45dccb28-d977-4abb-a006-4ea9792b7c53) + top, +} + +/// Returns an image type for use in `icon-image`, `*-pattern` entries and as a +/// section in the [format] expression. If set, the image argument will check +/// that the requested image exists in the style and will return either the +/// resolved image name or `null`, depending on whether or not the image is +/// currently in the style. This validation process is synchronous and requires +/// the image to have been added to the style before requesting it in the image +/// argument. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#image +Expression image(Object input) => Expression.fromJson([ + 'image', + switch (input) { + final String imageName => imageName, + final Expression expression => expression, + _ => throw ArgumentError.value( + input, + 'input', + 'Input must be either a String or an Expression', + ), + }, +]); + +/// Converts the input number into a string representation using the provided +/// format_options. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#number-format +Expression numberFormat({ + required Object input, + String? locale, + String? currency, + String? unit, + int? minFractionDigits, + int? maxFractionDigits, +}) => Expression.fromJson([ + 'number-format', + switch (input) { + final double value => value, + final Expression expr => expr, + _ => throw ArgumentError.value( + input, + 'input', + 'Input must be either a num or an Expression', + ), + }, + { + 'locale': ?locale, + 'currency': ?currency, + 'unit': ?unit, + 'min-fraction-digits': ?minFractionDigits, + 'max-fraction-digits': ?maxFractionDigits, + }, +]); + +/// Converts the input value to a string. If the input is null, the result is +/// "". If the input is a boolean, the result is "true" or "false". If the input +/// is a number, it is converted to a string as specified by the +/// ["NumberToString" algorithm](https://tc39.es/ecma262/#sec-tostring-applied-to-the-number-type) +/// of the ECMAScript Language Specification. If the +/// input is a color, it is converted to a string of the form `"rgba(r,g,b,a)"`, +/// where `r`, `g`, and `b` are numerals ranging from 0 to 255, and a ranges +/// from 0 to 1. Otherwise, the input is converted to a string in the format +/// specified by the [JSON.stringify](https://tc39.es/ecma262/#sec-json.stringify) function +/// of the ECMAScript Language Specification. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#to-string +Expression toString(Expression input) => + Expression.fromJson(['to-string', input]); + +/// Converts the input value to a number, if possible. If the input is null or +/// false, the result is 0. If the input is true, the result is 1. If the input +/// is a string, it is converted to a number as specified by the "ToNumber +/// Applied to the String Type" algorithm of the ECMAScript Language +/// Specification. If multiple values are provided, each one is evaluated in +/// order until the first successful conversion is obtained. If none of the +/// inputs can be converted, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#to-number +Expression toNumber(Object input) => switch (input) { + final List list => Expression.fromJson(['to-number', ...list]), + final Expression expr => Expression.fromJson(['to-number', expr]), + _ => throw ArgumentError.value( + input, + 'input', + 'Input must be either an Expression or a List', + ), +}; + +/// Converts the input value to a boolean. The result is false when the input is +/// an empty string, 0, false, null, or NaN; otherwise it is true. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#to-boolean +Expression toBoolean(Expression input) => + Expression.fromJson(['to-boolean', input]); + +/// Converts the input value to a color. If multiple values are provided, each +/// one is evaluated in order until the first successful conversion is obtained. +/// If none of the inputs can be converted, the expression is an error. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#to-color +Expression toColor(List inputs) { + return Expression.fromJson(['to-color', ...inputs]); +} diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/variable_binding.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/variable_binding.dart new file mode 100644 index 000000000..77354aa00 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/variable_binding.dart @@ -0,0 +1,27 @@ +part of 'expressions.dart'; + +/// Binds expressions to named variables, which can then be referenced in the +/// result expression using [var_]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#let +Expression let({ + required Map variables, + required Expression expression, +}) => Expression.fromJson([ + 'let', + ...variables.entries.expand((e) => [e.key, e.value]), + expression, +]); + +/// References variable bound using [let]. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#var +Expression var_(Object variable) => + Expression.fromJson([ + 'var', + switch (variable) { + final String name => name, + final Expression expression => expression, + _ => throw StateError('Unreachable'), + }, + ]); diff --git a/packages/maplibre_platform_interface/lib/src/style/expressions/zoom.dart b/packages/maplibre_platform_interface/lib/src/style/expressions/zoom.dart new file mode 100644 index 000000000..25a3bf131 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/expressions/zoom.dart @@ -0,0 +1,8 @@ +part of 'expressions.dart'; + +/// Gets the current zoom level. Note that in style layout and paint properties, +/// `["zoom"]` may only appear as the input to a top-level [step] or +/// [interpolate] expression. +/// +/// https://maplibre.org/maplibre-style-spec/expressions/#zoom +Expression zoom_() => const Expression.fromJson(['zoom']); diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/background_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/background_style_layer.dart index 37837529d..8a4601ec6 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/background_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/background_style_layer.dart @@ -6,31 +6,55 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class BackgroundStyleLayer extends StyleLayer { - /// Create a new [BackgroundStyleLayer]. - const BackgroundStyleLayer({ - required super.id, - this.visible = true, - this.color = const Color(0x00000000), - this.opacity = 1, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - }); - - /// Whether this layer is displayed. - /// - /// Defaults to visible. - final bool visible; +abstract interface class BackgroundStyleLayer implements StyleLayer { + /// Create a platform-specific implementation of [BackgroundStyleLayer]. + factory BackgroundStyleLayer({ + required String id, + bool visible = StyleLayer.defaultVisible, + PropertyValue color = defaultColor, + PropertyValue? pattern, + PropertyValue opacity = defaultOpacity, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + }) => MapLibrePlatform.instance.createBackgroundStyleLayer( + id: id, + visible: visible, + color: color, + pattern: pattern, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); /// The color with which the background will be drawn. /// - /// Defaults to #000000 (black). - final Color color; + /// Paint property. Optional color. Defaults to `#000000` (black). + /// Disabled by [pattern]. Supports [interpolate] expressions. Transitionable. + PropertyValue get color; + + set color(PropertyValue property); + + /// Default value of [color]. + static const defaultColor = PropertyValue.value(Color(0xFF000000)); + + /// Name of image in sprite to use for drawing an image background. For + /// seamless patterns, image width and height must be a factor of two + /// (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be + /// evaluated only at integer zoom levels. + /// + /// Paint property. Optional resolvedImage. Transitionable. + PropertyValue? get pattern; + + set pattern(PropertyValue? property); /// The opacity at which the background will be drawn. /// - /// Defaults to 1. - final double opacity; + /// Paint property. Optional number in range `0,1`. Defaults to `1.0`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value of [opacity]. + static const defaultOpacity = PropertyValue.value(1); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/circle_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/circle_style_layer.dart index b630eeb04..17aa6df13 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/circle_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/circle_style_layer.dart @@ -6,18 +6,159 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class CircleStyleLayer extends StyleLayerWithSource { - /// Default constructor for a [CircleStyleLayer] instance. - const CircleStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - - // TODO add properties +abstract interface class CircleStyleLayer + implements + StyleLayerWithVectorSource, + StyleLayerWithSortKey, + StyleLayerWithTranslate { + /// Create a platform-specific implementation of [CircleStyleLayer]. + factory CircleStyleLayer({ + required String id, + required String sourceId, + String? sourceLayerId, + Expression? filter, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + PropertyValue translate = StyleLayerWithTranslate.defaultTranslate, + PropertyValue translateAnchor = + StyleLayerWithTranslate.defaultTranslateAnchor, + PropertyValue? sortKey, + PropertyValue radius = defaultRadius, + PropertyValue color = defaultColor, + PropertyValue blur = defaultBlur, + PropertyValue opacity = defaultOpacity, + PropertyValue pitchScale = defaultPitchScale, + PropertyValue pitchAlignment = defaultPitchAlignment, + PropertyValue strokeWidth = defaultStrokeWidth, + PropertyValue strokeColor = defaultStrokeColor, + PropertyValue strokeOpacity = defaultStrokeOpacity, + }) => MapLibrePlatform.instance.createCircleStyleLayer( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + translate: translate, + translateAnchor: translateAnchor, + sortKey: sortKey, + radius: radius, + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + /// The radius of the circle. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `5`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get radius; + + set radius(PropertyValue property); + + /// The default value of [radius]. + static const defaultRadius = PropertyValue.value(5); + + /// The fill color of the circle. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"` (black). + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get color; + + set color(PropertyValue property); + + /// The default value of [color]. + static const defaultColor = PropertyValue.value(Color(0xFF000000)); + + /// Amount to blur the circle. 1 blurs the circle such that only the + /// centerpoint is full opacity. + /// + /// Paint property. Optional number. Defaults to `0`. Supports [featureState] + /// and [interpolate] expressions. Transitionable. + PropertyValue get blur; + + set blur(PropertyValue property); + + /// The default value of [blur]. + static const defaultBlur = PropertyValue.value(0); + + /// The opacity at which the circle will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// The default value of [opacity]. + static const defaultOpacity = PropertyValue.value(1); + + /// Controls the scaling behavior of the circle when the map is pitched. + /// + /// Paint property. Optional enum. Defaults to [ReferenceSpace.map]. + PropertyValue get pitchScale; + + set pitchScale(PropertyValue property); + + /// The default value of [pitchScale]. + static const defaultPitchScale = PropertyValue.value( + ReferenceSpace.map, + ); + + /// Orientation of circle when map is pitched. + /// + /// Paint property. Optional enum. Defaults to [ReferenceSpace.viewport]. + PropertyValue get pitchAlignment; + + set pitchAlignment(PropertyValue property); + + /// The default value of [pitchAlignment]. + static const defaultPitchAlignment = PropertyValue.value( + ReferenceSpace.viewport, + ); + + /// The width of the circle's stroke. Strokes are placed outside of + /// the [radius]. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get strokeWidth; + + set strokeWidth(PropertyValue property); + + /// The default value of [strokeWidth]. + static const defaultStrokeWidth = PropertyValue.value(0); + + /// The stroke color of the circle. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"`. Supports + /// [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get strokeColor; + + set strokeColor(PropertyValue property); + + /// The default value of [strokeColor]. + static const defaultStrokeColor = PropertyValue.value( + Color(0xFF000000), + ); + + /// The opacity of the circle's stroke. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get strokeOpacity; + + set strokeOpacity(PropertyValue property); + + /// The default value of [strokeOpacity]. + static const defaultStrokeOpacity = PropertyValue.value(1); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/color_relief_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/color_relief_style_layer.dart new file mode 100644 index 000000000..656654ceb --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/layers/color_relief_style_layer.dart @@ -0,0 +1,58 @@ +part of 'style_layer.dart'; + +/// A layer that contains markers. +/// +/// https://maplibre.org/maplibre-style-spec/layers/#color-relief +/// +/// {@category Style} +/// {@subCategory Style Layers} +abstract interface class ColorReliefStyleLayer implements StyleLayerWithSource { + /// Default constructor for a [ColorReliefStyleLayer] instance. + factory ColorReliefStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + PropertyValue? color, + PropertyValue opacity = ColorReliefStyleLayer.defaultOpacity, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + }) => MapLibrePlatform.instance.createColorReliefStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + color: color, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// The opacity at which the color-relief will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value for [opacity]. + static const defaultOpacity = PropertyValue.value(1); + + /// Defines the color of each pixel based on its elevation. Should be an + /// [Expression] that uses `["elevation"]` as input. For example: + /// ```json + /// "color-relief-color": [ + /// "interpolate", + /// ["linear"], + /// ["elevation"], + /// 0, + /// "black", + /// 8849, + /// "white" + /// ] + /// ``` + /// + /// Paint property. Optional [Color]. Supports [interpolate] expressions. + PropertyValue? get color; + + set color(PropertyValue? property); +} diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/fill_extrusion_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/fill_extrusion_style_layer.dart index 672497945..06b0cb8a2 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/fill_extrusion_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/fill_extrusion_style_layer.dart @@ -6,18 +6,124 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class FillExtrusionStyleLayer extends StyleLayerWithSource { +abstract interface class FillExtrusionStyleLayer + implements StyleLayerWithVectorSource, StyleLayerWithTranslate { /// Default constructor for a [FillExtrusionStyleLayer] instance. - const FillExtrusionStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - - // TODO add properties + factory FillExtrusionStyleLayer({ + required String id, + required String sourceId, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + bool visible = StyleLayer.defaultVisible, + Expression? filter, + String? sourceLayerId, + PropertyValue opacity = defaultOpacity, + PropertyValue color = defaultColor, + PropertyValue? pattern, + PropertyValue height = defaultHeight, + PropertyValue base = defaultBase, + PropertyValue verticalGradient = defaultVerticalGradient, + PropertyValue translate = StyleLayerWithTranslate.defaultTranslate, + PropertyValue translateAnchor = + StyleLayerWithTranslate.defaultTranslateAnchor, + }) => MapLibrePlatform.instance.createFillExtrusionStyleLayer( + id: id, + sourceId: sourceId, + minZoom: minZoom, + maxZoom: maxZoom, + visible: visible, + filter: filter, + sourceLayerId: sourceLayerId, + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + /// The opacity of the entire fill extrusion layer. This is rendered on a + /// per-layer, not per-feature, basis, and data-driven styling is not + /// available. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports interpolate expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value for [opacity]. + static const PropertyValue defaultOpacity = + PropertyValue.value(1); + + /// The base color of the extruded fill. The extrusion's surfaces will be + /// shaded differently based on this color in combination with the root light + /// settings. If this color is specified as `rgba` with an alpha component, + /// the alpha component will be ignored; use [opacity] to set layer + /// opacity. + /// + /// Paint property. Optional color. Defaults to `"#000000"` (black). + /// Disabled by [pattern]. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get color; + + set color(PropertyValue property); + + /// Default value for [color]. + static const PropertyValue defaultColor = PropertyValue.value( + Color(0xFF000000), + ); + + /// Name of image in sprite to use for drawing images on extruded fills. For + /// seamless patterns, image width and height must be a factor of two + /// (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be + /// evaluated only at integer zoom levels. + /// + /// Paint property. Optional resolvedImage. Transitionable. + PropertyValue? get pattern; + + set pattern(PropertyValue? property); + + /// The height with which to extrude this layer. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in meters. + /// Defaults to `0`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get height; + + set height(PropertyValue property); + + /// Default value for [height]. + static const PropertyValue defaultHeight = + PropertyValue.value(0); + + /// The height with which to extrude the base of this layer. Must be less + /// than or equal to [height]. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in meters. + /// Defaults to `0`. Requires [height]. Supports [featureState] and + /// [interpolate] expressions. Transitionable. + PropertyValue get base; + + set base(PropertyValue property); + + /// Default value for [base]. + static const PropertyValue defaultBase = PropertyValue.value( + 0, + ); + + /// Whether to apply a vertical gradient to the sides of a + /// [FillExtrusionStyleLayer]. If true, sides will be shaded slightly darker + /// farther down. + /// + /// Paint property. Optional boolean. Defaults to `true`. + PropertyValue get verticalGradient; + + set verticalGradient(PropertyValue property); + + /// Default value for [verticalGradient]. + static const PropertyValue defaultVerticalGradient = + PropertyValue.value(true); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/fill_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/fill_style_layer.dart index efc72ccff..19680741c 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/fill_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/fill_style_layer.dart @@ -6,17 +6,106 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class FillStyleLayer extends StyleLayerWithSource { - /// Default constructor for a [FillStyleLayer] instance. - const FillStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - // TODO add properties +abstract interface class FillStyleLayer + implements + StyleLayerWithVectorSource, + StyleLayerWithSortKey, + StyleLayerWithTranslate { + /// Create a platform-specific implementation of [FillStyleLayer]. + factory FillStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + String? sourceLayerId, + Expression? filter, + PropertyValue? sortKey, + PropertyValue translate = StyleLayerWithTranslate.defaultTranslate, + PropertyValue translateAnchor = + StyleLayerWithTranslate.defaultTranslateAnchor, + PropertyValue antialias = defaultAntialias, + PropertyValue color = defaultColor, + PropertyValue opacity = defaultOpacity, + PropertyValue outlineColor = defaultOutlineColor, + PropertyValue? pattern, + }) => MapLibrePlatform.instance.createFillStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + ); + + /// Whether or not the fill should be antialiased. + /// + /// Paint property. Optional boolean. Defaults to `true`. + PropertyValue get antialias; + + set antialias(PropertyValue property); + + /// Default value of [antialias]. + static const defaultAntialias = PropertyValue.value(true); + + /// The color of the filled part of this layer. This color can be specified + /// as `rgba` with an alpha component and the color's opacity will not affect + /// the opacity of the 1px stroke, if it is used. + /// + /// Paint property. Optional color. Defaults to `#000000` (black). + /// Disabled by [pattern]. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get color; + + set color(PropertyValue property); + + /// Default value of [color]. + static const defaultColor = PropertyValue.value(Color(0xFF000000)); + + /// The opacity of the entire fill layer. In contrast to the fill-color, this + /// value will also affect the 1px stroke around the fill, if the stroke is + /// used. + /// + /// Paint property. Optional number in range `0,1`. Defaults to `1.0`. + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value of [opacity]. + static const defaultOpacity = PropertyValue.value(1); + + /// The outline color of the fill. Matches the value of [color] if + /// unspecified. + /// + /// Paint property. Optional color. Disabled by [pattern]. Requires + /// [antialias] to be `true`. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get outlineColor; + + set outlineColor(PropertyValue property); + + /// Default value of [outlineColor]. + static const defaultOutlineColor = PropertyValue.value( + Color(0xFF000000), + ); + + /// Name of image in sprite to use for drawing image fills. For seamless + /// patterns, image width and height must be a factor of two + /// (2, 4, 8, ..., 512). Note that zoom-dependent expressions will be + /// evaluated only at integer zoom levels. + /// + /// Paint property. Optional resolvedImage. Transitionable. + PropertyValue? get pattern; + + set pattern(PropertyValue? property); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/heatmap_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/heatmap_style_layer.dart index 12dbf2f14..654b93161 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/heatmap_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/heatmap_style_layer.dart @@ -6,18 +6,105 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class HeatmapStyleLayer extends StyleLayerWithSource { +abstract interface class HeatmapStyleLayer + implements StyleLayerWithVectorSource { /// Default constructor for a [HeatmapStyleLayer] instance. - const HeatmapStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - - // TODO add properties + factory HeatmapStyleLayer({ + required String id, + required String sourceId, + String? sourceLayerId, + Expression? filter, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + PropertyValue radius = defaultRadius, + PropertyValue weight = defaultWeight, + PropertyValue intensity = defaultIntensity, + PropertyValue? color, + PropertyValue opacity = defaultOpacity, + }) => MapLibrePlatform.instance.createHeatmapStyleLayer( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + /// Radius of influence of one heatmap point in pixels. Increasing the value + /// makes the heatmap smoother, but less detailed. + /// + /// Paint property. Optional number in range `[1, ∞)`. Units in pixels. + /// Defaults to `30`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get radius; + + set radius(PropertyValue property); + + /// Default value of [radius]. + static const defaultRadius = PropertyValue.value(30); + + /// A measure of how much an individual point contributes to the heatmap. + /// A value of 10 would be equivalent to having 10 points of weight 1 in the + /// same spot. Especially useful when combined with clustering. + /// + /// Paint property. Optional number in range `[0, ∞)`. Defaults to `1`. + /// Supports [featureState] and [interpolate] expressions. + PropertyValue get weight; + + set weight(PropertyValue property); + + /// Default value of [weight]. + static const defaultWeight = PropertyValue.value(1); + + /// Similar to [weight] but controls the intensity of the heatmap globally. + /// Primarily used for adjusting the heatmap based on zoom level. + /// + /// Paint property. Optional number in range `[0, ∞)`. Defaults to `1`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get intensity; + + set intensity(PropertyValue property); + + /// Default value of [intensity]. + static const defaultIntensity = PropertyValue.value(1); + + /// Defines the color of each pixel based on its density value in a heatmap. + /// Should be an expression that uses `["heatmap-density"]` as input. + /// + /// Paint property. Optional color. Defaults to: + /// ```json + /// [ + /// "interpolate", + /// ["linear"], + /// ["heatmap-density"], + /// 0, "rgba(0, 0, 255, 0)", + /// 0.1,"royalblue", + /// 0.3,"cyan", + /// 0.5,"lime", + /// 0.7,"yellow", + /// 1,"red" + /// ] + /// ``` + /// Supports [interpolate] expressions. + PropertyValue? get color; + + set color(PropertyValue? property); + + /// The global opacity at which the heatmap layer will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value of [opacity]. + static const defaultOpacity = PropertyValue.value(1); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/hillshade_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/hillshade_style_layer.dart index 7340b8703..2d17c4419 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/hillshade_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/hillshade_style_layer.dart @@ -6,16 +6,191 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class HillshadeStyleLayer extends StyleLayerWithSource { +abstract interface class HillshadeStyleLayer implements StyleLayerWithSource { /// Default constructor for a [HillshadeStyleLayer] instance. - const HillshadeStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - }); - - // TODO add properties + factory HillshadeStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + PropertyValue illuminationDirection = + defaultIlluminationDirection, + PropertyValue illuminationAltitude = + defaultIlluminationAltitude, + PropertyValue illuminationAnchor = + defaultIlluminationAnchor, + PropertyValue exaggeration = defaultExaggeration, + PropertyValue shadowColor = defaultShadowColor, + PropertyValue highlightColor = defaultHighlightColor, + PropertyValue accentColor = defaultAccentColor, + PropertyValue method = defaultMethod, + }) => MapLibrePlatform.instance.createHillshadeStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + illuminationDirection: illuminationDirection, + illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + method: method, + ); + + /// The direction of the light source(s) used to generate the hillshading with + /// `0` as the top of the viewport if [illuminationAnchor] is set to + /// [IlluminationAnchor.viewport] and due north if [illuminationAnchor] is set + /// to [IlluminationAnchor.map]. + /// + /// {@template multiple-light-sources-notice} + /// Only when [method] is set to [HillshadeMethod.multidirectional] can you + /// specify multiple light sources. + /// {@endtemplate} + /// + /// Paint property. Optional [NumberArray] with value(s) in range `[0, 359]`. + /// Defaults to `335`. Supports [interpolate] expressions. + PropertyValue get illuminationDirection; + + set illuminationDirection(PropertyValue property); + + /// Default value of [illuminationDirection]. + static const defaultIlluminationDirection = PropertyValue.value( + NumberArray.number(335), + ); + + /// The altitude of the light source(s) used to generate the hillshading with + /// `0` as sunset and `90` as noon. + /// + /// {@macro multiple-light-sources-notice} + /// + /// Paint property. Optional [NumberArray] with value(s) in range `[0, 90]`. + /// Defaults to `45`. Supports interpolate expressions. + PropertyValue get illuminationAltitude; + + set illuminationAltitude(PropertyValue property); + + /// Default value of [illuminationAltitude]. + static const defaultIlluminationAltitude = PropertyValue.value( + NumberArray.number(45), + ); + + /// {@macro illumination-anchor} + /// + /// Paint property. Optional enum. Defaults to [IlluminationAnchor.viewport]. + PropertyValue get illuminationAnchor; + + set illuminationAnchor(PropertyValue property); + + /// Default value of [illuminationAnchor]. + static const defaultIlluminationAnchor = + PropertyValue.value(IlluminationAnchor.viewport); + + /// Intensity of the hillshade + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `0.5`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get exaggeration; + + set exaggeration(PropertyValue property); + + /// Default value of [exaggeration]. + static const defaultExaggeration = PropertyValue.value(0.5); + + /// The shading color of areas that face away from the light source(s). + /// + /// {@macro multiple-light-sources-notice} + /// + /// Paint property. Optional [ColorArray]. Defaults to `"#000000"`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get shadowColor; + + set shadowColor(PropertyValue property); + + /// Default value of [shadowColor]. + static const defaultShadowColor = PropertyValue.value( + Color(0xFF000000), + ); + + /// The shading color of areas that faces towards the light source(s). + /// + /// {@macro multiple-light-sources-notice} + /// + /// Paint property. Optional [ColorArray]. Defaults to `"#FFFFFF"`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get highlightColor; + + set highlightColor(PropertyValue property); + + /// Default value of [highlightColor]. + static const defaultHighlightColor = PropertyValue.value( + Color(0xFFFFFFFF), + ); + + /// The shading color used to accentuate rugged terrain like sharp cliffs and + /// gorges. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get accentColor; + + set accentColor(PropertyValue property); + + /// Default value of [accentColor]. + static const defaultAccentColor = PropertyValue.value( + Color(0xFF000000), + ); + + /// {@macro hillshade-method} + /// + /// Paint property. Optional enum. Defaults to [HillshadeMethod.standard]. + PropertyValue get method; + + set method(PropertyValue property); + + /// Default value of [method]. + static const defaultMethod = PropertyValue.value( + HillshadeMethod.standard, + ); +} + +/// {@template illumination-anchor} +/// Direction of light source when map is rotated. +/// {@endtemplate} +enum IlluminationAnchor { + /// The hillshade illumination is relative to the north direction. + map, + + /// The hillshade illumination is relative to the top of the viewport. + viewport, +} + +/// {@template hillshade-method} +/// The hillshade algorithm to use. +/// ![Preview](https://maplibre.org/maplibre-style-spec/assets/hillshade_methods.png) +/// {@endtemplate} +enum HillshadeMethod { + /// The legacy hillshade method. + standard, + + /// Basic hillshade. Uses a simple physics model where the reflected light + /// intensity is proportional to the cosine of the angle between the incident + /// light and the surface normal. Similar to GDAL's `gdaldem` default + /// algorithm. + basic, + + /// Hillshade algorithm whose intensity scales with slope. Similar to GDAL's + /// `gdaldem` with `-combined` option. + combined, + + /// Hillshade algorithm which tries to minimize effects on other map features + /// beneath. Similar to GDAL's `gdaldem` with `-igor` option. + igor, + + /// Hillshade with multiple illumination directions. Uses the basic hillshade + /// model with multiple independent light sources. + multidirectional, } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/layout_paint_helpers.dart b/packages/maplibre_platform_interface/lib/src/style/layers/layout_paint_helpers.dart new file mode 100644 index 000000000..0ff0d9b41 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/layers/layout_paint_helpers.dart @@ -0,0 +1,370 @@ +part of 'style_layer.dart'; + +/// Helper functions to create layout and paint properties for different layer +/// types. +Map createBackgroundLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [BackgroundStyleLayer]. +Map createBackgroundPaint({ + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, +}) => { + 'background-color': color.toJson(), + if (pattern case final pattern?) 'background-pattern': pattern.toJson(), + 'background-opacity': opacity.toJson(), +}; + +/// Creates a map of layout properties for a [CircleStyleLayer]. +Map createCircleLayout({ + required bool visible, + required PropertyValue? sortKey, +}) => { + if (sortKey case final sortKey?) 'circle-sort-key': sortKey.toJson(), + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [CircleStyleLayer]. +Map createCirclePaint({ + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, +}) => { + 'circle-radius': radius.toJson(), + 'circle-color': color.toJson(), + 'circle-blur': blur.toJson(), + 'circle-opacity': opacity.toJson(), + 'circle-translate': translate.toJson(), + 'circle-translate-anchor': translateAnchor.toJson(), + 'circle-pitch-scale': pitchScale.toJson(), + 'circle-pitch-alignment': pitchAlignment.toJson(), + 'circle-stroke-width': strokeWidth.toJson(), + 'circle-stroke-color': strokeColor.toJson(), + 'circle-stroke-opacity': strokeOpacity.toJson(), +}; + +/// Creates a map of layout properties for a [ColorReliefStyleLayer]. +Map createColorReliefLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [ColorReliefStyleLayer]. +Map createColorReliefPaint({ + required PropertyValue? color, + required PropertyValue opacity, +}) => { + if (color case final color?) 'color-relief-color': color.toJson(), + 'color-relief-opacity': opacity.toJson(), +}; + +/// Creates a map of layout properties for a [FillExtrusionStyleLayer]. +Map createFillExtrusionLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [FillExtrusionStyleLayer]. +Map createFillExtrusionPaint({ + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, +}) => { + 'fill-extrusion-opacity': opacity.toJson(), + 'fill-extrusion-color': color.toJson(), + 'fill-extrusion-translate': translate.toJson(), + 'fill-extrusion-translate-anchor': translateAnchor.toJson(), + if (pattern case final pattern?) 'fill-extrusion-pattern': pattern.toJson(), + 'fill-extrusion-height': height.toJson(), + 'fill-extrusion-base': base.toJson(), + 'fill-extrusion-vertical-gradient': verticalGradient.toJson(), +}; + +/// Creates a map of layout properties for a [FillStyleLayer]. +Map createFillLayout({ + required bool visible, + required PropertyValue? sortKey, +}) => { + if (sortKey case final sortKey?) 'fill-sort-key': sortKey.toJson(), + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [FillStyleLayer]. +Map createFillPaint({ + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? pattern, +}) => { + 'fill-antialias': antialias.toJson(), + 'fill-color': color.toJson(), + 'fill-opacity': opacity.toJson(), + 'fill-outline-color': outlineColor.toJson(), + 'fill-translate': translate.toJson(), + 'fill-translate-anchor': translateAnchor.toJson(), + if (pattern case final pattern?) 'fill-pattern': pattern.toJson(), +}; + +/// Creates a map of layout properties for a [HeatmapStyleLayer]. +Map createHeatmapLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [HeatmapStyleLayer]. +Map createHeatmapPaint({ + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, +}) => { + 'heatmap-radius': radius.toJson(), + 'heatmap-weight': weight.toJson(), + 'heatmap-intensity': intensity.toJson(), + if (color != null) 'heatmap-color': color.toJson(), + 'heatmap-opacity': opacity.toJson(), +}; + +/// Creates a map of layout properties for a [HillshadeStyleLayer]. +Map createHillshadeLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [HillshadeStyleLayer]. +Map createHillshadePaint({ + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, +}) => { + 'hillshade-illumination-direction': illuminationDirection.toJson(), + 'hillshade-illumination-altitude': illuminationAltitude.toJson(), + 'hillshade-illumination-anchor': illuminationAnchor.toJson(), + 'hillshade-exaggeration': exaggeration.toJson(), + 'hillshade-shadow-color': shadowColor.toJson(), + 'hillshade-highlight-color': highlightColor.toJson(), + 'hillshade-accent-color': accentColor.toJson(), + 'hillshade-method': method.toJson(), +}; + +/// Creates a map of layout properties for a [LineStyleLayer]. +Map createLineLayout({ + required bool visible, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue? sortKey, +}) => { + 'line-cap': cap.toJson(), + 'line-join': join.toJson(), + 'line-miter-limit': miterLimit.toJson(), + 'line-round-limit': roundLimit.toJson(), + if (sortKey case final sortKey?) 'line-sort-key': sortKey.toJson(), + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [LineStyleLayer]. +Map createLinePaint({ + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, +}) => { + 'line-opacity': opacity.toJson(), + 'line-color': color.toJson(), + 'line-translate': translate.toJson(), + 'line-translate-anchor': translateAnchor.toJson(), + 'line-width': width.toJson(), + 'line-gap-width': gapWidth.toJson(), + 'line-offset': offset.toJson(), + 'line-blur': blur.toJson(), + if (dashArray case final dashArray?) 'line-dasharray': dashArray.toJson(), + if (pattern case final pattern?) 'line-pattern': pattern.toJson(), + if (gradient case final gradient?) 'line-gradient': gradient.toJson(), +}; + +/// Creates a map of layout properties for a [RasterStyleLayer]. +Map createRasterLayout({required bool visible}) => { + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [RasterStyleLayer]. +Map createRasterPaint({ + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, +}) => { + 'raster-opacity': opacity.toJson(), + 'raster-hue-rotate': hueRotate.toJson(), + 'raster-brightness-min': brightnessMin.toJson(), + 'raster-brightness-max': brightnessMax.toJson(), + 'raster-saturation': saturation.toJson(), + 'raster-contrast': contrast.toJson(), + 'raster-resampling': resampling.toJson(), + 'raster-fade-duration': fadeDuration.toJson(), +}; + +/// Creates a map of layout properties for a [SymbolStyleLayer]. +Map createSymbolLayout({ + required bool visible, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? + textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, +}) => { + 'symbol-placement': placement.toJson(), + 'symbol-spacing': spacing.toJson(), + 'symbol-avoid-edges': avoidEdges.toJson(), + if (sortKey case final sortKey?) 'symbol-sort-key': sortKey.toJson(), + 'symbol-z-order': zOrder.toJson(), + 'icon-allow-overlap': iconAllowOverlap.toJson(), + 'icon-overlap': iconOverlap.toJson(), + 'icon-ignore-placement': iconIgnorePlacement.toJson(), + 'icon-optional': iconOptional.toJson(), + 'icon-rotation-alignment': iconRotationAlignment.toJson(), + 'icon-size': iconSize.toJson(), + 'icon-text-fit': iconTextFit.toJson(), + 'icon-text-fit-padding': iconTextFitPadding.toJson(), + if (iconImage case final iconImage?) 'icon-image': iconImage.toJson(), + 'icon-rotate': iconRotate.toJson(), + 'icon-padding': iconPadding.toJson(), + 'icon-keep-upright': iconKeepUpright.toJson(), + 'icon-offset': iconOffset.toJson(), + 'icon-anchor': iconAnchor.toJson(), + 'icon-pitch-alignment': iconPitchAlignment.toJson(), + 'text-pitch-alignment': textPitchAlignment.toJson(), + 'text-rotation-alignment': textRotationAlignment.toJson(), + 'text-field': textField.toJson(), + 'text-font': textFont.toJson(), + 'text-size': textSize.toJson(), + 'text-max-width': textMaxWidth.toJson(), + 'text-line-height': textLineHeight.toJson(), + 'text-letter-spacing': textLetterSpacing.toJson(), + 'text-justify': textJustify.toJson(), + 'text-radial-offset': textRadialOffset.toJson(), + if (textVariableAnchor case final textVariableAnchor?) + 'text-variable-anchor': textVariableAnchor.toJson(), + if (textVariableAnchorOffset case final textVariableAnchorOffset?) + 'text-variable-anchor-offset': textVariableAnchorOffset.toJson(), + 'text-anchor': textAnchor.toJson(), + 'text-max-angle': textMaxAngle.toJson(), + if (textWritingMode case final textWritingMode?) + 'text-writing-mode': textWritingMode.toJson(), + 'text-rotate': textRotate.toJson(), + 'text-padding': textPadding.toJson(), + 'text-keep-upright': textKeepUpright.toJson(), + 'text-transform': textTransform.toJson(), + 'text-offset': textOffset.toJson(), + 'text-allow-overlap': textAllowOverlap.toJson(), + if (textOverlap case final textOverlap?) 'text-overlap': textOverlap.toJson(), + 'text-ignore-placement': textIgnorePlacement.toJson(), + 'text-optional': textOptional.toJson(), + 'visibility': visible ? 'visible' : 'none', +}; + +/// Creates a map of paint properties for a [SymbolStyleLayer]. +Map createSymbolPaint({ + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, +}) => { + 'icon-opacity': iconOpacity.toJson(), + 'icon-color': iconColor.toJson(), + 'icon-halo-color': iconHaloColor.toJson(), + 'icon-halo-width': iconHaloWidth.toJson(), + 'icon-halo-blur': iconHaloBlur.toJson(), + 'icon-translate': iconTranslate.toJson(), + 'icon-translate-anchor': iconTranslateAnchor.toJson(), + 'text-opacity': textOpacity.toJson(), + 'text-color': textColor.toJson(), + 'text-halo-color': textHaloColor.toJson(), + 'text-halo-width': textHaloWidth.toJson(), + 'text-halo-blur': textHaloBlur.toJson(), + 'text-translate': textTranslate.toJson(), + 'text-translate-anchor': textTranslateAnchor.toJson(), +}; diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/line_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/line_style_layer.dart index db50db929..4407e3594 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/line_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/line_style_layer.dart @@ -6,18 +6,243 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class LineStyleLayer extends StyleLayerWithSource { +abstract interface class LineStyleLayer + implements + StyleLayerWithVectorSource, + StyleLayerWithSortKey, + StyleLayerWithTranslate { /// Default constructor for a [LineStyleLayer] instance. - const LineStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - - // TODO add properties + factory LineStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + String? sourceLayerId, + Expression? filter, + PropertyValue? sortKey, + PropertyValue translate = StyleLayerWithTranslate.defaultTranslate, + PropertyValue translateAnchor = + StyleLayerWithTranslate.defaultTranslateAnchor, + PropertyValue cap = defaultCap, + PropertyValue join = defaultJoin, + PropertyValue miterLimit = defaultMiterLimit, + PropertyValue roundLimit = defaultRoundLimit, + PropertyValue opacity = defaultOpacity, + PropertyValue color = defaultColor, + PropertyValue width = defaultWidth, + PropertyValue gapWidth = defaultGapWidth, + PropertyValue offset = defaultOffset, + PropertyValue blur = defaultBlur, + PropertyValue>? dashArray, + PropertyValue? pattern, + PropertyValue? gradient, + }) => MapLibrePlatform.instance.createLineStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + cap: cap, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + opacity: opacity, + color: color, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + ); + + /// {@macro line-cap} + /// + /// Layout property. Optional enum. Defaults to [LineCap.butt]. + PropertyValue get cap; + + set cap(PropertyValue property); + + /// Default value for [cap]. + static const defaultCap = PropertyValue.value(LineCap.butt); + + /// {@macro line-join} + /// + /// Layout property. Optional enum. Defaults to [LineJoin.miter]. + PropertyValue get join; + + set join(PropertyValue property); + + /// Default value for [join]. + static const defaultJoin = PropertyValue.value(LineJoin.miter); + + /// Used to automatically convert miter joins to bevel joins for sharp angles. + /// + /// Layout property. Optional number. Defaults to 2. Requires [join] to be + /// miter. Supports [interpolate] expressions. + PropertyValue get miterLimit; + + set miterLimit(PropertyValue property); + + /// Default value for [miterLimit]. + static const defaultMiterLimit = PropertyValue.value(2); + + /// Used to automatically convert round joins to miter joins for shallow + /// angles. + /// + /// Layout property. Optional number. Defaults to `1.05`. Requires [join] to + /// be [LineJoin.round]. Supports [interpolate] expressions. + PropertyValue get roundLimit; + + set roundLimit(PropertyValue property); + + /// Default value for [roundLimit]. + static const defaultRoundLimit = PropertyValue.value(1.05); + + /// The opacity at which the line will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value of [opacity]. + static const defaultOpacity = PropertyValue.value(1); + + /// The color with which the line will be drawn. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"` (black). + /// Disabled by [pattern]. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get color; + + set color(PropertyValue property); + + /// Default value of [color]. + static const defaultColor = PropertyValue.value(Color(0xFF000000)); + + /// Stroke thickness. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `1`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get width; + + set width(PropertyValue property); + + /// Default value of [width]. + static const defaultWidth = PropertyValue.value(1); + + /// Draws a line casing outside of a line's actual path. Value indicates the + /// width of the inner gap. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get gapWidth; + + set gapWidth(PropertyValue property); + + /// Default value of [gapWidth]. + static const defaultGapWidth = PropertyValue.value(0); + + /// The line's offset. For linear features, a positive value offsets the line + /// to the right, relative to the direction of the line, and a negative value + /// to the left. For polygon features, a positive value results in an inset, + /// and a negative value results in an outset. + /// + /// Paint property. Optional number. Units in pixels. Defaults to `0`. + /// Supports [featureState] and [interpolate] expressions. Transitionable. + PropertyValue get offset; + + set offset(PropertyValue property); + + /// Default value of [offset]. + static const defaultOffset = PropertyValue.value(0); + + /// Blur applied to the line, in pixels. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Supports feature-state and interpolate expressions. + /// Transitionable. + PropertyValue get blur; + + set blur(PropertyValue property); + + /// Default value of [blur]. + static const defaultBlur = PropertyValue.value(0); + + /// Specifies the lengths of the alternating dashes and gaps that form the + /// dash pattern. The lengths are later scaled by the line width. To convert + /// a dash length to pixels, multiply the length by the current line width. + /// GeoJSON sources with lineMetrics: true specified won't render dashed lines + /// to the expected scale. Zoom-dependent expressions will be evaluated only + /// at integer zoom levels. The only way to create an array value is using + /// `["literal", [...]]`; arrays cannot be read from or derived from feature + /// properties. + /// + /// Paint property. Optional array in range [0, ∞). Units in line widths. + /// Disabled by line-pattern. Transitionable. + PropertyValue>? get dashArray; + + set dashArray(PropertyValue>? property); + + /// Name of image in sprite to use for drawing image lines. For seamless + /// patterns, image width must be a factor of two (2, 4, 8, ..., 512). Note that + /// zoom-dependent expressions will be evaluated only at integer zoom levels. + /// + /// Paint property. Optional `resolvedImage` string. Transitionable. + PropertyValue? get pattern; + + set pattern(PropertyValue? property); + + /// Defines a gradient with which to color a line feature. Can only be used + /// with GeoJSON sources that specify "lineMetrics": true. + /// + /// Paint property. Optional [Color]. Disabled by [dashArray]. Disabled by + /// [pattern]. Requires source to be geojson. Supports [interpolate] + /// expressions. + PropertyValue? get gradient; + + set gradient(PropertyValue? property); +} + +/// {@template line-cap} +/// The display of line endings. +/// {@endtemplate} +enum LineCap { + /// A cap with a squared-off end which is drawn to the exact endpoint of the line. + butt, + + /// A cap with a rounded end which is drawn beyond the endpoint of the line at a radius of one-half of the line's width and centered on the endpoint of the line. + round, + + /// A cap with a squared-off end which is drawn beyond the endpoint of the line at a distance of one-half of the line's width. + square, +} + +/// {@template line-join} +/// The display of lines when joining. +/// {@endtemplate} +enum LineJoin { + /// A join with a squared-off end which is drawn beyond the endpoint of the + /// line at a distance of one-half of the line's width. + bevel, + + /// A join with a rounded end which is drawn beyond the endpoint of the line + /// at a radius of one-half of the line's width and centered on the endpoint + /// of the line. + round, + + /// A join with a sharp, angled corner which is drawn with the outer sides + /// beyond the endpoint of the path until they meet. + miter, } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/raster_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/raster_style_layer.dart index 84e75040a..af6de15dd 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/raster_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/raster_style_layer.dart @@ -6,16 +6,142 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class RasterStyleLayer extends StyleLayerWithSource { +abstract interface class RasterStyleLayer implements StyleLayerWithSource { /// Default constructor for a [RasterStyleLayer] instance. - const RasterStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - }); - - // TODO add properties + factory RasterStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + PropertyValue opacity = defaultOpacity, + PropertyValue hueRotate = defaultHueRotate, + PropertyValue brightnessMin = defaultBrightnessMin, + PropertyValue brightnessMax = defaultBrightnessMax, + PropertyValue saturation = defaultSaturation, + PropertyValue contrast = defaultContrast, + PropertyValue resampling = defaultResampling, + PropertyValue fadeDuration = defaultFadeDuration, + }) => MapLibrePlatform.instance.createRasterStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + /// The opacity at which the image will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get opacity; + + set opacity(PropertyValue property); + + /// Default value for [opacity] property. + static const defaultOpacity = PropertyValue.value(1); + + /// Rotates hues around the color wheel. + /// + /// Paint property. Optional number. Units in degrees. Defaults to `0`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get hueRotate; + + set hueRotate(PropertyValue property); + + /// Default value for [hueRotate] property. + static const defaultHueRotate = PropertyValue.value(0); + + /// Increase or reduce the brightness of the image. The value is the minimum + /// brightness. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `0`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get brightnessMin; + + set brightnessMin(PropertyValue property); + + /// Default value for [brightnessMin] property. + static const defaultBrightnessMin = PropertyValue.value(0); + + /// Increase or reduce the brightness of the image. The value is the maximum + /// brightness. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get brightnessMax; + + set brightnessMax(PropertyValue property); + + /// Default value for [brightnessMax] property. + static const defaultBrightnessMax = PropertyValue.value(1); + + /// Increase or reduce the saturation of the image. + /// + /// Paint property. Optional number in range `[-1, 1]`. Defaults to `0`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get saturation; + + set saturation(PropertyValue property); + + /// Default value for [saturation] property. + static const defaultSaturation = PropertyValue.value(0); + + /// Increase or reduce the contrast of the image. + /// + /// Paint property. Optional number in range `[-1, 1]`. Defaults to `0`. + /// Supports [interpolate] expressions. Transitionable. + PropertyValue get contrast; + + set contrast(PropertyValue property); + + /// Default value for [contrast] property. + static const defaultContrast = PropertyValue.value(0); + + /// {@macro raster-resampling} + /// + /// Paint property. Optional enum. Defaults to "linear". + PropertyValue get resampling; + + set resampling(PropertyValue property); + + /// Default value for [resampling] property. + static const defaultResampling = PropertyValue.value( + RasterResampling.linear, + ); + + /// Fade duration when a new tile is added, or when a video is started or its + /// coordinates are updated. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in milliseconds. + /// Defaults to `300`. Supports [interpolate] expressions. + PropertyValue get fadeDuration; + + set fadeDuration(PropertyValue property); + + /// Default value for [fadeDuration] property. + static const defaultFadeDuration = PropertyValue.value(300); +} + +/// {@template raster-resampling} +/// The resampling/interpolation method to use for overscaling, also known as +/// texture magnification filter. +/// {@endtemplate} +enum RasterResampling { + /// (Bi)linear filtering interpolates pixel values using the weighted average + /// of the four closest original source pixels creating a smooth but blurry + /// look when overscaled. + linear, + + /// Nearest neighbor filtering interpolates pixel values using the nearest + /// original source pixel creating a sharp but pixelated look when overscaled. + nearest, } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/style_layer.dart index 535fe97cc..6e2fe19a6 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/style_layer.dart @@ -1,46 +1,92 @@ -import 'dart:ui'; - +import 'package:flutter/painting.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; part 'background_style_layer.dart'; part 'circle_style_layer.dart'; +part 'color_relief_style_layer.dart'; part 'fill_extrusion_style_layer.dart'; part 'fill_style_layer.dart'; part 'heatmap_style_layer.dart'; part 'hillshade_style_layer.dart'; +part 'layout_paint_helpers.dart'; part 'line_style_layer.dart'; part 'raster_style_layer.dart'; part 'symbol_style_layer.dart'; +/// The coordinate space or frame of reference in which a style value is +/// evaluated. +enum ReferenceSpace { + /// Relative to the map's coordinate system. + map, + + /// Relative to the viewport / screen. + viewport, +} + /// The base Layer class that can't be used directly. /// /// {@category Style} /// {@subCategory Style Layers} -interface class StyleLayer { - /// Create a new [StyleLayer] instance. - const StyleLayer({ - required this.id, - this.layout = const {}, - this.paint = const {}, - this.minZoom = 0, - this.maxZoom = 24, - this.filter, - }); - +abstract class StyleLayer { /// Unique layer name. - final String id; + /// + /// Required string. + String get id; /// The minimum zoom level for the layer. At zoom levels less than the /// minzoom, the layer will be hidden. /// - /// Needs to be in the range of [0,24]. - final double minZoom; + /// Optional number in the range of `0,24`. + double get minZoom; + + set minZoom(double property); + + /// Default value of [minZoom]. + static const defaultMinZoom = 0.0; /// The maximum zoom level for the layer. At zoom levels equal to or greater /// than the maxzoom, the layer will be hidden. /// - /// Needs to be in the range of [0,24]. - final double maxZoom; + /// Optional number in the range of `0,24`. + double get maxZoom; + + set maxZoom(double property); + + /// Default value of [maxZoom]. + static const defaultMaxZoom = 24.0; + + /// Whether this layer is displayed. + /// + /// Layout property. Defaults to `true`. + bool get visible; + + set visible(bool property); + + /// Default value of [visible]. + static const defaultVisible = true; +} + +/// A [StyleLayer] that pulls its data from a [Source]. Basically every layer +/// except [BackgroundStyleLayer]. +/// +/// {@category Style} +/// {@subCategory Style Layers} +abstract interface class StyleLayerWithSource implements StyleLayer { + /// Name of a source description to be used for this layer. Required for all + /// layer types except background. + /// + /// Optional string. + String get sourceId; +} + +/// A [StyleLayerWithSource] that has a [sourceLayerId] and [filter] property. +abstract interface class StyleLayerWithVectorSource + implements StyleLayerWithSource { + /// Layer to use from a vector tile source. Required for [VectorSource]; + /// prohibited for all other source types, including GeoJSON sources. + String? get sourceLayerId; + + set sourceLayerId(String value); /// A expression specifying conditions on source features. Only features that /// match the filter are displayed. Zoom expressions in filters are only @@ -51,38 +97,47 @@ interface class StyleLayer { /// from a source: [CircleStyleLayer], [FillStyleLayer], /// [FillExtrusionStyleLayer], [HeatmapStyleLayer], [LineStyleLayer] /// and [SymbolStyleLayer]. - final List? filter; + Expression? get filter; - /// Layout properties for the layer. - final Map layout; + set filter(Expression expression); +} + +/// A [StyleLayerWithSource] that has a [sortKey] property. +abstract interface class StyleLayerWithSortKey extends StyleLayerWithSource { + /// Sorts features in ascending order based on this value. Features with a + /// higher sort key will appear above features with a lower sort key. + /// + /// Layout property. Optional number. + PropertyValue? get sortKey; - /// Default paint properties for this layer. - final Map paint; + set sortKey(PropertyValue? property); } -/// A [StyleLayer] that pulls its data from a [Source]. Basically every layer -/// except [BackgroundStyleLayer]. -/// -/// {@category Style} -/// {@subCategory Style Layers} -interface class StyleLayerWithSource extends StyleLayer { - /// const constructor for [StyleLayerWithSource]. - const StyleLayerWithSource({ - required super.id, - required this.sourceId, - super.paint = const {}, - super.layout = const {}, - super.minZoom, - super.maxZoom, - super.filter, - this.sourceLayerId, - }); +/// A [StyleLayerWithSource] that has a [translate] and [translateAnchor] +/// property. +abstract interface class StyleLayerWithTranslate extends StyleLayerWithSource { + /// The geometry's [Offset]. Values are `[x, y]` where negatives indicate left + /// and up, respectively. + /// + /// Paint property. Optional array. Units in pixels. Defaults to + /// [Offset.zero]. Supports interpolate expressions. Transitionable. + PropertyValue get translate; - /// Name of a source description to be used for this layer. Required for all - /// layer types except background. - final String sourceId; + set translate(PropertyValue property); + + /// Default value of [translate]. + static const defaultTranslate = PropertyValue.value(Offset.zero); + + /// Controls the frame of reference for [translate]. + /// + /// Paint property. Optional enum. Defaults to [ReferenceSpace.map]. + /// Requires [translate]. + PropertyValue get translateAnchor; + + set translateAnchor(PropertyValue property); - /// Layer to use from a vector tile source. Required for vector tile - /// sources; prohibited for all other source types, including GeoJSON sources. - final String? sourceLayerId; + /// The default value of [translateAnchor]. + static const defaultTranslateAnchor = PropertyValue.value( + ReferenceSpace.map, + ); } diff --git a/packages/maplibre_platform_interface/lib/src/style/layers/symbol_style_layer.dart b/packages/maplibre_platform_interface/lib/src/style/layers/symbol_style_layer.dart index ea8a8dee6..9970c20a8 100644 --- a/packages/maplibre_platform_interface/lib/src/style/layers/symbol_style_layer.dart +++ b/packages/maplibre_platform_interface/lib/src/style/layers/symbol_style_layer.dart @@ -6,18 +6,1204 @@ part of 'style_layer.dart'; /// /// {@category Style} /// {@subCategory Style Layers} -final class SymbolStyleLayer extends StyleLayerWithSource { +abstract interface class SymbolStyleLayer + implements StyleLayerWithVectorSource, StyleLayerWithSortKey { /// Default constructor for a [SymbolStyleLayer] instance. - const SymbolStyleLayer({ - required super.id, - required super.sourceId, - super.layout, - super.paint, - super.minZoom = 0, - super.maxZoom = 24, - super.filter, - super.sourceLayerId, - }); - - // TODO add properties + factory SymbolStyleLayer({ + required String id, + required String sourceId, + bool visible = StyleLayer.defaultVisible, + double minZoom = StyleLayer.defaultMinZoom, + double maxZoom = StyleLayer.defaultMaxZoom, + String? sourceLayerId, + Expression? filter, + PropertyValue? sortKey, + PropertyValue placement = defaultPlacement, + PropertyValue spacing = defaultSpacing, + PropertyValue avoidEdges = defaultAvoidEdges, + PropertyValue zOrder = defaultZOrder, + PropertyValue iconAllowOverlap = defaultIconAllowOverlap, + PropertyValue iconOverlap = defaultIconOverlap, + PropertyValue iconIgnorePlacement = defaultIconIgnorePlacement, + PropertyValue iconOptional = defaultIconOptional, + PropertyValue iconRotationAlignment = + defaultIconRotationAlignment, + PropertyValue iconSize = defaultIconSize, + PropertyValue iconTextFit = defaultIconTextFit, + PropertyValue iconTextFitPadding = defaultIconTextFitPadding, + PropertyValue? iconImage, + PropertyValue iconRotate = defaultIconRotate, + PropertyValue iconPadding = defaultIconPadding, + PropertyValue iconKeepUpright = defaultIconKeepUpright, + PropertyValue iconOffset = defaultIconOffset, + PropertyValue iconAnchor = defaultIconAnchor, + PropertyValue iconPitchAlignment = + defaultIconPitchAlignment, + PropertyValue textPitchAlignment = + defaultTextPitchAlignment, + PropertyValue textRotationAlignment = + defaultTextRotationAlignment, + PropertyValue textField = defaultTextField, + PropertyValue> textFont = defaultTextFont, + PropertyValue textSize = defaultTextSize, + PropertyValue textMaxWidth = defaultTextMaxWidth, + PropertyValue textLineHeight = defaultTextLineHeight, + PropertyValue textLetterSpacing = defaultTextLetterSpacing, + PropertyValue textJustify = defaultTextJustify, + PropertyValue textRadialOffset = defaultTextRadialOffset, + PropertyValue>? textVariableAnchor, + PropertyValue>? textVariableAnchorOffset, + PropertyValue textAnchor = defaultTextAnchor, + PropertyValue textMaxAngle = defaultTextMaxAngle, + PropertyValue>? textWritingMode, + PropertyValue textRotate = defaultTextRotate, + PropertyValue textPadding = defaultTextPadding, + PropertyValue textKeepUpright = defaultTextKeepUpright, + PropertyValue textTransform = defaultTextTransform, + PropertyValue textOffset = defaultTextOffset, + PropertyValue textAllowOverlap = defaultTextAllowOverlap, + PropertyValue? textOverlap, + PropertyValue textIgnorePlacement = defaultTextIgnorePlacement, + PropertyValue textOptional = defaultTextOptional, + PropertyValue iconOpacity = defaultIconOpacity, + PropertyValue iconColor = defaultIconColor, + PropertyValue iconHaloColor = defaultIconHaloColor, + PropertyValue iconHaloWidth = defaultIconHaloWidth, + PropertyValue iconHaloBlur = defaultIconHaloBlur, + PropertyValue iconTranslate = defaultIconTranslate, + PropertyValue iconTranslateAnchor = + defaultIconTranslateAnchor, + PropertyValue textOpacity = defaultTextOpacity, + PropertyValue textColor = defaultTextColor, + PropertyValue textHaloColor = defaultTextHaloColor, + PropertyValue textHaloWidth = defaultTextHaloWidth, + PropertyValue textHaloBlur = defaultTextHaloBlur, + PropertyValue textTranslate = defaultTextTranslate, + PropertyValue textTranslateAnchor = + defaultTextTranslateAnchor, + }) => MapLibrePlatform.instance.createSymbolStyleLayer( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); + + /// {@macro symbol-placement} + /// + /// Layout property. Optional enum. Defaults to [SymbolPlacement.point]. + PropertyValue get placement; + + set placement(PropertyValue property); + + /// Default value for [spacing]. + static const defaultPlacement = PropertyValue.value( + SymbolPlacement.point, + ); + + /// Distance between two symbol anchors. + /// + /// Layout property. Optional number in range `[1, ∞)`. Units in pixels. + /// Defaults to `250`. Requires [placement] to be line. Supports [interpolate] + /// expressions. + PropertyValue get spacing; + + set spacing(PropertyValue property); + + /// Default value for [spacing]. + static const defaultSpacing = PropertyValue.value(250); + + /// If true, the symbols will not cross tile edges to avoid mutual collisions. + /// Recommended in layers that don't have enough padding in the vector tile to + /// prevent collisions, or if it is a point symbol layer placed after a line + /// symbol layer. When using a client that supports global collision + /// detection, like MapLibre GL JS version 0.42.0 or greater, enabling this + /// property is not needed to prevent clipped labels at tile boundaries. + /// + /// Layout property. Optional boolean. Defaults to `false`. + PropertyValue get avoidEdges; + + set avoidEdges(PropertyValue property); + + /// Default value for [avoidEdges]. + static const defaultAvoidEdges = PropertyValue.value(false); + + /// {@macro symbol-z-order} + /// + /// Layout property. Optional enum. Defaults to [SymbolZOrder.auto]. + PropertyValue get zOrder; + + set zOrder(PropertyValue property); + + /// Default value for [zOrder]. + static const defaultZOrder = PropertyValue.value( + SymbolZOrder.auto, + ); + + /// If true, the icon will be visible even if it collides with other + /// previously drawn symbols. + /// + /// Layout property. Optional boolean. Defaults to `false`. + /// Requires [iconImage]. Disabled by [iconOverlap]. + PropertyValue get iconAllowOverlap; + + set iconAllowOverlap(PropertyValue property); + + /// Default value for [iconAllowOverlap]. + static const defaultIconAllowOverlap = PropertyValue.value(false); + + /// Allows for control over whether to show an icon when it overlaps other + /// symbols on the map. If [iconOverlap] is not set, [iconAllowOverlap] is + /// used instead. + /// + /// Layout property. Optional enum. Requires [iconImage]. + PropertyValue get iconOverlap; + + set iconOverlap(PropertyValue property); + + /// Default value for [iconOverlap]. + static const defaultIconOverlap = PropertyValue.value( + SymbolOverlap.never, + ); + + /// If true, other symbols can be visible even if they collide with the icon. + /// + /// Layout property. Optional boolean. Defaults to `false`. + /// Requires [iconImage]. + PropertyValue get iconIgnorePlacement; + + set iconIgnorePlacement(PropertyValue property); + + /// Default value for [iconIgnorePlacement]. + static const defaultIconIgnorePlacement = PropertyValue.value(false); + + /// If true, text will display without their corresponding icons when the + /// icon collides with other symbols and the text does not. + /// + /// Layout property. Optional boolean. Defaults to `false`. + /// Requires [iconImage]. Requires [textField]. + PropertyValue get iconOptional; + + set iconOptional(PropertyValue property); + + /// Default value for [iconOptional]. + static const defaultIconOptional = PropertyValue.value(false); + + /// {@macro icon-rotation-alignment} + /// + /// Layout property. Optional enum. Defaults to "auto". Requires [iconImage]. + PropertyValue get iconRotationAlignment; + + set iconRotationAlignment(PropertyValue property); + + /// Default value for [iconRotationAlignment]. + static const defaultIconRotationAlignment = + PropertyValue.value(IconRotationAlignment.auto); + + /// Scales the original size of the icon by the provided factor. The new + /// pixel size of the image will be the original pixel size multiplied by + /// [iconSize]. 1 is the original size; 3 triples the size of the image. + /// + /// Layout property. Optional number in range `[0, ∞)`. Units in factor of + /// the original icon size. Defaults to `1`. Requires [iconImage]. Supports + /// [interpolate] expressions. + PropertyValue get iconSize; + + set iconSize(PropertyValue property); + + /// Default value for [iconSize]. + static const defaultIconSize = PropertyValue.value(1); + + /// {@macro icon-text-fit} + /// + /// Layout property. Optional enum. Defaults to [IconTextFit.none]. + /// Requires [iconImage]. Requires [textField]. + PropertyValue get iconTextFit; + + set iconTextFit(PropertyValue property); + + /// Default value for [iconTextFit]. + static const defaultIconTextFit = PropertyValue.value( + IconTextFit.none, + ); + + /// Size of the additional area added to dimensions determined by + /// [iconTextFit], in clockwise order: top, right, bottom, left. + /// + /// Layout property. Optional array. Units in pixels. Defaults to + /// [EdgeInsets.zero]. Requires [iconImage]. Requires [textField]. Requires + /// [iconTextFit] to be one of [IconTextFit.both], [IconTextFit.width], + /// [IconTextFit.height]. Supports [interpolate] expressions. + PropertyValue get iconTextFitPadding; + + set iconTextFitPadding(PropertyValue property); + + /// Default value for [iconTextFitPadding]. + static const defaultIconTextFitPadding = PropertyValue.value( + EdgeInsets.zero, + ); + + /// Name of image in sprite to use for drawing an image background. + /// + /// Layout property. Optional resolvedImage. + PropertyValue? get iconImage; + + set iconImage(PropertyValue? property); + + /// Rotates the icon clockwise. + /// + /// Layout property. Optional number. Units in degrees. Defaults to `0`. + /// Requires [iconImage]. Supports [interpolate] expressions. + PropertyValue get iconRotate; + + set iconRotate(PropertyValue property); + + /// Default value for [iconRotate]. + static const defaultIconRotate = PropertyValue.value(0); + + /// Size of additional area round the icon bounding box used for detecting + /// symbol collisions. + /// + /// Layout property. Optional padding. Units in pixels. Defaults to `[2]`. + /// Requires [iconImage]. Supports [interpolate] expressions. + PropertyValue get iconPadding; + + set iconPadding(PropertyValue property); + + /// Default value for [iconPadding]. + static const defaultIconPadding = PropertyValue.value( + EdgeInsets.all(2), + ); + + /// If true, the icon may be flipped to prevent it from being rendered + /// upside-down. + /// + /// Layout property. Optional boolean. Defaults to `false`. Requires + /// [iconImage]. Requires [iconRotationAlignment] to be + /// [IconRotationAlignment.map]. Requires [placement] to be one of + /// [SymbolPlacement.line], [SymbolPlacement.lineCenter]. + PropertyValue get iconKeepUpright; + + set iconKeepUpright(PropertyValue property); + + /// Default value for [iconKeepUpright]. + static const defaultIconKeepUpright = PropertyValue.value(false); + + /// Offset distance of icon from its anchor. Positive values indicate right + /// and down, while negative values indicate left and up. Each component is + /// multiplied by the value of [iconSize] to obtain the final offset in + /// pixels. When combined with [iconRotate] the offset will be as if the + /// rotated direction was up. + /// + /// Layout property. Optional array. Defaults to [Offset.zero]. Requires + /// [iconImage]. Supports [interpolate] expressions. + PropertyValue get iconOffset; + + set iconOffset(PropertyValue property); + + /// Default value for [iconOffset]. + static const defaultIconOffset = PropertyValue.value(Offset.zero); + + /// {@macro icon-anchor} + /// + /// Layout property. Optional enum. Defaults to [IconAnchor.center]. + /// Requires [iconImage]. + PropertyValue get iconAnchor; + + set iconAnchor(PropertyValue property); + + /// Default value for [iconAnchor]. + static const defaultIconAnchor = PropertyValue.value( + IconAnchor.center, + ); + + /// {@macro icon-pitch-alignment} + /// + /// Layout property. Optional enum. Defaults to [IconPitchAlignment.auto]. + /// Requires [iconImage]. + PropertyValue get iconPitchAlignment; + + set iconPitchAlignment(PropertyValue property); + + /// Default value for [iconPitchAlignment]. + static const defaultIconPitchAlignment = + PropertyValue.value(IconPitchAlignment.auto); + + /// {@macro text-pitch-alignment} + /// + /// Layout property. Optional enum. Defaults to [TextPitchAlignment.auto]. + /// Requires [textField]. + PropertyValue get textPitchAlignment; + + set textPitchAlignment(PropertyValue property); + + /// Default value for [textPitchAlignment]. + static const defaultTextPitchAlignment = + PropertyValue.value(TextPitchAlignment.auto); + + /// {@macro text-rotation-alignment} + /// + /// Layout property. Optional enum. Defaults to + /// [TextRotationAlignment.auto]. Requires [textField]. + PropertyValue get textRotationAlignment; + + set textRotationAlignment(PropertyValue property); + + /// Default value for [textRotationAlignment]. + static const defaultTextRotationAlignment = + PropertyValue.value(TextRotationAlignment.auto); + + /// Value to use for a text label. If a plain `string` is provided, it will be + /// treated as a `formatted` with default/inherited formatting options. + /// + /// Layout property. Optional formatted. Defaults to `""`. + PropertyValue get textField; + + set textField(PropertyValue property); + + /// Default value for [textField]. + static const defaultTextField = PropertyValue.value(''); + + /// Fonts to use for displaying text. If the `glyphs` root property is + /// specified, this array is joined together and interpreted as a font stack + /// name. Otherwise, it is interpreted as a cascading fallback list of local + /// font names. + /// + /// Layout property. Optional `array`. Defaults to + /// `["Open Sans Regular","Arial Unicode MS Regular"]`. Requires [textField]. + PropertyValue> get textFont; + + set textFont(PropertyValue> property); + + /// Default value for [textFont]. + static const defaultTextFont = PropertyValue>.value([ + 'Open Sans Regular', + 'Arial Unicode MS Regular', + ]); + + /// Font size. + /// + /// Layout property. Optional `number` in range `[0, ∞)`. Units in pixels. + /// Defaults to `16`. Requires [textField]. Supports [interpolate] + /// expressions. + PropertyValue get textSize; + + set textSize(PropertyValue property); + + /// Default value for [textSize]. + static const defaultTextSize = PropertyValue.value(16); + + /// The maximum line width for text wrapping. + /// + /// Layout property. Optional `number` in range `[0, ∞)`. Units in ems. + /// Defaults to `10`. Requires [textField]. Supports [interpolate] + /// expressions. + PropertyValue get textMaxWidth; + + set textMaxWidth(PropertyValue property); + + /// Default value for [textMaxWidth]. + static const defaultTextMaxWidth = PropertyValue.value(10); + + /// Text leading value for multi-line text. + /// + /// Layout property. Optional `number`. Units in ems. Defaults to `1.2`. + /// Requires [textField]. Supports [interpolate] expressions. + PropertyValue get textLineHeight; + + set textLineHeight(PropertyValue property); + + /// Default value for [textLineHeight]. + static const defaultTextLineHeight = PropertyValue.value(1.2); + + /// Text tracking amount. + /// + /// Layout property. Optional `number`. Units in ems. Defaults to `0`. + /// Requires [textField]. Supports [interpolate] expressions. + PropertyValue get textLetterSpacing; + + set textLetterSpacing(PropertyValue property); + + /// Default value for [textLetterSpacing]. + static const defaultTextLetterSpacing = PropertyValue.value(0); + + /// {@macro text-justify} + /// + /// Layout property. Optional enum. Defaults to [TextJustify.center]. + /// Requires [textField]. + PropertyValue get textJustify; + + set textJustify(PropertyValue property); + + /// Default value for [textJustify]. + static const defaultTextJustify = PropertyValue.value( + TextJustify.center, + ); + + /// Radial offset of text, in the direction of the symbol's anchor. Useful in + /// combination with [textVariableAnchor], which defaults to using the + /// two-dimensional [textOffset] if present. + /// + /// Layout property. Optional `number`. Units in ems. Defaults to `0`. + /// Requires [textField]. Supports [interpolate] expressions. + PropertyValue get textRadialOffset; + + set textRadialOffset(PropertyValue property); + + /// Default value for [textRadialOffset]. + static const defaultTextRadialOffset = PropertyValue.value(0); + + /// To increase the chance of placing high-priority labels on the map, you + /// can provide an array of [textAnchor] locations: the renderer will attempt + /// to place the label at each location, in order, before moving onto the + /// next label. Use [textJustify]: auto to choose justification based on + /// anchor position. To apply an offset, use the [textRadialOffset] or the + /// two-dimensional [textOffset]. For example: + /// ```json + /// "text-variable-anchor": ["center", "left", "right"] + /// ``` + /// + /// Layout property. Optional `array`. Requires [textField]. Requires + /// [placement] to be [SymbolPlacement.point]. + PropertyValue>? get textVariableAnchor; + + set textVariableAnchor(PropertyValue>? property); + + /// To increase the chance of placing high-priority labels on the map, you + /// can provide an array of text-anchor locations, each paired with an offset + /// value. The renderer will attempt to place the label at each location, in + /// order, before moving on to the next location+offset. Use [textJustify] + /// [TextJustify.auto] to choose justification based on anchor + /// position. + /// + /// The length of the array must be even, and must alternate between enum and + /// point entries. i.e., each anchor location must be accompanied by a point, + /// and that point defines the offset when the corresponding anchor location + /// is used. Positive offset values indicate right and down, while negative + /// values indicate left and up. Anchor locations may repeat, allowing the + /// renderer to try multiple offsets to try and place a label using the same + /// anchor. + /// + /// When present, this property takes precedence over [textAnchor], + /// [textVariableAnchor], [textOffset], and [textRadialOffset]. + /// ```json + /// { + /// "text-variable-anchor-offset": [ + /// "top", [0, 4], + /// "left", [3,0], + /// "bottom", [1, 1] + /// ] + /// } + /// ``` + /// + /// When the renderer chooses the top anchor, `[0, 4]` will be used for + /// [textOffset]; the text will be shifted down by `4 ems`. + /// + /// When the renderer chooses the left anchor, `[3, 0]` will be used for + /// [textOffset]; the text will be shifted right by `3 ems`. + /// + /// ```json + /// "text-variable-anchor-offset": [ + /// "top", [0, 4], + /// "left", [3, 0], + /// "bottom", [1, 1] + /// ] + /// ``` + /// + /// Layout property. Optional. Requires [textField]. Requires [placement] to + /// be [SymbolPlacement.point]. Supports [interpolate] expressions. + PropertyValue>? get textVariableAnchorOffset; + + set textVariableAnchorOffset(PropertyValue>? property); + + /// {@macro text-anchor} + /// + /// Layout property. Optional enum. Defaults to [TextAnchor.center]. Requires + /// [textField]. Disabled by [textVariableAnchor]. + PropertyValue get textAnchor; + + set textAnchor(PropertyValue property); + + /// Default value for [textAnchor]. + static const defaultTextAnchor = PropertyValue.value( + TextAnchor.center, + ); + + /// Maximum angle change between adjacent characters. + /// + /// Layout property. Optional `number`. Units in degrees. Defaults to `45`. + /// Requires [textField]. Requires [placement] to be one of + /// [SymbolPlacement.line], [SymbolPlacement.lineCenter]. Supports + /// [interpolate] expressions. + PropertyValue get textMaxAngle; + + set textMaxAngle(PropertyValue property); + + /// Default value for [textMaxAngle]. + static const defaultTextMaxAngle = PropertyValue.value(45); + + /// {@macro text-writing-mode} + /// + /// Note that the + /// property values act as a hint, so that a symbol whose language doesn’t + /// support the provided orientation will be laid out in its natural + /// orientation. Example: English point symbol will be rendered horizontally + /// even if array value contains single 'vertical' enum value. The order of + /// elements in an array define priority order for the placement of an + /// orientation variant. + /// + /// Layout property. Optional array. Requires [textField]. Requires + /// [placement] to be [SymbolPlacement.point]. + PropertyValue>? get textWritingMode; + + set textWritingMode(PropertyValue>? property); + + /// Rotates the text clockwise. + /// + /// Layout property. Optional number. Units in degrees. Defaults to `0`. + /// Requires [textField]. Supports [interpolate] expressions. + PropertyValue get textRotate; + + set textRotate(PropertyValue property); + + /// Default value for [textRotate]. + static const defaultTextRotate = PropertyValue.value(0); + + /// Size of the additional area around the text bounding box used for + /// detecting symbol collisions. + /// + /// Layout property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `2`. Requires [textField]. Supports [interpolate] expressions. + PropertyValue get textPadding; + + set textPadding(PropertyValue property); + + /// Default value for [textPadding]. + static const defaultTextPadding = PropertyValue.value(2); + + /// If `true`, the text may be flipped vertically to prevent it from being + /// rendered upside-down. + /// + /// Layout property. Optional `boolean`. Defaults to `true`. Requires + /// [textField]. Requires [textRotationAlignment] to be + /// [TextRotationAlignment.map]. Requires [placement] to be one of + /// [SymbolPlacement.line], [SymbolPlacement.lineCenter]. + PropertyValue get textKeepUpright; + + set textKeepUpright(PropertyValue property); + + /// Default value for [textKeepUpright]. + static const defaultTextKeepUpright = PropertyValue.value(true); + + /// Specifies how to capitalize text, similar to the CSS text-transform + /// property. + /// + /// Layout property. Optional enum. Defaults to [TextTransform.none]. + /// Requires [textField]. + PropertyValue get textTransform; + + set textTransform(PropertyValue property); + + /// Default value for [textTransform]. + static const defaultTextTransform = PropertyValue.value( + TextTransform.none, + ); + + /// Offset distance of text from its anchor. Positive values indicate right + /// and down, while negative values indicate left and up. If used with + /// text-variable-anchor, input values will be taken as absolute values. + /// Offsets along the x- and y-axis will be applied automatically based on + /// the anchor position. + /// + /// Layout property. Optional array. Units in ems. Defaults to [Offset.zero]. + /// Requires [textField]. Disabled by [textRadialOffset]. Supports + /// [interpolate] expressions. + PropertyValue get textOffset; + + set textOffset(PropertyValue property); + + /// Default value for [textOffset]. + static const defaultTextOffset = PropertyValue.value(Offset.zero); + + /// If true, the text will be visible even if it collides with other + /// previously drawn symbols. + /// + /// Layout property. Optional boolean. Defaults to `false`. Requires + /// [textField]. Disabled by [textOverlap]. + PropertyValue get textAllowOverlap; + + set textAllowOverlap(PropertyValue property); + + /// Default value for [textAllowOverlap]. + static const defaultTextAllowOverlap = PropertyValue.value(false); + + /// Allows for control over whether to show symbol text when it overlaps + /// other symbols on the map. If [textOverlap] is not set, [textAllowOverlap] + /// is used instead. + /// + /// Layout property. Optional enum. Requires [textField]. + PropertyValue? get textOverlap; + + set textOverlap(PropertyValue? property); + + /// If `true`, other symbols can be visible even if they collide with the + /// text. + /// + /// Layout property. Optional `boolean`. Defaults to `false`. Requires + /// [textField]. + PropertyValue get textIgnorePlacement; + + set textIgnorePlacement(PropertyValue property); + + /// Default value for [textIgnorePlacement]. + static const defaultTextIgnorePlacement = PropertyValue.value(false); + + /// If true, icons will display without their corresponding text when the + /// text collides with other symbols and the icon does not. + /// + /// Layout property. Optional `boolean`. Defaults to `false`. Requires + /// [textField]. Requires [iconImage]. + PropertyValue get textOptional; + + set textOptional(PropertyValue property); + + /// Default value for [textOptional]. + static const defaultTextOptional = PropertyValue.value(false); + + /// The opacity at which the icon will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Requires [iconImage]. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get iconOpacity; + + set iconOpacity(PropertyValue property); + + /// Default value for [iconOpacity]. + static const defaultIconOpacity = PropertyValue.value(1); + + /// The color of the icon. This can only be used with SDF icons. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"`. Requires + /// [iconImage]. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get iconColor; + + set iconColor(PropertyValue property); + + /// Default value for [iconColor]. + static const defaultIconColor = PropertyValue.value(Color(0xFF000000)); + + /// The color of the icon's halo. Icon halos can only be used with SDF icons. + /// + /// Paint property. Optional [Color]. Defaults to `"rgba(0, 0, 0, 0)"`. + /// Requires iconImage. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get iconHaloColor; + + set iconHaloColor(PropertyValue property); + + /// Default value for [iconHaloColor]. + static const defaultIconHaloColor = PropertyValue.value( + Color(0x00000000), + ); + + /// Distance of halo to the icon outline. + /// + /// The unit is in pixels only for SDF sprites that were created with a blur + /// radius of 8, multiplied by the display density. I.e., the radius needs to + /// be 16 for @2x sprites, etc. + /// + /// Paint property. Optional `number` in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Requires [iconImage]. Supports [featureState] and + /// [interpolate] expressions. Transitionable. + PropertyValue get iconHaloWidth; + + set iconHaloWidth(PropertyValue property); + + /// Default value for [iconHaloWidth]. + static const defaultIconHaloWidth = PropertyValue.value(0); + + /// Fade out the halo towards the outside. + /// + /// Paint property. Optional `number` in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Requires [iconImage]. Supports [featureState] and + /// [interpolate] expressions. Transitionable. + PropertyValue get iconHaloBlur; + + set iconHaloBlur(PropertyValue property); + + /// Default value for [iconHaloBlur]. + static const defaultIconHaloBlur = PropertyValue.value(0); + + /// Distance that the icon's anchor is moved from its original placement. + /// Positive values indicate right and down, while negative values indicate + /// left and up. + /// + /// Paint property. Optional array. Units in pixels. Defaults to [Offset.zero]. + /// Requires [iconImage]. Supports [interpolate] expressions. Transitionable. + PropertyValue get iconTranslate; + + set iconTranslate(PropertyValue property); + + /// Default value for [iconTranslate]. + static const defaultIconTranslate = PropertyValue.value(Offset.zero); + + /// Controls the frame of reference for [iconTranslate]. + /// + /// Paint property. Optional enum. Defaults to [ReferenceSpace.map]. + /// Requires [iconImage]. Requires [iconTranslate]. + PropertyValue get iconTranslateAnchor; + + set iconTranslateAnchor(PropertyValue property); + + /// Default value for [iconTranslateAnchor]. + static const defaultIconTranslateAnchor = PropertyValue.value( + ReferenceSpace.map, + ); + + /// The opacity at which the text will be drawn. + /// + /// Paint property. Optional number in range `[0, 1]`. Defaults to `1`. + /// Requires [textField]. Supports [featureState] and [interpolate] + /// expressions. Transitionable. + PropertyValue get textOpacity; + + set textOpacity(PropertyValue property); + + /// The default value for [textOpacity]. + static const defaultTextOpacity = PropertyValue.value(1); + + /// The color with which the text will be drawn. + /// + /// Paint property. Optional [Color]. Defaults to `"#000000"`. Requires + /// [textField]. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get textColor; + + set textColor(PropertyValue property); + + /// The default value for [textColor]. + static const defaultTextColor = PropertyValue.value(Color(0xFF000000)); + + /// The color of the text's halo, which helps it stand out from backgrounds. + /// + /// Paint property. Optional color. Defaults to "rgba(0, 0, 0, 0)". Requires + /// [textField]. Supports [featureState] and [interpolate] expressions. + /// Transitionable. + PropertyValue get textHaloColor; + + set textHaloColor(PropertyValue property); + + /// The default value for [textHaloColor]. + static const defaultTextHaloColor = PropertyValue.value( + Color(0x00000000), + ); + + /// Distance of halo to the font outline. Max text halo width is 1/4 of the + /// [textSize]. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Requires [textField]. Supports [featureState] and + /// [interpolate] expressions. Transitionable. + PropertyValue get textHaloWidth; + + set textHaloWidth(PropertyValue property); + + /// The default value for [textHaloWidth]. + static const defaultTextHaloWidth = PropertyValue.value(0); + + /// The halo's fadeout distance towards the outside. + /// + /// Paint property. Optional number in range `[0, ∞)`. Units in pixels. + /// Defaults to `0`. Requires [textField]. Supports [featureState] and + /// [interpolate] expressions. Transitionable. + PropertyValue get textHaloBlur; + + set textHaloBlur(PropertyValue property); + + /// The default value for [textHaloBlur]. + static const defaultTextHaloBlur = PropertyValue.value(0); + + /// Distance that the text's anchor is moved from its original placement. + /// Positive values indicate right and down, while negative values indicate + /// left and up. + /// + /// Paint property. Optional array. Units in pixels. Defaults to [Offset]. + /// Requires [textField]. Supports [interpolate] expressions. Transitionable. + PropertyValue get textTranslate; + + set textTranslate(PropertyValue property); + + /// Default value for [textTranslate]. + static const defaultTextTranslate = PropertyValue.value(Offset.zero); + + /// Controls the frame of reference for [textTranslate]. + /// + /// Paint property. Optional enum. Defaults to [ReferenceSpace.map]. + /// Requires [textField]. Requires [textTranslate]. + PropertyValue get textTranslateAnchor; + + set textTranslateAnchor(PropertyValue property); + + /// Default value for [textTranslateAnchor]. + static const defaultTextTranslateAnchor = PropertyValue.value( + ReferenceSpace.map, + ); +} + +/// {@template symbol-placement} +/// Label placement relative to its geometry. +/// {@endtemplate} +enum SymbolPlacement { + /// The label is placed at the point where the geometry is located. + point('point'), + + /// The label is placed along the line of the geometry. Can only be used on + /// `LineString` and `Polygon` geometries. + line('line'), + + /// The label is placed at the center of the line of the geometry. Can only be + /// used on `LineString` and `Polygon` geometries. Note that a single feature + /// in a vector tile may contain multiple line geometries. + lineCenter('line-center'); + + const SymbolPlacement(this.name); + + /// The MapLibre Style spec compatible name. + final String name; +} + +/// {@template symbol-z-order} +/// Determines whether overlapping symbols in the same layer are rendered in +/// the order that they appear in the data source or by their y-position +/// relative to the viewport. To control the order and prioritization of +/// symbols otherwise, use [SymbolStyleLayer.sortKey]. +/// {@endtemplate} +enum SymbolZOrder { + /// Sorts symbols by [SymbolStyleLayer.sortKey] if set. Otherwise, sorts + /// symbols by their y-position relative to the viewport if + /// [SymbolStyleLayer.iconAllowOverlap] or [SymbolStyleLayer.textAllowOverlap] + /// is set to true or [SymbolStyleLayer.iconIgnorePlacement] or + /// [SymbolStyleLayer.textIgnorePlacement] is false. + auto('auto'), + + /// Sorts symbols by their y-position relative to the viewport if + /// [SymbolStyleLayer.iconAllowOverlap] or [SymbolStyleLayer.textAllowOverlap] + /// is set to true or [SymbolStyleLayer.iconIgnorePlacement] or + /// [SymbolStyleLayer.textIgnorePlacement] is `false`. + viewportY('viewport-y'), + + /// Sorts symbols by [SymbolStyleLayer.sortKey] if set. Otherwise, no sorting + /// is applied; symbols are rendered in the same order as the source data. + source('source'); + + const SymbolZOrder(this.name); + + /// The MapLibre Style spec compatible name. + final String name; +} + +/// {@template icon-overlap} +/// Allows for control over whether to show an icon when it overlaps other +/// symbols on the map. If [SymbolStyleLayer.iconOverlap] is not set, +/// [SymbolStyleLayer.iconAllowOverlap] is used instead. +/// {@endtemplate} +enum SymbolOverlap { + /// The icon will be hidden if it collides with any other previously drawn + /// symbol. + never, + + /// The icon will be visible even if it collides with any other previously + /// drawn symbol. + always, + + /// If the icon collides with another previously drawn symbol, the overlap + /// mode for that symbol is checked. If the previous symbol was placed using + /// never overlap mode, the new icon is hidden. If the previous symbol was + /// placed using [always] or [cooperative] overlap mode, the new icon is + /// visible. + cooperative, +} + +/// {@template icon-rotation-alignment} +/// In combination with [SymbolStyleLayer.placement], determines the +/// rotation behavior of icons. +/// {@endtemplate} +enum IconRotationAlignment { + /// When [SymbolStyleLayer.placement] is set to [SymbolPlacement.point], + /// aligns icons east-west. When [SymbolStyleLayer.placement] is set to + /// [SymbolPlacement.line] or [SymbolPlacement.lineCenter], aligns icon + /// x-axes with the line. + map, + + /// Produces icons whose x-axes are aligned with the x-axis of the viewport, + /// regardless of the value of [SymbolStyleLayer.placement]. + viewport, + + /// When [SymbolStyleLayer.placement] is set to [SymbolPlacement.point], this + /// is equivalent to [viewport]. When [SymbolStyleLayer.placement] is set to + /// [SymbolPlacement.line] or [SymbolPlacement.lineCenter], this is + /// equivalent to [map]. + auto, +} + +/// {@template icon-text-fit} +/// Scales the icon to fit around the associated text. +/// {@endtemplate} +enum IconTextFit { + /// The icon is displayed at its intrinsic aspect ratio. + none, + + /// The icon is scaled in the x-dimension to fit the width of the text. + width, + + /// The icon is scaled in the y-dimension to fit the height of the text. + height, + + /// The icon is scaled in both x- and y-dimensions. + both, +} + +/// {@template icon-anchor} +/// Part of the icon placed closest to the anchor. +/// {@endtemplate} +enum IconAnchor { + /// The center of the icon is placed closest to the anchor. + center('center'), + + /// The left side of the icon is placed closest to the anchor. + left('left'), + + /// The right side of the icon is placed closest to the anchor. + right('right'), + + /// The top of the icon is placed closest to the anchor. + top('top'), + + /// The bottom of the icon is placed closest to the anchor. + bottom('bottom'), + + /// The top left corner of the icon is placed closest to the anchor. + topLeft('top-left'), + + /// The top right corner of the icon is placed closest to the anchor. + topRight('top-right'), + + /// The bottom left corner of the icon is placed closest to the anchor. + bottomLeft('bottom-left'), + + /// The bottom right corner of the icon is placed closest to the anchor. + bottomRight('bottom-right'); + + const IconAnchor(this.name); + + /// The MapLibre Style spec compatible name. + final String name; +} + +/// {@template icon-pitch-alignment} +/// Orientation of icon when map is pitched. +/// {@endtemplate} +enum IconPitchAlignment { + /// The icon is aligned to the plane of the map. + map, + + /// The icon is aligned to the plane of the viewport. + viewport, + + /// Automatically matches the value of + /// [SymbolStyleLayer.iconRotationAlignment]. + auto, +} + +/// {@template text-pitch-alignment} +/// Orientation of icon when map is pitched. +/// {@endtemplate} +enum TextPitchAlignment { + /// The text is aligned to the plane of the map. + map, + + /// The text is aligned to the plane of the viewport. + viewport, + + /// Automatically matches the value of + /// [SymbolStyleLayer.textRotationAlignment]. + auto, +} + +/// {@template text-rotation-alignment} +/// In combination with [SymbolStyleLayer.placement], determines the rotation +/// behavior of the individual glyphs forming the text. +/// {@endtemplate} +enum TextRotationAlignment { + /// When [SymbolStyleLayer.placement] is set to [SymbolPlacement.point], + /// aligns text east-west. When [SymbolStyleLayer.placement] is set to + /// [SymbolPlacement.line] or line-center, aligns text x-axes with the line. + map('map'), + + /// Produces glyphs whose x-axes are aligned with the x-axis of the viewport, + /// regardless of the value of symbol-placement. + viewport('viewport'), + + /// When [SymbolStyleLayer.placement] is set to [SymbolPlacement.point], + /// aligns text to the x-axis of the viewport. When + /// [SymbolStyleLayer.placement] is set to [SymbolPlacement.line] or + /// [SymbolPlacement.lineCenter], aligns glyphs to the x-axis of the + /// viewport and places them along the line. + viewportGlyphs('viewport-glyphs'), + + /// When [SymbolStyleLayer.placement] is set to [SymbolPlacement.point], this + /// is equivalent to [viewport]. When [SymbolStyleLayer.placement] is set to + /// [SymbolPlacement.line] or [SymbolPlacement.lineCenter], this is + /// equivalent to [map]. + auto('auto'); + + const TextRotationAlignment(this.name); + + /// The MapLibre Style spec compatible name. + final String name; +} + +/// {@template text-justify} +/// Text justification options. +/// {@endtemplate} +enum TextJustify { + /// The text is aligned towards the anchor position. + auto, + + /// The text is aligned to the left. + left, + + /// The text is centered. + center, + + /// The text is aligned to the right. + right, +} + +/// {@template text-anchor} +/// Part of the text placed closest to the anchor. +/// {@endtemplate} +enum TextAnchor { + /// The center of the text is placed closest to the anchor. + center('center'), + + /// The left side of the text is placed closest to the anchor. + left('left'), + + /// The right side of the text is placed closest to the anchor. + right('right'), + + /// The top of the text is placed closest to the anchor. + top('top'), + + /// The bottom of the text is placed closest to the anchor. + bottom('bottom'), + + /// The top left corner of the text is placed closest to the anchor. + topLeft('top-left'), + + /// The top right corner of the text is placed closest to the anchor. + topRight('top-right'), + + /// The bottom left corner of the text is placed closest to the anchor. + bottomLeft('bottom-left'), + + /// The bottom right corner of the text is placed closest to the anchor. + bottomRight('bottom-right'); + + const TextAnchor(this.name); + + /// The MapLibre Style spec compatible name. + final String name; +} + +/// {@template text-writing-mode} +/// The property allows control over a symbol's orientation. +/// {@endtemplate} +enum TextWritingMode { + /// If a text's language supports horizontal writing mode, symbols with point + /// placement would be laid out horizontally. + horizontal, + + /// If a text's language supports vertical writing mode, symbols with point + /// placement would be laid out vertically. + vertical, +} + +/// {@template text-transform} +/// Specifies how to capitalize text, similar to the CSS text-transform +/// property. +/// {@endtemplate} +enum TextTransform { + /// No capitalization. Text is rendered as it is provided in the data + /// source. + none, + + /// Forces all letters to be displayed in uppercase. + uppercase, + + /// Forces all letters to be displayed in lowercase. + lowercase, } diff --git a/packages/maplibre_platform_interface/lib/src/style/property_value.dart b/packages/maplibre_platform_interface/lib/src/style/property_value.dart new file mode 100644 index 000000000..e15c95f94 --- /dev/null +++ b/packages/maplibre_platform_interface/lib/src/style/property_value.dart @@ -0,0 +1,44 @@ +import 'package:flutter/painting.dart'; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; + +/// [PropertyValue] is a simple wrapper around a value that can either be a +/// literal value of type [V] or an [Expression] that evaluates to a value of +/// type [V]. +/// +/// It is used so that style properties can be created in a type-safe way. +class PropertyValue { + /// Creates a [PropertyValue] from an [Expression]. + const PropertyValue.expression(Expression expression) + : _object = expression, + isExpression = true; + + /// Creates a [PropertyValue] from a literal value. + const PropertyValue.value(V value) : _object = value, isExpression = false; + + final dynamic _object; + + /// Indicates whether the [PropertyValue] is an expression. + final bool isExpression; + + /// Get the value as type [V]. + V get value => _object as V; + + /// Get the value as an [Expression]. + Expression get expression => _object as Expression; + + /// Converts the [PropertyValue] to a JSON-compatible format. + Object? toJson() => switch (_object) { + final Expression expression => expression.json, + null => null, + final Color color => color.toHexString(), + final Enum object => object.name, + final Offset offset => [offset.dx, offset.dy], + final EdgeInsets edgeInsets => [ + edgeInsets.top, + edgeInsets.right, + edgeInsets.bottom, + edgeInsets.left, + ], + _ => _object, + }; +} diff --git a/packages/maplibre_platform_interface/lib/src/style/style.dart b/packages/maplibre_platform_interface/lib/src/style/style.dart index efc178afd..6ba7aa149 100644 --- a/packages/maplibre_platform_interface/lib/src/style/style.dart +++ b/packages/maplibre_platform_interface/lib/src/style/style.dart @@ -1,2 +1,97 @@ +import 'package:flutter/cupertino.dart'; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; + +export 'expressions/expressions.dart'; export 'layers/style_layer.dart'; export 'sources/source.dart'; + +/// https://maplibre.org/maplibre-style-spec/types/#color +// ignore: camel_case_types +extension type const Color_(String color) {} + +/// https://maplibre.org/maplibre-style-spec/types/#formatted +extension type const Formatted.fromJson(List json) { + /// Creates a [Formatted] from a [List] of sections. + Formatted(Map> sections) + : this.fromJson([ + 'format', + for (final entry in sections.entries) ...[entry.key, entry.value], + ]); +} + +/// A single [double] value, or an [List] of [double] values. +/// +/// ```json +/// { +/// "hillshade-illumination-direction": 24, +/// "hillshade-illumination-direction": [45, 57.3] +/// } +/// ``` +/// +/// https://maplibre.org/maplibre-style-spec/types/#numberarray +extension type const NumberArray._(Object object) { + /// Creates a [NumberArray] from an [List]. + const NumberArray.array(List list) : object = list; + + /// Creates a [NumberArray] from a [double]. + const NumberArray.number(double value) : object = value; + + /// Get the value as [double]. + double get number => object as double; + + /// Get the value as an [List]. + List get array => object as List; + + /// Indicates whether the [NumberArray] is a [List]. + bool get isArray => object is List; +} + +/// A single [Color] value, or an [List] of [Color] values. +/// +/// ```json +/// { +/// "hillshade-highlight-color": "#ffff00", +/// "hillshade-highlight-color": ["#ffff00", "rgb(255, 255, 0)", "yellow"] +/// } +/// ``` +/// +/// https://maplibre.org/maplibre-style-spec/types/#colorarray +extension type const ColorArray._(Object object) { + /// Creates a [ColorArray] from a [List]. + const ColorArray.array(List list) : object = list; + + /// Creates a [ColorArray] from a [Color]. + const ColorArray.color(Color value) : object = value; + + /// Get the value as [Color]. + Color get color => object as Color; + + /// Get the value as an [List]. + List get array => object as List; + + /// Indicates whether the [ColorArray] is a [List]. + bool get isArray => object is List; +} + +/// The interpolation type for [interpolate] and [interpolateHcl]. +extension type const InterpolationType.fromJson(List json) { + /// Interpolates linearly between the pair of stops just less than and just greater than the input. + InterpolationType.linear() : this.fromJson(['linear']); + + /// Interpolates exponentially between the stops just less than and just greater than the input. + /// - [base]: rate at which the output increases in f(x) = x^r. Values higher than 1 increase, close to one behaves linearly, and below one decrease. + InterpolationType.exponential(double base) + : this.fromJson(['exponential', base]); + + /// Interpolates using the cubic bézier curve defined by the given control points. + /// - [x1]: X-coordinate of the first control point. Must be between zero and one for a valid monotonic easing curve. Controls how quickly the curve speeds up at the beginning. + /// - [y1]: Y-coordinate of the first control point. Typically between zero and one, but may exceed this range for overshoot effects. Influences the starting slope of the curve. + /// - [x2]: X-coordinate of the second control point. Must be between zero and one for a valid monotonic easing curve. Controls when the curve begins to decelerate toward the end. + /// - [y2]: Y-coordinate of the second control point. Typically between zero and one, but may exceed this range for overshoot effects. Influences the ending slope of the curve. + InterpolationType.cubicBezier({ + required double x1, + required double y1, + required double x2, + required double y2, + }) : this.fromJson(['cubic-bezier', x1, y1, x2, y2]); +} diff --git a/packages/maplibre_web/lib/src/extensions.dart b/packages/maplibre_web/lib/src/extensions.dart index 7055d3de4..c58d5feac 100644 --- a/packages/maplibre_web/lib/src/extensions.dart +++ b/packages/maplibre_web/lib/src/extensions.dart @@ -46,10 +46,201 @@ extension EdgeInsetsExt on EdgeInsets { /// Internal extension to convert JSAny to Map\ extension StringMap on JSAny? { - /// Like [JSAny?.dartify], but for the special case of a JSON map. + /// Like [dartify], but for the special case of a JSON map. Map? asStringMap() => (dartify() as Map?)?.map( (k, v) => MapEntry(k.toString(), v), ) ?? {}; + + /// Convert a [JSAny] to a [PropertyValue] of type [V] that can be [double], + /// [String], [boolean], [int], or [List]. + PropertyValue? toPropertyValue() { + final property = dartify(); + if (property == null) { + return null; + } else if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } else { + return PropertyValue.value(property as V); + } + } + + /// Convert a [JSAny] to a [PropertyValue] of type [Offset]. + PropertyValue? toOffsetPropertyValue() { + final property = dartify(); + if (property == null) { + return null; + } else if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } else if (property is Map) { + final map = property.map((k, v) => MapEntry(k.toString(), v)); + if (map.containsKey('x') && map.containsKey('y')) { + return PropertyValue.value( + Offset((map['x']! as num).toDouble(), (map['y']! as num).toDouble()), + ); + } else { + throw StateError( + 'Expected a map with keys "x" and "y" for an offset property, but got $property', + ); + } + } + throw StateError( + 'Expected a String, List, or Map for an offset property, but got ${property.runtimeType}', + ); + } + + /// Convert a [JSAny] to a [PropertyValue] of type [Color]. + PropertyValue? toColorPropertyValue() { + final property = dartify(); + if (property == null) { + return null; + } else if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } else if (property is String) { + if (property.startsWith('#')) { + // hex color string + return PropertyValue.value( + Color(int.parse(property.replaceFirst('#', '0xff'))), + ); + } else { + // rgba color string + final regex = RegExp( + r'rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d*\.?\d+))?\)', + ); + final match = regex.firstMatch(property); + if (match != null) { + final r = int.parse(match.group(1)!); + final g = int.parse(match.group(2)!); + final b = int.parse(match.group(3)!); + final a = match.group(4) != null + ? (double.parse(match.group(4)!) * 255).round() + : 255; + return PropertyValue.value(Color.fromARGB(a, r, g, b)); + } else { + throw StateError('Invalid color string: $property'); + } + } + } + throw StateError( + 'Expected a String or List for a color property, but got ${property.runtimeType}', + ); + } + + /// Generic helper to convert a JS value to a [PropertyValue] of an enum type. + /// + /// Supports: + /// - `null` -> `null` + /// - expression JSON array -> `PropertyValue.expression(...)` + /// - string/other -> matched against `enumValues[i].name` + PropertyValue? toEnumPropertyValue(List enumValues) { + final property = dartify(); + if (property == null) return null; + if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } + + final asString = property.toString(); + return PropertyValue.value( + enumValues.firstWhere( + (e) => e.name == asString, + orElse: () => throw StateError('Invalid enum value: $property'), + ), + ); + } + + /// Convert a [JSAny] to a [PropertyValue] of type [NumberArray]. + PropertyValue? toNumberArrayPropertyValue() { + final property = dartify(); + if (property == null) return null; + if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } + if (property is num) { + return PropertyValue.value(NumberArray.number(property.toDouble())); + } + if (property is List) { + final nums = property + .whereType() + .map((e) => e.toDouble()) + .toList(growable: false); + return PropertyValue.value(NumberArray.array(nums)); + } + throw StateError( + 'Expected num, List, or expression for NumberArray, got ${property.runtimeType}', + ); + } + + /// Convert a [JSAny] to a [PropertyValue] of type [EdgeInsets]. + PropertyValue? toEdgeInsetsPropertyValue() { + final property = dartify(); + if (property == null) return null; + if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } + if (property is List) { + final nums = property.whereType().map((e) => e.toDouble()).toList(); + if (nums.length != 4) { + throw StateError( + 'Expected 4 numbers for EdgeInsets [top,right,bottom,left], got $property', + ); + } + return PropertyValue.value( + EdgeInsets.fromLTRB(nums[3], nums[0], nums[1], nums[2]), + ); + } + throw StateError( + 'Expected List or expression for EdgeInsets, got ${property.runtimeType}', + ); + } + + /// Convert a [JSAny] to a [PropertyValue] of `List`. + PropertyValue>? toEnumListPropertyValue( + List enumValues, + ) { + final property = dartify(); + if (property == null) return null; + if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } + if (property is List) { + final result = property + .map( + (e) => enumValues.firstWhere( + (v) => v.name == e.toString(), + orElse: () => throw StateError('Invalid enum value: $e'), + ), + ) + .toList(growable: false); + return PropertyValue.value(result); + } + throw StateError( + 'Expected List or expression for enum list, got ${property.runtimeType}', + ); + } + + /// Convert a [JSAny] to a [PropertyValue] of `Map`. + /// + /// This is used for "text-variable-anchor-offset". + PropertyValue>? toStringOffsetMapPropertyValue() { + final property = dartify(); + if (property == null) return null; + if (property is List && property.firstOrNull is String) { + return PropertyValue.expression(Expression.fromJson(property)); + } + if (property is! List) { + throw StateError( + 'Expected List for OneOf2 list, got ${property.runtimeType}', + ); + } + final values = {}; + for (var i = 0; i < property.length; i += 2) { + final key = property[i] as String; + final item = property[i + 1] as List; + final nums = item.whereType().toList(); + final value = Offset(nums[0].toDouble(), nums[1].toDouble()); + values[key] = value; + } + return PropertyValue.value(values); + } } diff --git a/packages/maplibre_web/lib/src/interop/interop.dart b/packages/maplibre_web/lib/src/interop/interop.dart index a44b9e8fe..60d4f3a92 100644 --- a/packages/maplibre_web/lib/src/interop/interop.dart +++ b/packages/maplibre_web/lib/src/interop/interop.dart @@ -12,7 +12,9 @@ part 'gesture_handlers.dart'; part 'map.dart'; part 'map_geojson_feature.dart'; part 'marker.dart'; -part 'style_specification.dart'; +part 'style/style.dart'; +part 'style/layer.dart'; +part 'style/source.dart'; /// A simple x/y [Point] class for JavaScript. @anonymous diff --git a/packages/maplibre_web/lib/src/interop/map.dart b/packages/maplibre_web/lib/src/interop/map.dart index 05886a63a..08c4a9d67 100644 --- a/packages/maplibre_web/lib/src/interop/map.dart +++ b/packages/maplibre_web/lib/src/interop/map.dart @@ -38,12 +38,12 @@ extension type JsMap._(Camera _) implements Camera { /// https://github.com/maplibre/maplibre-gl-js/blob/41e5b32f5bd6264cbc4a8b38210ec6a410152259/src/ui/map.ts#L1178 external LngLat unproject(Point point); - /// Get the [HTMLElement] of the map. + /// The [HTMLElement] of the map. /// /// https://github.com/maplibre/maplibre-gl-js/blob/41e5b32f5bd6264cbc4a8b38210ec6a410152259/src/ui/map.ts#L2891 external HTMLElement getContainer(); - /// Get the JS canvas of the map. + /// The JS canvas of the map. /// /// https://github.com/maplibre/maplibre-gl-js/blob/41e5b32f5bd6264cbc4a8b38210ec6a410152259/src/ui/map.ts#L2919 external HTMLCanvasElement getCanvas(); @@ -114,7 +114,7 @@ extension type JsMap._(Camera _) implements Camera { /// Get a Source by its id. external SourceSpecification? getSource(String id); - /// Get the loaded style. + /// The loaded style. external StyleSpecification? getStyle(); external DoubleClickZoomHandler doubleClickZoom; @@ -145,6 +145,44 @@ extension type JsMap._(Camera _) implements Camera { JSArray rect, JSAny? options, ); + + /// Sets the value of a paint property in the specified style layer. + /// + /// [layerId] - The ID of the layer to set the paint property in. + /// [name] - The name of the paint property to set. + /// [value] - The value of the paint property to set. + /// * Must be of a type appropriate for the property, as defined in the + /// [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/). + /// * Pass `null` to unset the existing value. + external void setPaintProperty(String layerId, String name, JSAny? value); + + /// Sets the value of a layout property in the specified style layer. + /// + /// [layerId] - The ID of the layer to set the layout property in. + /// [name] - The name of the layout property to set. + /// [value] - The value of the layout property to set. + /// * Must be of a type appropriate for the property, as defined in the + /// [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/). + /// * Pass `null` to unset the existing value. + external void setLayoutProperty(String layerId, String name, JSAny? value); + + /// Get the value of a paint property in the specified style layer. + /// + /// [layerId] - The ID of the layer to get the paint property from. + /// [name] - The name of the paint property to get. + /// Returns the value of the paint property, or `null` if it is not set. + /// + /// https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#getpaintproperty + external JSAny? getPaintProperty(String layerId, String name); + + /// Get the value of a layout property in the specified style layer. + /// + /// [layerId] - The ID of the layer to get the layout property from. + /// [name] - The name of the layout property to get. + /// Returns the value of the layout property, or `null` if it is not set. + /// + /// https://maplibre.org/maplibre-gl-js/docs/API/classes/Map/#getlayoutproperty + external JSAny? getLayoutProperty(String layerId, String name); } /// Anonymous MapOptions for the MapLibre JavaScript [JsMap]. @@ -163,197 +201,6 @@ extension type MapOptions._(JSObject _) implements JSObject { }); } -/// The specifications of map sources. -@anonymous -@JS() -extension type SourceSpecification._(JSObject _) implements JSObject { - /// The default constructor for a [SourceSpecification]. - external SourceSpecification({required String type}); - - /// Create a new GeoJSON source. - external factory SourceSpecification.geoJson({ - required String type, - required JSAny data, - num? maxzoom, - String? attribution, - num? buffer, - }); - - /// Create a new raster DEM source. - factory SourceSpecification.rasterDem({ - required String type, - required int tileSize, - required String? attribution, - String? url, - JSAny? tiles, - }) => url != null - ? SourceSpecification._rasterDemUrl( - type: type, - tileSize: tileSize, - attribution: attribution, - url: url, - ) - : SourceSpecification._rasterDemTiles( - type: type, - tileSize: tileSize, - attribution: attribution, - tiles: tiles!, - ); - - external factory SourceSpecification._rasterDemUrl({ - required String type, - required int tileSize, - required String? attribution, - required String url, - }); - - external factory SourceSpecification._rasterDemTiles({ - required String type, - required int tileSize, - required String? attribution, - required JSAny tiles, - }); - - /// Create a new raster source. - factory SourceSpecification.raster({ - required String type, - required int tileSize, - required String? attribution, - JSAny? tiles, - String? url, - }) => url != null - ? SourceSpecification._rasterUrl( - type: type, - tileSize: tileSize, - attribution: attribution, - url: url, - ) - : SourceSpecification._rasterTiles( - type: type, - tileSize: tileSize, - attribution: attribution, - tiles: tiles!, - ); - - external factory SourceSpecification._rasterUrl({ - required String type, - required int tileSize, - required String? attribution, - required String url, - }); - - external factory SourceSpecification._rasterTiles({ - required String type, - required int tileSize, - required String? attribution, - required JSAny tiles, - }); - - /// Create a new vector source. - external factory SourceSpecification.vector({ - required String type, - required String? url, - }); - - /// Create a new image source. - external factory SourceSpecification.image({ - required String type, - required String url, - required JSAny coordinates, - }); - - /// Create a new video source. - external factory SourceSpecification.video({ - required String type, - required JSAny urls, - required JSAny coordinates, - }); - - /// Used to update the data of a GeoJSON source. - external void setData(JSAny data); -} - -/// The specifications of map layers. -@anonymous -@JS() -extension type LayerSpecification._(JSObject _) implements JSObject { - /// The default constructor for a [LayerSpecification]. - external LayerSpecification({ - required String id, - required String type, - required JSAny layout, - required JSAny paint, - required double? minzoom, - required double? maxzoom, - String? source, - }); - - /// Get the layer id. - external String id; - - external JSAny filter; - @JS('source-layer') - external String? sourceLayer; -} - -/// Image data used by [JsMap.addImage]. -/// -/// https://github.com/maplibre/maplibre-gl-js/blob/42ecfef53c25150227256bb0b5daa29af6efd9e7/src/style/style_image.ts#L92-L93 -@anonymous -@JS() -extension type ImageSpecification._(JSObject _) implements JSObject { - /// Create a new [ImageSpecification] object. - external ImageSpecification({ - required int width, - required int height, - required JSUint8Array data, - }); -} - -/// The style's image metadata -/// -/// https://github.com/maplibre/maplibre-gl-js/blob/42ecfef53c25150227256bb0b5daa29af6efd9e7/src/style/style_image.ts#L58-L59 -@anonymous -@JS() -extension type StyleImageMetadata._(JSObject _) implements JSObject { - /// Create a new [StyleImageMetadata] object. - external StyleImageMetadata({ - /// The ratio of pixels in the image to physical pixels on the screen - double? pixelRatio, - - /// Whether the image should be interpreted as an SDF image - bool? sdf, - }); -} - -/// Projection used by [JsMap.setProjection]. -/// -/// Defaults to mercator. Supports interpolate expressions. -@anonymous -@JS() -extension type ProjectionSpecification._(JSObject _) implements JSObject { - /// Create a new [ProjectionSpecification] object. - external ProjectionSpecification({ - /// The projection definition type. Can be specified as a string, a - /// transition state, or an expression. - required String type, - }); -} - -/// StyleSwapOptions -/// -/// https://github.com/maplibre/maplibre-gl-js/blob/76410880f81de2582be073bc2d730b3f4b8f254d/src/style/style.ts#L159 -@anonymous -@JS() -extension type StyleSwapOptions._(JSObject _) implements JSObject { - /// Create a new [StyleSwapOptions] object. - external StyleSwapOptions({ - /// If false, force a 'full' update, removing the current style - /// and building the given one instead of attempting a diff-based update. - bool? diff, - }); -} - /// QueryRenderedFeaturesOptions /// /// https://maplibre.org/maplibre-gl-js/docs/API/type-aliases/QueryRenderedFeaturesOptions/ diff --git a/packages/maplibre_web/lib/src/interop/style/layer.dart b/packages/maplibre_web/lib/src/interop/style/layer.dart new file mode 100644 index 000000000..7e78fc0f3 --- /dev/null +++ b/packages/maplibre_web/lib/src/interop/style/layer.dart @@ -0,0 +1,97 @@ +part of '../interop.dart'; + +/// The specifications of map layers. +@anonymous +@JS() +extension type LayerSpecification._(JSObject _) implements JSObject { + /// The default constructor for a [LayerSpecification]. + external LayerSpecification({ + required String id, + required String type, + required double? minzoom, + required double? maxzoom, + JSAny? layout, + JSAny? paint, + String? source, + }); + + /// The layer id. + external String id; + + /// The minimum zoom level. + external double? minzoom; + + /// The maximum zoom level. + external double? maxzoom; + + /// The source id. + external String? source; + + external JSAny filter; + @JS('source-layer') + external String? sourceLayer; + + /// The layout properties of the layer. + external JSAny? layout; + + /// The paint properties of the layer. + external JSAny? paint; +} + +/// Image data used by [JsMap.addImage]. +/// +/// https://github.com/maplibre/maplibre-gl-js/blob/42ecfef53c25150227256bb0b5daa29af6efd9e7/src/style/style_image.ts#L92-L93 +@anonymous +@JS() +extension type ImageSpecification._(JSObject _) implements JSObject { + /// Create a new [ImageSpecification] object. + external ImageSpecification({ + required int width, + required int height, + required JSUint8Array data, + }); +} + +/// The style's image metadata +/// +/// https://github.com/maplibre/maplibre-gl-js/blob/42ecfef53c25150227256bb0b5daa29af6efd9e7/src/style/style_image.ts#L58-L59 +@anonymous +@JS() +extension type StyleImageMetadata._(JSObject _) implements JSObject { + /// Create a new [StyleImageMetadata] object. + external StyleImageMetadata({ + /// The ratio of pixels in the image to physical pixels on the screen + double? pixelRatio, + + /// Whether the image should be interpreted as an SDF image + bool? sdf, + }); +} + +/// Projection used by [JsMap.setProjection]. +/// +/// Defaults to mercator. Supports interpolate expressions. +@anonymous +@JS() +extension type ProjectionSpecification._(JSObject _) implements JSObject { + /// Create a new [ProjectionSpecification] object. + external ProjectionSpecification({ + /// The projection definition type. Can be specified as a string, a + /// transition state, or an expression. + required String type, + }); +} + +/// StyleSwapOptions +/// +/// https://github.com/maplibre/maplibre-gl-js/blob/76410880f81de2582be073bc2d730b3f4b8f254d/src/style/style.ts#L159 +@anonymous +@JS() +extension type StyleSwapOptions._(JSObject _) implements JSObject { + /// Create a new [StyleSwapOptions] object. + external StyleSwapOptions({ + /// If false, force a 'full' update, removing the current style + /// and building the given one instead of attempting a diff-based update. + bool? diff, + }); +} diff --git a/packages/maplibre_web/lib/src/interop/style/source.dart b/packages/maplibre_web/lib/src/interop/style/source.dart new file mode 100644 index 000000000..13843fcdf --- /dev/null +++ b/packages/maplibre_web/lib/src/interop/style/source.dart @@ -0,0 +1,111 @@ +part of '../interop.dart'; + +/// The specifications of map sources. +@anonymous +@JS() +extension type SourceSpecification._(JSObject _) implements JSObject { + /// The default constructor for a [SourceSpecification]. + external SourceSpecification({required String type}); + + /// Create a new GeoJSON source. + external factory SourceSpecification.geoJson({ + required String type, + required JSAny data, + num? maxzoom, + String? attribution, + num? buffer, + }); + + /// Create a new raster DEM source. + factory SourceSpecification.rasterDem({ + required String type, + required int tileSize, + required String? attribution, + String? url, + JSAny? tiles, + }) => url != null + ? SourceSpecification._rasterDemUrl( + type: type, + tileSize: tileSize, + attribution: attribution, + url: url, + ) + : SourceSpecification._rasterDemTiles( + type: type, + tileSize: tileSize, + attribution: attribution, + tiles: tiles!, + ); + + external factory SourceSpecification._rasterDemUrl({ + required String type, + required int tileSize, + required String? attribution, + required String url, + }); + + external factory SourceSpecification._rasterDemTiles({ + required String type, + required int tileSize, + required String? attribution, + required JSAny tiles, + }); + + /// Create a new raster source. + factory SourceSpecification.raster({ + required String type, + required int tileSize, + required String? attribution, + JSAny? tiles, + String? url, + }) => url != null + ? SourceSpecification._rasterUrl( + type: type, + tileSize: tileSize, + attribution: attribution, + url: url, + ) + : SourceSpecification._rasterTiles( + type: type, + tileSize: tileSize, + attribution: attribution, + tiles: tiles!, + ); + + external factory SourceSpecification._rasterUrl({ + required String type, + required int tileSize, + required String? attribution, + required String url, + }); + + external factory SourceSpecification._rasterTiles({ + required String type, + required int tileSize, + required String? attribution, + required JSAny tiles, + }); + + /// Create a new vector source. + external factory SourceSpecification.vector({ + required String type, + required String? url, + }); + + /// Create a new image source. + external factory SourceSpecification.image({ + required String type, + required String url, + required JSAny coordinates, + }); + + /// Create a new video source. + external factory SourceSpecification.video({ + required String type, + required JSAny urls, + required JSAny coordinates, + }); + + /// Used to update the data of a GeoJSON source. + external void setData(JSAny data); +} diff --git a/packages/maplibre_web/lib/src/interop/style_specification.dart b/packages/maplibre_web/lib/src/interop/style/style.dart similarity index 88% rename from packages/maplibre_web/lib/src/interop/style_specification.dart rename to packages/maplibre_web/lib/src/interop/style/style.dart index f13be3abd..16dd9870f 100644 --- a/packages/maplibre_web/lib/src/interop/style_specification.dart +++ b/packages/maplibre_web/lib/src/interop/style/style.dart @@ -1,4 +1,4 @@ -part of 'interop.dart'; +part of '../interop.dart'; /// https://github.com/maplibre/maplibre-gl-js/blob/main/src/ui/map.ts#L1917 extension type StyleSpecification._(JSObject _) implements JSObject { diff --git a/packages/maplibre_web/lib/src/map_state.dart b/packages/maplibre_web/lib/src/map_state.dart index d02fb7446..71b8121a4 100644 --- a/packages/maplibre_web/lib/src/map_state.dart +++ b/packages/maplibre_web/lib/src/map_state.dart @@ -12,6 +12,7 @@ import 'package:maplibre_web/src/extensions.dart'; import 'package:maplibre_web/src/interop/interop.dart' as interop; import 'package:maplibre_web/src/interop/json.dart'; import 'package:maplibre_web/src/interop/pmtiles.dart' as pmtiles; +import 'package:maplibre_web/src/style/layers/style_layer.dart'; import 'package:web/web.dart'; part 'style_controller.dart'; diff --git a/packages/maplibre_web/lib/src/maplibre_plugin.dart b/packages/maplibre_web/lib/src/maplibre_plugin.dart index 65817e405..11ab4ef8d 100644 --- a/packages/maplibre_web/lib/src/maplibre_plugin.dart +++ b/packages/maplibre_web/lib/src/maplibre_plugin.dart @@ -1,6 +1,8 @@ +import 'package:flutter/painting.dart'; import 'package:flutter_web_plugins/flutter_web_plugins.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:maplibre_web/src/map_state.dart'; +import 'package:maplibre_web/src/style/layers/style_layer.dart'; /// Web implementation of the federated MapLibre plugin. final class MapLibrePlugin extends MapLibrePlatform { @@ -19,4 +21,443 @@ final class MapLibrePlugin extends MapLibrePlatform { PermissionManager createPermissionManager() { throw Exception('The PermissionManager can not be used on web.'); } + + /// Create a platform specific [BackgroundStyleLayer] object. + @override + BackgroundStyleLayer createBackgroundStyleLayer({ + required String id, + required bool visible, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => BackgroundStyleLayerWeb( + id: id, + visible: visible, + color: color, + pattern: pattern, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [CircleStyleLayer] object. + @override + CircleStyleLayer createCircleStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => CircleStyleLayerWeb( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + translate: translate, + translateAnchor: translateAnchor, + sortKey: sortKey, + radius: radius, + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + /// Create a platform specific [ColorReliefStyleLayer] object. + @override + ColorReliefStyleLayer createColorReliefStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => ColorReliefStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + color: color, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [FillExtrusionStyleLayer] object. + @override + FillExtrusionStyleLayer createFillExtrusionStyleLayer({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => FillExtrusionStyleLayerWeb( + id: id, + sourceId: sourceId, + minZoom: minZoom, + maxZoom: maxZoom, + visible: visible, + filter: filter, + sourceLayerId: sourceLayerId, + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + /// Create a platform specific [FillStyleLayer] object. + @override + FillStyleLayer createFillStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) => FillStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + ); + + /// Create a platform specific [HeatmapStyleLayer] object. + @override + HeatmapStyleLayer createHeatmapStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => HeatmapStyleLayerWeb( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + /// Create a platform specific [HillshadeStyleLayer] object. + @override + HillshadeStyleLayer createHillshadeStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) => HillshadeStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + illuminationDirection: illuminationDirection, + illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + method: method, + ); + + /// Create a platform specific [LineStyleLayer] object. + @override + LineStyleLayer createLineStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => LineStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + opacity: opacity, + color: color, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + cap: cap, + ); + + /// Create a platform specific [RasterStyleLayer] object. + @override + RasterStyleLayer createRasterStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => RasterStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + /// Create a platform specific [SymbolStyleLayer] object. + @override + SymbolStyleLayer createSymbolStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? + textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) => SymbolStyleLayerWeb( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); } diff --git a/packages/maplibre_web/lib/src/style/layers/background_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/background_style_layer.dart new file mode 100644 index 000000000..07fc12e8e --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/background_style_layer.dart @@ -0,0 +1,67 @@ +part of 'style_layer.dart'; + +/// Web implementation of [BackgroundStyleLayer]. +class BackgroundStyleLayerWeb extends StyleLayerWeb + implements BackgroundStyleLayer { + /// Default constructor for a [BackgroundStyleLayerWeb] instance. + BackgroundStyleLayerWeb({ + required String id, + required double minZoom, + required double maxZoom, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required bool visible, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'background', + maxzoom: maxZoom, + minzoom: minZoom, + layout: createBackgroundLayout(visible: visible).jsify(), + paint: createBackgroundPaint( + color: color, + pattern: pattern, + opacity: opacity, + ).jsify(), + ), + ); + + @override + PropertyValue get color => + requireMap + .getPaintProperty(id, 'background-color') + .toPropertyValue() ?? + BackgroundStyleLayer.defaultColor; + + @override + set color(PropertyValue value) { + requireMap.setPaintProperty(id, 'background-color', value.toJson().jsify()); + } + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'background-opacity') + .toPropertyValue() ?? + BackgroundStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue value) => requireMap.setPaintProperty( + id, + 'background-opacity', + value.toJson().jsify(), + ); + + @override + PropertyValue? get pattern => requireMap + .getPaintProperty(id, 'background-pattern') + .toPropertyValue(); + + @override + set pattern(PropertyValue? value) => requireMap.setPaintProperty( + id, + 'background-pattern', + value?.toJson().jsify(), + ); +} diff --git a/packages/maplibre_web/lib/src/style/layers/circle_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/circle_style_layer.dart new file mode 100644 index 000000000..3d4cd7df5 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/circle_style_layer.dart @@ -0,0 +1,214 @@ +part of 'style_layer.dart'; + +/// Web implementation of [CircleStyleLayer]. +class CircleStyleLayerWeb extends StyleLayerWeb implements CircleStyleLayer { + /// Default constructor for a [CircleStyleLayerWeb] instance. + CircleStyleLayerWeb({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'circle', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createCircleLayout( + visible: visible, + sortKey: sortKey, + ).jsify(), + paint: createCirclePaint( + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + radius: radius, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + translate: translate, + translateAnchor: translateAnchor, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get blur => + requireMap + .getPaintProperty(id, 'circle-blur') + .toPropertyValue() ?? + CircleStyleLayer.defaultBlur; + + @override + set blur(PropertyValue value) => + requireMap.setPaintProperty(id, 'circle-blur', value.toJson().jsify()); + + @override + PropertyValue get color => + requireMap.getPaintProperty(id, 'circle-color').toColorPropertyValue() ?? + CircleStyleLayer.defaultColor; + + @override + set color(PropertyValue value) => + requireMap.setPaintProperty(id, 'circle-color', value.toJson().jsify()); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression value) => jsLayer.filter = value.json.jsify()!; + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'circle-opacity') + .toPropertyValue() ?? + CircleStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue value) => + requireMap.setPaintProperty(id, 'circle-opacity', value.toJson().jsify()); + + @override + PropertyValue get pitchAlignment => + requireMap + .getPaintProperty(id, 'circle-pitch-alignment') + .toEnumPropertyValue(ReferenceSpace.values) ?? + CircleStyleLayer.defaultPitchAlignment; + + @override + set pitchAlignment(PropertyValue value) => requireMap + .setPaintProperty(id, 'circle-pitch-alignment', value.toJson().jsify()); + + @override + PropertyValue get pitchScale => + requireMap + .getPaintProperty(id, 'circle-pitch-scale') + .toEnumPropertyValue(ReferenceSpace.values) ?? + CircleStyleLayer.defaultPitchScale; + + @override + set pitchScale(PropertyValue value) => requireMap + .setPaintProperty(id, 'circle-pitch-scale', value.toJson().jsify()); + + @override + PropertyValue get radius => + requireMap + .getPaintProperty(id, 'circle-radius') + .toPropertyValue() ?? + CircleStyleLayer.defaultRadius; + + @override + set radius(PropertyValue value) => + requireMap.setPaintProperty(id, 'circle-radius', value.toJson().jsify()); + + @override + PropertyValue? get sortKey => requireMap + .getLayoutProperty(id, 'circle-sort-key') + .toPropertyValue(); + + @override + set sortKey(PropertyValue? value) => requireMap.setLayoutProperty( + id, + 'circle-sort-key', + value?.toJson().jsify(), + ); + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + PropertyValue get strokeColor => + requireMap + .getPaintProperty(id, 'circle-stroke-color') + .toColorPropertyValue() ?? + CircleStyleLayer.defaultStrokeColor; + + @override + set strokeColor(PropertyValue value) => requireMap.setPaintProperty( + id, + 'circle-stroke-color', + value.toJson().jsify(), + ); + + @override + PropertyValue get strokeOpacity => + requireMap + .getPaintProperty(id, 'circle-stroke-opacity') + .toPropertyValue() ?? + CircleStyleLayer.defaultStrokeOpacity; + + @override + set strokeOpacity(PropertyValue value) => requireMap.setPaintProperty( + id, + 'circle-stroke-opacity', + value.toJson().jsify(), + ); + + @override + PropertyValue get strokeWidth => + requireMap + .getPaintProperty(id, 'circle-stroke-width') + .toPropertyValue() ?? + CircleStyleLayer.defaultStrokeWidth; + + @override + set strokeWidth(PropertyValue value) => requireMap.setPaintProperty( + id, + 'circle-stroke-width', + value.toJson().jsify(), + ); + + @override + PropertyValue get translate => + requireMap + .getPaintProperty(id, 'circle-translate') + .toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue value) => requireMap.setPaintProperty( + id, + 'circle-translate', + value.toJson().jsify(), + ); + + @override + PropertyValue get translateAnchor => + requireMap + .getPaintProperty(id, 'circle-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue value) => requireMap + .setPaintProperty(id, 'circle-translate-anchor', value.toJson().jsify()); + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/color_relief_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/color_relief_style_layer.dart new file mode 100644 index 000000000..8f7555e67 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/color_relief_style_layer.dart @@ -0,0 +1,58 @@ +part of 'style_layer.dart'; + +/// Web implementation of [ColorReliefStyleLayer]. +class ColorReliefStyleLayerWeb extends StyleLayerWeb + implements ColorReliefStyleLayer { + /// Default constructor for a [ColorReliefStyleLayerWeb] instance. + ColorReliefStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'color-relief', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createColorReliefLayout(visible: visible).jsify(), + paint: createColorReliefPaint( + color: color, + opacity: opacity, + ).jsify(), + ), + ); + + @override + PropertyValue? get color => requireMap + .getPaintProperty(id, 'color-relief-color') + .toColorPropertyValue(); + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'color-relief-opacity') + .toPropertyValue() ?? + ColorReliefStyleLayer.defaultOpacity; + + @override + String get sourceId => jsLayer.sourceLayer!; + + @override + set color(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'color-relief-color', + property?.toJson().jsify(), + ); + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'color-relief-opacity', + property.toJson().jsify(), + ); +} diff --git a/packages/maplibre_web/lib/src/style/layers/fill_extrusion_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/fill_extrusion_style_layer.dart new file mode 100644 index 000000000..3a0024b46 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/fill_extrusion_style_layer.dart @@ -0,0 +1,174 @@ +part of 'style_layer.dart'; + +/// Web implementation of [FillExtrusionStyleLayer]. +class FillExtrusionStyleLayerWeb extends StyleLayerWeb + implements FillExtrusionStyleLayer { + /// Default constructor for a [FillExtrusionStyleLayerWeb] instance. + FillExtrusionStyleLayerWeb({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'fill-extrusion', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createFillExtrusionLayout(visible: visible).jsify(), + paint: createFillExtrusionPaint( + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'fill-extrusion-opacity') + .toPropertyValue() ?? + FillExtrusionStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-opacity', + property.toJson().jsify(), + ); + + @override + PropertyValue get color => + requireMap + .getPaintProperty(id, 'fill-extrusion-color') + .toColorPropertyValue() ?? + FillExtrusionStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-color', + property.toJson().jsify(), + ); + + @override + PropertyValue? get pattern => requireMap + .getPaintProperty(id, 'fill-extrusion-pattern') + .toPropertyValue(); + + @override + set pattern(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-pattern', + property?.toJson().jsify(), + ); + + @override + PropertyValue get height => + requireMap + .getPaintProperty(id, 'fill-extrusion-height') + .toPropertyValue() ?? + FillExtrusionStyleLayer.defaultHeight; + + @override + set height(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-height', + property.toJson().jsify(), + ); + + @override + PropertyValue get base => + requireMap + .getPaintProperty(id, 'fill-extrusion-base') + .toPropertyValue() ?? + FillExtrusionStyleLayer.defaultBase; + + @override + set base(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-base', + property.toJson().jsify(), + ); + + @override + PropertyValue get verticalGradient => + requireMap + .getPaintProperty(id, 'fill-extrusion-vertical-gradient') + .toPropertyValue() ?? + FillExtrusionStyleLayer.defaultVerticalGradient; + + @override + set verticalGradient(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'fill-extrusion-vertical-gradient', + property.toJson().jsify(), + ); + + @override + PropertyValue get translate => + requireMap + .getPaintProperty(id, 'fill-extrusion-translate') + .toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-extrusion-translate', + property.toJson().jsify(), + ); + + @override + PropertyValue get translateAnchor => + requireMap + .getPaintProperty(id, 'fill-extrusion-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'fill-extrusion-translate-anchor', + property.toJson().jsify(), + ); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression value) => jsLayer.filter = value.json.jsify()!; + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/fill_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/fill_style_layer.dart new file mode 100644 index 000000000..4cd9d2e9c --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/fill_style_layer.dart @@ -0,0 +1,153 @@ +part of 'style_layer.dart'; + +/// Web implementation of [FillStyleLayer]. +class FillStyleLayerWeb extends StyleLayerWeb implements FillStyleLayer { + /// Default constructor for a [FillStyleLayerWeb] instance. + FillStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'fill', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createFillLayout(visible: visible, sortKey: sortKey).jsify(), + paint: createFillPaint( + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + translate: translate, + translateAnchor: translateAnchor, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get antialias => + requireMap + .getPaintProperty(id, 'fill-antialias') + .toPropertyValue() ?? + FillStyleLayer.defaultAntialias; + + @override + set antialias(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-antialias', + property.toJson().jsify(), + ); + + @override + PropertyValue get color => + requireMap.getPaintProperty(id, 'fill-color').toColorPropertyValue() ?? + FillStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => + requireMap.setPaintProperty(id, 'fill-color', property.toJson().jsify()); + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'fill-opacity') + .toPropertyValue() ?? + FillStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'fill-opacity', + property.toJson().jsify(), + ); + + @override + PropertyValue get outlineColor => + requireMap + .getPaintProperty(id, 'fill-outline-color') + .toColorPropertyValue() ?? + FillStyleLayer.defaultOutlineColor; + + @override + set outlineColor(PropertyValue property) => requireMap + .setPaintProperty(id, 'fill-outline-color', property.toJson().jsify()); + + @override + PropertyValue? get pattern => + requireMap.getPaintProperty(id, 'fill-pattern').toPropertyValue(); + + @override + set pattern(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'fill-pattern', + property?.toJson().jsify(), + ); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression? value) => jsLayer.filter = value!.json.jsify()!; + + @override + PropertyValue? get sortKey => requireMap + .getLayoutProperty(id, 'fill-sort-key') + .toPropertyValue(); + + @override + set sortKey(PropertyValue? value) => requireMap.setLayoutProperty( + id, + 'fill-sort-key', + value?.toJson().jsify(), + ); + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + PropertyValue get translate => + requireMap + .getPaintProperty(id, 'fill-translate') + .toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue value) => + requireMap.setPaintProperty(id, 'fill-translate', value.toJson().jsify()); + + @override + PropertyValue get translateAnchor => + requireMap + .getPaintProperty(id, 'fill-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue value) => requireMap + .setPaintProperty(id, 'fill-translate-anchor', value.toJson().jsify()); + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/heatmap_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/heatmap_style_layer.dart new file mode 100644 index 000000000..ca39fce02 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/heatmap_style_layer.dart @@ -0,0 +1,122 @@ +part of 'style_layer.dart'; + +/// Web implementation of [HeatmapStyleLayer]. +class HeatmapStyleLayerWeb extends StyleLayerWeb implements HeatmapStyleLayer { + /// Default constructor for a [HeatmapStyleLayerWeb] instance. + HeatmapStyleLayerWeb({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'heatmap', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createHeatmapLayout(visible: visible).jsify(), + paint: createHeatmapPaint( + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get radius => + requireMap + .getPaintProperty(id, 'heatmap-radius') + .toPropertyValue() ?? + HeatmapStyleLayer.defaultRadius; + + @override + set radius(PropertyValue property) => requireMap.setPaintProperty( + id, + 'heatmap-radius', + property.toJson().jsify(), + ); + + @override + PropertyValue get weight => + requireMap + .getPaintProperty(id, 'heatmap-weight') + .toPropertyValue() ?? + HeatmapStyleLayer.defaultWeight; + + @override + set weight(PropertyValue property) => requireMap.setPaintProperty( + id, + 'heatmap-weight', + property.toJson().jsify(), + ); + + @override + PropertyValue get intensity => + requireMap + .getPaintProperty(id, 'heatmap-intensity') + .toPropertyValue() ?? + HeatmapStyleLayer.defaultIntensity; + + @override + set intensity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'heatmap-intensity', + property.toJson().jsify(), + ); + + @override + PropertyValue? get color => + requireMap.getPaintProperty(id, 'heatmap-color').toColorPropertyValue(); + + @override + set color(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'heatmap-color', + property?.toJson().jsify(), + ); + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'heatmap-opacity') + .toPropertyValue() ?? + HeatmapStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'heatmap-opacity', + property.toJson().jsify(), + ); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression? value) => jsLayer.filter = value!.json.jsify()!; + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/hillshade_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/hillshade_style_layer.dart new file mode 100644 index 000000000..3ff95e299 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/hillshade_style_layer.dart @@ -0,0 +1,158 @@ +part of 'style_layer.dart'; + +/// Web implementation of [HillshadeStyleLayer]. +class HillshadeStyleLayerWeb extends StyleLayerWeb + implements HillshadeStyleLayer { + /// Default constructor for a [HillshadeStyleLayerWeb] instance. + HillshadeStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'hillshade', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createHillshadeLayout(visible: visible).jsify(), + paint: createHillshadePaint( + illuminationDirection: illuminationDirection, + illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + method: method, + ).jsify(), + ), + ); + + @override + PropertyValue get illuminationDirection => + requireMap + .getPaintProperty(id, 'hillshade-illumination-direction') + .toNumberArrayPropertyValue() ?? + HillshadeStyleLayer.defaultIlluminationDirection; + + @override + set illuminationDirection(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'hillshade-illumination-direction', + property.toJson().jsify(), + ); + + @override + PropertyValue get illuminationAltitude => + requireMap + .getPaintProperty(id, 'hillshade-illumination-altitude') + .toNumberArrayPropertyValue() ?? + HillshadeStyleLayer.defaultIlluminationAltitude; + + @override + set illuminationAltitude(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'hillshade-illumination-altitude', + property.toJson().jsify(), + ); + + @override + PropertyValue get illuminationAnchor => + requireMap + .getPaintProperty(id, 'hillshade-illumination-anchor') + .toEnumPropertyValue(IlluminationAnchor.values) ?? + HillshadeStyleLayer.defaultIlluminationAnchor; + + @override + set illuminationAnchor(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'hillshade-illumination-anchor', + property.toJson().jsify(), + ); + + @override + PropertyValue get exaggeration => + requireMap + .getPaintProperty(id, 'hillshade-exaggeration') + .toPropertyValue() ?? + HillshadeStyleLayer.defaultExaggeration; + + @override + set exaggeration(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'hillshade-exaggeration', + property.toJson().jsify(), + ); + + @override + PropertyValue get shadowColor => + requireMap + .getPaintProperty(id, 'hillshade-shadow-color') + .toColorPropertyValue() ?? + HillshadeStyleLayer.defaultShadowColor; + + @override + set shadowColor(PropertyValue property) => requireMap.setPaintProperty( + id, + 'hillshade-shadow-color', + property.toJson().jsify(), + ); + + @override + PropertyValue get highlightColor => + requireMap + .getPaintProperty(id, 'hillshade-highlight-color') + .toColorPropertyValue() ?? + HillshadeStyleLayer.defaultHighlightColor; + + @override + set highlightColor(PropertyValue property) => + requireMap.setPaintProperty( + id, + 'hillshade-highlight-color', + property.toJson().jsify(), + ); + + @override + PropertyValue get accentColor => + requireMap + .getPaintProperty(id, 'hillshade-accent-color') + .toColorPropertyValue() ?? + HillshadeStyleLayer.defaultAccentColor; + + @override + set accentColor(PropertyValue property) => requireMap.setPaintProperty( + id, + 'hillshade-accent-color', + property.toJson().jsify(), + ); + + @override + PropertyValue get method => + requireMap + .getPaintProperty(id, 'hillshade-method') + .toEnumPropertyValue(HillshadeMethod.values) ?? + HillshadeStyleLayer.defaultMethod; + + @override + set method(PropertyValue property) => requireMap + .setPaintProperty(id, 'hillshade-method', property.toJson().jsify()); + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/line_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/line_style_layer.dart new file mode 100644 index 000000000..b65254cd2 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/line_style_layer.dart @@ -0,0 +1,254 @@ +part of 'style_layer.dart'; + +/// Web implementation of [LineStyleLayer]. +class LineStyleLayerWeb extends StyleLayerWeb implements LineStyleLayer { + /// Default constructor for a [LineStyleLayerWeb] instance. + LineStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'line', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createLineLayout( + visible: visible, + cap: cap, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + sortKey: sortKey, + ).jsify(), + paint: createLinePaint( + opacity: opacity, + color: color, + translate: translate, + translateAnchor: translateAnchor, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get cap => + requireMap + .getLayoutProperty(id, 'line-cap') + .toEnumPropertyValue(LineCap.values) ?? + LineStyleLayer.defaultCap; + + @override + set cap(PropertyValue property) => + requireMap.setLayoutProperty(id, 'line-cap', property.toJson().jsify()); + + @override + PropertyValue get join => + requireMap + .getLayoutProperty(id, 'line-join') + .toEnumPropertyValue(LineJoin.values) ?? + LineStyleLayer.defaultJoin; + + @override + set join(PropertyValue property) => + requireMap.setLayoutProperty(id, 'line-join', property.toJson().jsify()); + + @override + PropertyValue get miterLimit => + requireMap + .getLayoutProperty(id, 'line-miter-limit') + .toPropertyValue() ?? + LineStyleLayer.defaultMiterLimit; + + @override + set miterLimit(PropertyValue property) => requireMap + .setLayoutProperty(id, 'line-miter-limit', property.toJson().jsify()); + + @override + PropertyValue get roundLimit => + requireMap + .getLayoutProperty(id, 'line-round-limit') + .toPropertyValue() ?? + LineStyleLayer.defaultRoundLimit; + + @override + set roundLimit(PropertyValue property) => requireMap + .setLayoutProperty(id, 'line-round-limit', property.toJson().jsify()); + + @override + PropertyValue? get sortKey => requireMap + .getLayoutProperty(id, 'line-sort-key') + .toPropertyValue(); + + @override + set sortKey(PropertyValue? value) => requireMap.setLayoutProperty( + id, + 'line-sort-key', + value?.toJson().jsify(), + ); + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'line-opacity') + .toPropertyValue() ?? + LineStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'line-opacity', + property.toJson().jsify(), + ); + + @override + PropertyValue get color => + requireMap.getPaintProperty(id, 'line-color').toColorPropertyValue() ?? + LineStyleLayer.defaultColor; + + @override + set color(PropertyValue property) => + requireMap.setPaintProperty(id, 'line-color', property.toJson().jsify()); + + @override + PropertyValue get width => + requireMap.getPaintProperty(id, 'line-width').toPropertyValue() ?? + LineStyleLayer.defaultWidth; + + @override + set width(PropertyValue property) => + requireMap.setPaintProperty(id, 'line-width', property.toJson().jsify()); + + @override + PropertyValue get gapWidth => + requireMap + .getPaintProperty(id, 'line-gap-width') + .toPropertyValue() ?? + LineStyleLayer.defaultGapWidth; + + @override + set gapWidth(PropertyValue property) => requireMap.setPaintProperty( + id, + 'line-gap-width', + property.toJson().jsify(), + ); + + @override + PropertyValue get offset => + requireMap + .getPaintProperty(id, 'line-offset') + .toPropertyValue() ?? + LineStyleLayer.defaultOffset; + + @override + set offset(PropertyValue property) => + requireMap.setPaintProperty(id, 'line-offset', property.toJson().jsify()); + + @override + PropertyValue get blur => + requireMap.getPaintProperty(id, 'line-blur').toPropertyValue() ?? + LineStyleLayer.defaultBlur; + + @override + set blur(PropertyValue property) => + requireMap.setPaintProperty(id, 'line-blur', property.toJson().jsify()); + + @override + PropertyValue>? get dashArray => requireMap + .getPaintProperty(id, 'line-dasharray') + .toPropertyValue>(); + + @override + set dashArray(PropertyValue>? property) => requireMap + .setPaintProperty(id, 'line-dasharray', property?.toJson().jsify()); + + @override + PropertyValue? get pattern => + requireMap.getPaintProperty(id, 'line-pattern').toPropertyValue(); + + @override + set pattern(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'line-pattern', + property?.toJson().jsify(), + ); + + @override + PropertyValue? get gradient => + requireMap.getPaintProperty(id, 'line-gradient').toColorPropertyValue(); + + @override + set gradient(PropertyValue? property) => requireMap.setPaintProperty( + id, + 'line-gradient', + property?.toJson().jsify(), + ); + + @override + PropertyValue get translate => + requireMap + .getPaintProperty(id, 'line-translate') + .toOffsetPropertyValue() ?? + StyleLayerWithTranslate.defaultTranslate; + + @override + set translate(PropertyValue value) => + requireMap.setPaintProperty(id, 'line-translate', value.toJson().jsify()); + + @override + PropertyValue get translateAnchor => + requireMap + .getPaintProperty(id, 'line-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + StyleLayerWithTranslate.defaultTranslateAnchor; + + @override + set translateAnchor(PropertyValue value) => requireMap + .setPaintProperty(id, 'line-translate-anchor', value.toJson().jsify()); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression? value) => jsLayer.filter = value!.json.jsify()!; + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/raster_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/raster_style_layer.dart new file mode 100644 index 000000000..b53dcb253 --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/raster_style_layer.dart @@ -0,0 +1,143 @@ +part of 'style_layer.dart'; + +/// Web implementation of [RasterStyleLayer]. +class RasterStyleLayerWeb extends StyleLayerWeb implements RasterStyleLayer { + /// Default constructor for a [RasterStyleLayerWeb] instance. + RasterStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'raster', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createRasterLayout(visible: visible).jsify(), + paint: createRasterPaint( + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ).jsify(), + ), + ); + + @override + PropertyValue get opacity => + requireMap + .getPaintProperty(id, 'raster-opacity') + .toPropertyValue() ?? + RasterStyleLayer.defaultOpacity; + + @override + set opacity(PropertyValue property) => requireMap.setPaintProperty( + id, + 'raster-opacity', + property.toJson().jsify(), + ); + + @override + PropertyValue get hueRotate => + requireMap + .getPaintProperty(id, 'raster-hue-rotate') + .toPropertyValue() ?? + RasterStyleLayer.defaultHueRotate; + + @override + set hueRotate(PropertyValue property) => requireMap.setPaintProperty( + id, + 'raster-hue-rotate', + property.toJson().jsify(), + ); + + @override + PropertyValue get brightnessMin => + requireMap + .getPaintProperty(id, 'raster-brightness-min') + .toPropertyValue() ?? + RasterStyleLayer.defaultBrightnessMin; + + @override + set brightnessMin(PropertyValue property) => requireMap + .setPaintProperty(id, 'raster-brightness-min', property.toJson().jsify()); + + @override + PropertyValue get brightnessMax => + requireMap + .getPaintProperty(id, 'raster-brightness-max') + .toPropertyValue() ?? + RasterStyleLayer.defaultBrightnessMax; + + @override + set brightnessMax(PropertyValue property) => requireMap + .setPaintProperty(id, 'raster-brightness-max', property.toJson().jsify()); + + @override + PropertyValue get saturation => + requireMap + .getPaintProperty(id, 'raster-saturation') + .toPropertyValue() ?? + RasterStyleLayer.defaultSaturation; + + @override + set saturation(PropertyValue property) => requireMap.setPaintProperty( + id, + 'raster-saturation', + property.toJson().jsify(), + ); + + @override + PropertyValue get contrast => + requireMap + .getPaintProperty(id, 'raster-contrast') + .toPropertyValue() ?? + RasterStyleLayer.defaultContrast; + + @override + set contrast(PropertyValue property) => requireMap.setPaintProperty( + id, + 'raster-contrast', + property.toJson().jsify(), + ); + + @override + PropertyValue get resampling => + requireMap + .getPaintProperty(id, 'raster-resampling') + .toEnumPropertyValue(RasterResampling.values) ?? + RasterStyleLayer.defaultResampling; + + @override + set resampling(PropertyValue property) => requireMap + .setPaintProperty(id, 'raster-resampling', property.toJson().jsify()); + + @override + PropertyValue get fadeDuration => + requireMap + .getPaintProperty(id, 'raster-fade-duration') + .toPropertyValue() ?? + RasterStyleLayer.defaultFadeDuration; + + @override + set fadeDuration(PropertyValue property) => requireMap + .setPaintProperty(id, 'raster-fade-duration', property.toJson().jsify()); + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style/layers/style_layer.dart b/packages/maplibre_web/lib/src/style/layers/style_layer.dart new file mode 100644 index 000000000..ac48baf5e --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/style_layer.dart @@ -0,0 +1,59 @@ +import 'dart:js_interop'; + +import 'package:flutter/painting.dart'; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; +import 'package:maplibre_web/src/extensions.dart'; +import 'package:maplibre_web/src/interop/interop.dart' as js; + +part 'background_style_layer.dart'; +part 'circle_style_layer.dart'; +part 'color_relief_style_layer.dart'; +part 'fill_extrusion_style_layer.dart'; +part 'fill_style_layer.dart'; +part 'heatmap_style_layer.dart'; +part 'hillshade_style_layer.dart'; +part 'line_style_layer.dart'; +part 'raster_style_layer.dart'; +part 'symbol_style_layer.dart'; + +/// Web implementation of [StyleLayer]. +abstract class StyleLayerWeb implements StyleLayer { + /// Construct an [StyleLayerWeb] from a JNI layer. + StyleLayerWeb.fromNativeLayer({required this.jsLayer, this.jsMap}); + + /// The JavaScript layer instance. + final js.LayerSpecification jsLayer; + + /// The JavaScript map instance, if attached to a map. + js.JsMap? jsMap; + + /// Gets the JavaScript map instance, throwing if not attached to a map. + js.JsMap get requireMap => + jsMap ?? (throw StateError('Map is not attached to the layer')); + + @override + String get id => jsLayer.id; + + @override + double get maxZoom => jsLayer.maxzoom ?? StyleLayer.defaultMaxZoom; + + @override + set maxZoom(double value) => jsLayer.maxzoom = value; + + @override + double get minZoom => jsLayer.minzoom ?? StyleLayer.defaultMinZoom; + + @override + set minZoom(double value) => jsLayer.minzoom = value; + + @override + bool get visible => + requireMap.getPaintProperty(id, 'visibility').toString() == 'visible'; + + @override + set visible(bool value) => requireMap.setPaintProperty( + id, + 'visibility', + (value ? 'visible' : 'none').toJS, + ); +} diff --git a/packages/maplibre_web/lib/src/style/layers/symbol_style_layer.dart b/packages/maplibre_web/lib/src/style/layers/symbol_style_layer.dart new file mode 100644 index 000000000..44dcaa35d --- /dev/null +++ b/packages/maplibre_web/lib/src/style/layers/symbol_style_layer.dart @@ -0,0 +1,828 @@ +part of 'style_layer.dart'; + +/// Web implementation of [SymbolStyleLayer]. +class SymbolStyleLayerWeb extends StyleLayerWeb implements SymbolStyleLayer { + /// Default constructor for a [SymbolStyleLayerWeb] instance. + SymbolStyleLayerWeb({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? + textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) : super.fromNativeLayer( + jsLayer: js.LayerSpecification( + id: id, + type: 'symbol', + maxzoom: maxZoom, + minzoom: minZoom, + source: sourceId, + layout: createSymbolLayout( + visible: visible, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + ).jsify(), + paint: createSymbolPaint( + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ).jsify(), + ), + ) { + if (sourceLayerId case final id?) jsLayer.sourceLayer = id; + if (filter case final filter?) jsLayer.filter = filter.json.jsify()!; + } + + @override + PropertyValue get placement => + requireMap + .getLayoutProperty(id, 'symbol-placement') + .toEnumPropertyValue(SymbolPlacement.values) ?? + SymbolStyleLayer.defaultPlacement; + + @override + set placement(PropertyValue property) => requireMap + .setLayoutProperty(id, 'symbol-placement', property.toJson().jsify()); + + @override + PropertyValue get spacing => + requireMap + .getLayoutProperty(id, 'symbol-spacing') + .toPropertyValue() ?? + SymbolStyleLayer.defaultSpacing; + + @override + set spacing(PropertyValue property) => requireMap.setLayoutProperty( + id, + 'symbol-spacing', + property.toJson().jsify(), + ); + + @override + PropertyValue get avoidEdges => + requireMap + .getLayoutProperty(id, 'symbol-avoid-edges') + .toPropertyValue() ?? + SymbolStyleLayer.defaultAvoidEdges; + + @override + set avoidEdges(PropertyValue property) => requireMap.setLayoutProperty( + id, + 'symbol-avoid-edges', + property.toJson().jsify(), + ); + + @override + PropertyValue get zOrder => + requireMap + .getLayoutProperty(id, 'symbol-z-order') + .toEnumPropertyValue(SymbolZOrder.values) ?? + SymbolStyleLayer.defaultZOrder; + + @override + set zOrder(PropertyValue property) => requireMap + .setLayoutProperty(id, 'symbol-z-order', property.toJson().jsify()); + + @override + PropertyValue? get sortKey => requireMap + .getLayoutProperty(id, 'symbol-sort-key') + .toPropertyValue(); + + @override + set sortKey(PropertyValue? property) => requireMap.setLayoutProperty( + id, + 'symbol-sort-key', + property?.toJson().jsify(), + ); + + @override + PropertyValue get iconAllowOverlap => + requireMap + .getLayoutProperty(id, 'icon-allow-overlap') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconAllowOverlap; + + @override + set iconAllowOverlap(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-allow-overlap', property.toJson().jsify()); + + @override + PropertyValue get iconOverlap => + requireMap + .getLayoutProperty(id, 'icon-overlap') + .toEnumPropertyValue(SymbolOverlap.values) ?? + SymbolStyleLayer.defaultIconOverlap; + + @override + set iconOverlap(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-overlap', property.toJson().jsify()); + + @override + PropertyValue get iconIgnorePlacement => + requireMap + .getLayoutProperty(id, 'icon-ignore-placement') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconIgnorePlacement; + + @override + set iconIgnorePlacement(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'icon-ignore-placement', + property.toJson().jsify(), + ); + + @override + PropertyValue get iconOptional => + requireMap + .getLayoutProperty(id, 'icon-optional') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconOptional; + + @override + set iconOptional(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-optional', property.toJson().jsify()); + + @override + PropertyValue get iconRotationAlignment => + requireMap + .getLayoutProperty(id, 'icon-rotation-alignment') + .toEnumPropertyValue(IconRotationAlignment.values) ?? + SymbolStyleLayer.defaultIconRotationAlignment; + + @override + set iconRotationAlignment(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'icon-rotation-alignment', + property.toJson().jsify(), + ); + + @override + PropertyValue get iconSize => + requireMap.getLayoutProperty(id, 'icon-size').toPropertyValue() ?? + SymbolStyleLayer.defaultIconSize; + + @override + set iconSize(PropertyValue property) => + requireMap.setLayoutProperty(id, 'icon-size', property.toJson().jsify()); + + @override + PropertyValue get iconTextFit => + requireMap + .getLayoutProperty(id, 'icon-text-fit') + .toEnumPropertyValue(IconTextFit.values) ?? + SymbolStyleLayer.defaultIconTextFit; + + @override + set iconTextFit(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-text-fit', property.toJson().jsify()); + + @override + PropertyValue get iconTextFitPadding => + requireMap + .getLayoutProperty(id, 'icon-text-fit-padding') + .toEdgeInsetsPropertyValue() ?? + SymbolStyleLayer.defaultIconTextFitPadding; + + @override + set iconTextFitPadding(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'icon-text-fit-padding', + property.toJson().jsify(), + ); + + @override + PropertyValue? get iconImage => + requireMap.getLayoutProperty(id, 'icon-image').toPropertyValue(); + + @override + set iconImage(PropertyValue? property) => requireMap + .setLayoutProperty(id, 'icon-image', property?.toJson().jsify()); + + @override + PropertyValue get iconRotate => + requireMap + .getLayoutProperty(id, 'icon-rotate') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconRotate; + + @override + set iconRotate(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-rotate', property.toJson().jsify()); + + @override + PropertyValue get iconPadding => + requireMap + .getLayoutProperty(id, 'icon-padding') + .toEdgeInsetsPropertyValue() ?? + SymbolStyleLayer.defaultIconPadding; + + @override + set iconPadding(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-padding', property.toJson().jsify()); + + @override + PropertyValue get iconKeepUpright => + requireMap + .getLayoutProperty(id, 'icon-keep-upright') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconKeepUpright; + + @override + set iconKeepUpright(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-keep-upright', property.toJson().jsify()); + + @override + PropertyValue get iconOffset => + requireMap.getLayoutProperty(id, 'icon-offset').toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultIconOffset; + + @override + set iconOffset(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-offset', property.toJson().jsify()); + + @override + PropertyValue get iconAnchor => + requireMap + .getLayoutProperty(id, 'icon-anchor') + .toEnumPropertyValue(IconAnchor.values) ?? + SymbolStyleLayer.defaultIconAnchor; + + @override + set iconAnchor(PropertyValue property) => requireMap + .setLayoutProperty(id, 'icon-anchor', property.toJson().jsify()); + + @override + PropertyValue get iconPitchAlignment => + requireMap + .getLayoutProperty(id, 'icon-pitch-alignment') + .toEnumPropertyValue(IconPitchAlignment.values) ?? + SymbolStyleLayer.defaultIconPitchAlignment; + + @override + set iconPitchAlignment(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'icon-pitch-alignment', + property.toJson().jsify(), + ); + + @override + PropertyValue get textPitchAlignment => + requireMap + .getLayoutProperty(id, 'text-pitch-alignment') + .toEnumPropertyValue(TextPitchAlignment.values) ?? + SymbolStyleLayer.defaultTextPitchAlignment; + + @override + set textPitchAlignment(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'text-pitch-alignment', + property.toJson().jsify(), + ); + + @override + PropertyValue get textRotationAlignment => + requireMap + .getLayoutProperty(id, 'text-rotation-alignment') + .toEnumPropertyValue(TextRotationAlignment.values) ?? + SymbolStyleLayer.defaultTextRotationAlignment; + + @override + set textRotationAlignment(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'text-rotation-alignment', + property.toJson().jsify(), + ); + + @override + PropertyValue get textField => + requireMap + .getLayoutProperty(id, 'text-field') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextField; + + @override + set textField(PropertyValue property) => + requireMap.setLayoutProperty(id, 'text-field', property.toJson().jsify()); + + @override + PropertyValue> get textFont => + requireMap + .getLayoutProperty(id, 'text-font') + .toPropertyValue>() ?? + SymbolStyleLayer.defaultTextFont; + + @override + set textFont(PropertyValue> property) => + requireMap.setLayoutProperty(id, 'text-font', property.toJson().jsify()); + + @override + PropertyValue get textSize => + requireMap.getLayoutProperty(id, 'text-size').toPropertyValue() ?? + SymbolStyleLayer.defaultTextSize; + + @override + set textSize(PropertyValue property) => + requireMap.setLayoutProperty(id, 'text-size', property.toJson().jsify()); + + @override + PropertyValue get textMaxWidth => + requireMap + .getLayoutProperty(id, 'text-max-width') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextMaxWidth; + + @override + set textMaxWidth(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-max-width', property.toJson().jsify()); + + @override + PropertyValue get textLineHeight => + requireMap + .getLayoutProperty(id, 'text-line-height') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextLineHeight; + + @override + set textLineHeight(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-line-height', property.toJson().jsify()); + + @override + PropertyValue get textLetterSpacing => + requireMap + .getLayoutProperty(id, 'text-letter-spacing') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextLetterSpacing; + + @override + set textLetterSpacing(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-letter-spacing', property.toJson().jsify()); + + @override + PropertyValue get textJustify => + requireMap + .getLayoutProperty(id, 'text-justify') + .toEnumPropertyValue(TextJustify.values) ?? + SymbolStyleLayer.defaultTextJustify; + + @override + set textJustify(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-justify', property.toJson().jsify()); + + @override + PropertyValue get textRadialOffset => + requireMap + .getLayoutProperty(id, 'text-radial-offset') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextRadialOffset; + + @override + set textRadialOffset(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-radial-offset', property.toJson().jsify()); + + @override + PropertyValue>? get textVariableAnchor => requireMap + .getLayoutProperty(id, 'text-variable-anchor') + .toEnumListPropertyValue(IconAnchor.values); + + @override + set textVariableAnchor(PropertyValue>? property) => + requireMap.setLayoutProperty( + id, + 'text-variable-anchor', + property?.toJson().jsify(), + ); + + @override + PropertyValue>? get textVariableAnchorOffset => + requireMap + .getLayoutProperty(id, 'text-variable-anchor-offset') + .toStringOffsetMapPropertyValue(); + + @override + set textVariableAnchorOffset( + PropertyValue>? property, + ) => requireMap.setLayoutProperty( + id, + 'text-variable-anchor-offset', + property?.toJson().jsify(), + ); + + @override + PropertyValue get textAnchor => + requireMap + .getLayoutProperty(id, 'text-anchor') + .toEnumPropertyValue(TextAnchor.values) ?? + SymbolStyleLayer.defaultTextAnchor; + + @override + set textAnchor(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-anchor', property.toJson().jsify()); + + @override + PropertyValue get textMaxAngle => + requireMap + .getLayoutProperty(id, 'text-max-angle') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextMaxAngle; + + @override + set textMaxAngle(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-max-angle', property.toJson().jsify()); + + @override + PropertyValue>? get textWritingMode => requireMap + .getLayoutProperty(id, 'text-writing-mode') + .toEnumListPropertyValue(TextWritingMode.values); + + @override + set textWritingMode(PropertyValue>? property) => + requireMap.setLayoutProperty( + id, + 'text-writing-mode', + property?.toJson().jsify(), + ); + + @override + PropertyValue get textRotate => + requireMap + .getLayoutProperty(id, 'text-rotate') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextRotate; + + @override + set textRotate(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-rotate', property.toJson().jsify()); + + @override + PropertyValue get textPadding => + requireMap + .getLayoutProperty(id, 'text-padding') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextPadding; + + @override + set textPadding(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-padding', property.toJson().jsify()); + + @override + PropertyValue get textKeepUpright => + requireMap + .getLayoutProperty(id, 'text-keep-upright') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextKeepUpright; + + @override + set textKeepUpright(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-keep-upright', property.toJson().jsify()); + + @override + PropertyValue get textTransform => + requireMap + .getLayoutProperty(id, 'text-transform') + .toEnumPropertyValue(TextTransform.values) ?? + SymbolStyleLayer.defaultTextTransform; + + @override + set textTransform(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-transform', property.toJson().jsify()); + + @override + PropertyValue get textOffset => + requireMap.getLayoutProperty(id, 'text-offset').toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultTextOffset; + + @override + set textOffset(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-offset', property.toJson().jsify()); + + @override + PropertyValue get textAllowOverlap => + requireMap + .getLayoutProperty(id, 'text-allow-overlap') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextAllowOverlap; + + @override + set textAllowOverlap(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-allow-overlap', property.toJson().jsify()); + + @override + PropertyValue? get textOverlap => requireMap + .getLayoutProperty(id, 'text-overlap') + .toEnumPropertyValue(SymbolOverlap.values); + + @override + set textOverlap(PropertyValue? property) => requireMap + .setLayoutProperty(id, 'text-overlap', property?.toJson().jsify()); + + @override + PropertyValue get textIgnorePlacement => + requireMap + .getLayoutProperty(id, 'text-ignore-placement') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextIgnorePlacement; + + @override + set textIgnorePlacement(PropertyValue property) => + requireMap.setLayoutProperty( + id, + 'text-ignore-placement', + property.toJson().jsify(), + ); + + @override + PropertyValue get textOptional => + requireMap + .getLayoutProperty(id, 'text-optional') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextOptional; + + @override + set textOptional(PropertyValue property) => requireMap + .setLayoutProperty(id, 'text-optional', property.toJson().jsify()); + + @override + PropertyValue get iconOpacity => + requireMap + .getPaintProperty(id, 'icon-opacity') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconOpacity; + + @override + set iconOpacity(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-opacity', property.toJson().jsify()); + + @override + PropertyValue get iconColor => + requireMap.getPaintProperty(id, 'icon-color').toColorPropertyValue() ?? + SymbolStyleLayer.defaultIconColor; + + @override + set iconColor(PropertyValue property) => + requireMap.setPaintProperty(id, 'icon-color', property.toJson().jsify()); + + @override + PropertyValue get iconHaloColor => + requireMap + .getPaintProperty(id, 'icon-halo-color') + .toColorPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloColor; + + @override + set iconHaloColor(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-halo-color', property.toJson().jsify()); + + @override + PropertyValue get iconHaloWidth => + requireMap + .getPaintProperty(id, 'icon-halo-width') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloWidth; + + @override + set iconHaloWidth(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-halo-width', property.toJson().jsify()); + + @override + PropertyValue get iconHaloBlur => + requireMap + .getPaintProperty(id, 'icon-halo-blur') + .toPropertyValue() ?? + SymbolStyleLayer.defaultIconHaloBlur; + + @override + set iconHaloBlur(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-halo-blur', property.toJson().jsify()); + + @override + PropertyValue get iconTranslate => + requireMap + .getPaintProperty(id, 'icon-translate') + .toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultIconTranslate; + + @override + set iconTranslate(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-translate', property.toJson().jsify()); + + @override + PropertyValue get iconTranslateAnchor => + requireMap + .getPaintProperty(id, 'icon-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + SymbolStyleLayer.defaultIconTranslateAnchor; + + @override + set iconTranslateAnchor(PropertyValue property) => requireMap + .setPaintProperty(id, 'icon-translate-anchor', property.toJson().jsify()); + + @override + PropertyValue get textOpacity => + requireMap + .getPaintProperty(id, 'text-opacity') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextOpacity; + + @override + set textOpacity(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-opacity', property.toJson().jsify()); + + @override + PropertyValue get textColor => + requireMap.getPaintProperty(id, 'text-color').toColorPropertyValue() ?? + SymbolStyleLayer.defaultTextColor; + + @override + set textColor(PropertyValue property) => + requireMap.setPaintProperty(id, 'text-color', property.toJson().jsify()); + + @override + PropertyValue get textHaloColor => + requireMap + .getPaintProperty(id, 'text-halo-color') + .toColorPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloColor; + + @override + set textHaloColor(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-halo-color', property.toJson().jsify()); + + @override + PropertyValue get textHaloWidth => + requireMap + .getPaintProperty(id, 'text-halo-width') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloWidth; + + @override + set textHaloWidth(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-halo-width', property.toJson().jsify()); + + @override + PropertyValue get textHaloBlur => + requireMap + .getPaintProperty(id, 'text-halo-blur') + .toPropertyValue() ?? + SymbolStyleLayer.defaultTextHaloBlur; + + @override + set textHaloBlur(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-halo-blur', property.toJson().jsify()); + + @override + PropertyValue get textTranslate => + requireMap + .getPaintProperty(id, 'text-translate') + .toOffsetPropertyValue() ?? + SymbolStyleLayer.defaultTextTranslate; + + @override + set textTranslate(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-translate', property.toJson().jsify()); + + @override + PropertyValue get textTranslateAnchor => + requireMap + .getPaintProperty(id, 'text-translate-anchor') + .toEnumPropertyValue(ReferenceSpace.values) ?? + SymbolStyleLayer.defaultTextTranslateAnchor; + + @override + set textTranslateAnchor(PropertyValue property) => requireMap + .setPaintProperty(id, 'text-translate-anchor', property.toJson().jsify()); + + @override + Expression? get filter => + Expression.fromJson(jsLayer.filter.dartify()! as List); + + @override + set filter(Expression? value) => jsLayer.filter = value!.json.jsify()!; + + @override + String? get sourceLayerId => jsLayer.sourceLayer; + + @override + set sourceLayerId(String? value) => jsLayer.sourceLayer = value; + + @override + String get sourceId => jsLayer.source!; +} diff --git a/packages/maplibre_web/lib/src/style_controller.dart b/packages/maplibre_web/lib/src/style_controller.dart index 2fab52dff..65c55852b 100644 --- a/packages/maplibre_web/lib/src/style_controller.dart +++ b/packages/maplibre_web/lib/src/style_controller.dart @@ -84,112 +84,9 @@ class StyleControllerWeb extends StyleController { ); } - final interop.LayerSpecification jsLayer; - switch (layer) { - case FillStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'fill', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case CircleStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'circle', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case BackgroundStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'background', - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case FillExtrusionStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'fill-extrusion', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case HeatmapStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'heatmap', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case HillshadeStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'hillshade', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case LineStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'line', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case RasterStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'raster', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - case SymbolStyleLayer(): - jsLayer = interop.LayerSpecification( - id: layer.id, - type: 'symbol', - source: layer.sourceId, - layout: layer.layout.jsify()!, - paint: layer.paint.jsify()!, - minzoom: layer.minZoom, - maxzoom: layer.maxZoom, - ); - default: - throw UnimplementedError( - 'Layer type "${layer.runtimeType}" is not implemented for Web.', - ); - } - if (layer.filter case final filter?) { - final jsFilter = filter.jsify(); - if (jsFilter != null) { - jsLayer.filter = jsFilter; - } - } - if (layer is StyleLayerWithSource && layer.sourceLayerId != null) { - jsLayer.sourceLayer = layer.sourceLayerId; - } - _map.addLayer(jsLayer, belowLayerId); + final webLayer = layer as StyleLayerWeb; + webLayer.jsMap = _map; + _map.addLayer(webLayer.jsLayer, belowLayerId); } @override diff --git a/packages/maplibre_webview/lib/src/maplibre_plugin.dart b/packages/maplibre_webview/lib/src/maplibre_plugin.dart index b51eb2170..e81b02971 100644 --- a/packages/maplibre_webview/lib/src/maplibre_plugin.dart +++ b/packages/maplibre_webview/lib/src/maplibre_plugin.dart @@ -1,5 +1,7 @@ +import 'package:flutter/painting.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:maplibre_webview/src/map_state.dart'; +import 'package:maplibre_webview/src/style/layers/style_layer.dart'; /// WebView implementation of the federated MapLibre plugin. class MapLibrePlugin extends MapLibrePlatform { @@ -17,4 +19,442 @@ class MapLibrePlugin extends MapLibrePlatform { PermissionManager createPermissionManager() { throw Exception('The PermissionManager can not be used on webview.'); } + + /// Create a platform specific [BackgroundStyleLayer] object. + @override + BackgroundStyleLayer createBackgroundStyleLayer({ + required String id, + required bool visible, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => BackgroundStyleLayerWebView( + id: id, + visible: visible, + color: color, + pattern: pattern, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [CircleStyleLayer] object. + @override + CircleStyleLayer createCircleStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue? sortKey, + required PropertyValue radius, + required PropertyValue color, + required PropertyValue blur, + required PropertyValue opacity, + required PropertyValue pitchScale, + required PropertyValue pitchAlignment, + required PropertyValue strokeWidth, + required PropertyValue strokeColor, + required PropertyValue strokeOpacity, + }) => CircleStyleLayerWebView( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + translate: translate, + translateAnchor: translateAnchor, + sortKey: sortKey, + radius: radius, + color: color, + blur: blur, + opacity: opacity, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + /// Create a platform specific [ColorReliefStyleLayer] object. + @override + ColorReliefStyleLayer createColorReliefStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required PropertyValue? color, + required PropertyValue opacity, + required double minZoom, + required double maxZoom, + }) => ColorReliefStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + color: color, + opacity: opacity, + minZoom: minZoom, + maxZoom: maxZoom, + ); + + /// Create a platform specific [FillExtrusionStyleLayer] object. + @override + FillExtrusionStyleLayer createFillExtrusionStyleLayer({ + required String id, + required String sourceId, + required double minZoom, + required double maxZoom, + required bool visible, + required Expression? filter, + required String? sourceLayerId, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue? pattern, + required PropertyValue height, + required PropertyValue base, + required PropertyValue verticalGradient, + required PropertyValue translate, + required PropertyValue translateAnchor, + }) => FillExtrusionStyleLayerWebView( + id: id, + sourceId: sourceId, + minZoom: minZoom, + maxZoom: maxZoom, + visible: visible, + filter: filter, + sourceLayerId: sourceLayerId, + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + /// Create a platform specific [FillStyleLayer] object. + @override + FillStyleLayer createFillStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue antialias, + required PropertyValue color, + required PropertyValue opacity, + required PropertyValue outlineColor, + required PropertyValue? pattern, + }) => FillStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + pattern: pattern, + ); + + /// Create a platform specific [HeatmapStyleLayer] object. + @override + HeatmapStyleLayer createHeatmapStyleLayer({ + required String id, + required String sourceId, + required String? sourceLayerId, + required Expression? filter, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue radius, + required PropertyValue weight, + required PropertyValue intensity, + required PropertyValue? color, + required PropertyValue opacity, + }) => HeatmapStyleLayerWebView( + id: id, + sourceId: sourceId, + sourceLayerId: sourceLayerId, + filter: filter, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + /// Create a platform specific [HillshadeStyleLayer] object. + @override + HillshadeStyleLayer createHillshadeStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue illuminationDirection, + required PropertyValue illuminationAltitude, + required PropertyValue illuminationAnchor, + required PropertyValue exaggeration, + required PropertyValue shadowColor, + required PropertyValue highlightColor, + required PropertyValue accentColor, + required PropertyValue method, + }) => HillshadeStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + illuminationDirection: illuminationDirection, + illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + method: method, + ); + + /// Create a platform specific [LineStyleLayer] object. + @override + LineStyleLayer createLineStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue translate, + required PropertyValue translateAnchor, + required PropertyValue cap, + required PropertyValue join, + required PropertyValue miterLimit, + required PropertyValue roundLimit, + required PropertyValue opacity, + required PropertyValue color, + required PropertyValue width, + required PropertyValue gapWidth, + required PropertyValue offset, + required PropertyValue blur, + required PropertyValue>? dashArray, + required PropertyValue? pattern, + required PropertyValue? gradient, + }) => LineStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + translate: translate, + translateAnchor: translateAnchor, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + opacity: opacity, + color: color, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + cap: cap, + ); + + /// Create a platform specific [RasterStyleLayer] object. + @override + RasterStyleLayer createRasterStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required PropertyValue opacity, + required PropertyValue hueRotate, + required PropertyValue brightnessMin, + required PropertyValue brightnessMax, + required PropertyValue saturation, + required PropertyValue contrast, + required PropertyValue resampling, + required PropertyValue fadeDuration, + }) => RasterStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + /// Create a platform specific [SymbolStyleLayer] object. + @override + SymbolStyleLayer createSymbolStyleLayer({ + required String id, + required String sourceId, + required bool visible, + required double minZoom, + required double maxZoom, + required String? sourceLayerId, + required Expression? filter, + required PropertyValue? sortKey, + required PropertyValue placement, + required PropertyValue spacing, + required PropertyValue avoidEdges, + required PropertyValue zOrder, + required PropertyValue iconAllowOverlap, + required PropertyValue iconOverlap, + required PropertyValue iconIgnorePlacement, + required PropertyValue iconOptional, + required PropertyValue iconRotationAlignment, + required PropertyValue iconSize, + required PropertyValue iconTextFit, + required PropertyValue iconTextFitPadding, + required PropertyValue? iconImage, + required PropertyValue iconRotate, + required PropertyValue iconPadding, + required PropertyValue iconKeepUpright, + required PropertyValue iconOffset, + required PropertyValue iconAnchor, + required PropertyValue iconPitchAlignment, + required PropertyValue textPitchAlignment, + required PropertyValue textRotationAlignment, + required PropertyValue textField, + required PropertyValue> textFont, + required PropertyValue textSize, + required PropertyValue textMaxWidth, + required PropertyValue textLineHeight, + required PropertyValue textLetterSpacing, + required PropertyValue textJustify, + required PropertyValue textRadialOffset, + required PropertyValue>? textVariableAnchor, + required PropertyValue>? textVariableAnchorOffset, + required PropertyValue textAnchor, + required PropertyValue textMaxAngle, + required PropertyValue>? textWritingMode, + required PropertyValue textRotate, + required PropertyValue textPadding, + required PropertyValue textKeepUpright, + required PropertyValue textTransform, + required PropertyValue textOffset, + required PropertyValue textAllowOverlap, + required PropertyValue? textOverlap, + required PropertyValue textIgnorePlacement, + required PropertyValue textOptional, + required PropertyValue iconOpacity, + required PropertyValue iconColor, + required PropertyValue iconHaloColor, + required PropertyValue iconHaloWidth, + required PropertyValue iconHaloBlur, + required PropertyValue iconTranslate, + required PropertyValue iconTranslateAnchor, + required PropertyValue textOpacity, + required PropertyValue textColor, + required PropertyValue textHaloColor, + required PropertyValue textHaloWidth, + required PropertyValue textHaloBlur, + required PropertyValue textTranslate, + required PropertyValue textTranslateAnchor, + }) => SymbolStyleLayerWebView( + id: id, + sourceId: sourceId, + visible: visible, + minZoom: minZoom, + maxZoom: maxZoom, + sourceLayerId: sourceLayerId, + filter: filter, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); } diff --git a/packages/maplibre_webview/lib/src/style/layers/background_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/background_style_layer.dart new file mode 100644 index 000000000..6fc9bc929 --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/background_style_layer.dart @@ -0,0 +1,44 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [BackgroundStyleLayer]. +class BackgroundStyleLayerWebView extends StyleLayerWebView + implements BackgroundStyleLayer { + /// Creates a background style layer. + BackgroundStyleLayerWebView({ + required this.id, + required this.color, + required this.maxZoom, + required this.minZoom, + required this.opacity, + required this.pattern, + required this.visible, + }); + + @override + Map get layout => createBackgroundLayout(visible: visible); + + @override + Map get paint => + createBackgroundPaint(color: color, opacity: opacity, pattern: pattern); + + @override + PropertyValue color; + + @override + double maxZoom; + + @override + double minZoom; + + @override + PropertyValue opacity; + + @override + PropertyValue? pattern; + + @override + bool visible; + + @override + final String id; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/circle_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/circle_style_layer.dart new file mode 100644 index 000000000..eb168521e --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/circle_style_layer.dart @@ -0,0 +1,104 @@ +part of 'style_layer.dart'; + +/// Web implementation of [CircleStyleLayer]. +class CircleStyleLayerWebView extends StyleLayerWebView + implements CircleStyleLayer { + /// Construct a [CircleStyleLayerWebView] with the given properties. + CircleStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.sourceLayerId, + required this.filter, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.translate, + required this.translateAnchor, + required this.sortKey, + required this.radius, + required this.color, + required this.blur, + required this.opacity, + required this.pitchScale, + required this.pitchAlignment, + required this.strokeWidth, + required this.strokeColor, + required this.strokeOpacity, + }); + + @override + Map get layout => + createCircleLayout(visible: visible, sortKey: sortKey); + + @override + Map get paint => createCirclePaint( + radius: radius, + color: color, + blur: blur, + opacity: opacity, + translate: translate, + translateAnchor: translateAnchor, + pitchScale: pitchScale, + pitchAlignment: pitchAlignment, + strokeWidth: strokeWidth, + strokeColor: strokeColor, + strokeOpacity: strokeOpacity, + ); + + @override + PropertyValue blur; + + @override + PropertyValue color; + + @override + Expression? filter; + + @override + double maxZoom; + + @override + double minZoom; + + @override + PropertyValue opacity; + + @override + PropertyValue pitchAlignment; + + @override + PropertyValue pitchScale; + + @override + PropertyValue radius; + + @override + PropertyValue? sortKey; + + @override + String? sourceLayerId; + + @override + PropertyValue strokeColor; + + @override + PropertyValue strokeOpacity; + + @override + PropertyValue strokeWidth; + + @override + PropertyValue translate; + + @override + PropertyValue translateAnchor; + + @override + bool visible; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/color_relief_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/color_relief_style_layer.dart new file mode 100644 index 000000000..28b81ef0f --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/color_relief_style_layer.dart @@ -0,0 +1,44 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [ColorReliefStyleLayer]. +class ColorReliefStyleLayerWebView extends StyleLayerWebView + implements ColorReliefStyleLayer { + /// Creates a color relief style layer. + ColorReliefStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.color, + required this.opacity, + required this.minZoom, + required this.maxZoom, + }); + + @override + Map get layout => createColorReliefLayout(visible: visible); + + @override + Map get paint => + createColorReliefPaint(color: color, opacity: opacity); + + @override + bool visible; + + @override + PropertyValue? color; + + @override + PropertyValue opacity; + + @override + double minZoom; + + @override + double maxZoom; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/fill_extrusion_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/fill_extrusion_style_layer.dart new file mode 100644 index 000000000..0572e80d2 --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/fill_extrusion_style_layer.dart @@ -0,0 +1,85 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [FillExtrusionStyleLayer]. +class FillExtrusionStyleLayerWebView extends StyleLayerWebView + implements FillExtrusionStyleLayer { + /// Creates a fill extrusion style layer. + FillExtrusionStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.minZoom, + required this.maxZoom, + required this.visible, + required this.filter, + required this.sourceLayerId, + required this.opacity, + required this.color, + required this.pattern, + required this.height, + required this.base, + required this.verticalGradient, + required this.translate, + required this.translateAnchor, + }); + + @override + Map get layout => + createFillExtrusionLayout(visible: visible); + + @override + Map get paint => createFillExtrusionPaint( + opacity: opacity, + color: color, + pattern: pattern, + height: height, + base: base, + verticalGradient: verticalGradient, + translate: translate, + translateAnchor: translateAnchor, + ); + + @override + final String id; + + @override + final String sourceId; + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + Expression? filter; + + @override + String? sourceLayerId; + + @override + PropertyValue opacity; + + @override + PropertyValue color; + + @override + PropertyValue? pattern; + + @override + PropertyValue height; + + @override + PropertyValue base; + + @override + PropertyValue verticalGradient; + + @override + PropertyValue translate; + + @override + PropertyValue translateAnchor; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/fill_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/fill_style_layer.dart new file mode 100644 index 000000000..2fcfebf3e --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/fill_style_layer.dart @@ -0,0 +1,84 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [FillStyleLayer]. +class FillStyleLayerWebView extends StyleLayerWebView + implements FillStyleLayer { + /// Creates a fill style layer. + FillStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.sourceLayerId, + required this.filter, + required this.sortKey, + required this.translate, + required this.translateAnchor, + required this.antialias, + required this.color, + required this.opacity, + required this.outlineColor, + required this.pattern, + }); + + @override + Map get layout => + createFillLayout(visible: visible, sortKey: sortKey); + + @override + Map get paint => createFillPaint( + antialias: antialias, + color: color, + opacity: opacity, + outlineColor: outlineColor, + translate: translate, + translateAnchor: translateAnchor, + pattern: pattern, + ); + + @override + final String id; + + @override + final String sourceId; + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + Expression? filter; + + @override + String? sourceLayerId; + + @override + PropertyValue? sortKey; + + @override + PropertyValue translate; + + @override + PropertyValue translateAnchor; + + @override + PropertyValue antialias; + + @override + PropertyValue color; + + @override + PropertyValue opacity; + + @override + PropertyValue outlineColor; + + @override + PropertyValue? pattern; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/heatmap_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/heatmap_style_layer.dart new file mode 100644 index 000000000..45195915c --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/heatmap_style_layer.dart @@ -0,0 +1,69 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [HeatmapStyleLayer]. +class HeatmapStyleLayerWebView extends StyleLayerWebView + implements HeatmapStyleLayer { + /// Creates a heatmap style layer. + HeatmapStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.sourceLayerId, + required this.filter, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.radius, + required this.weight, + required this.intensity, + required this.color, + required this.opacity, + }); + + @override + Map get layout => createHeatmapLayout(visible: visible); + + @override + Map get paint => createHeatmapPaint( + radius: radius, + weight: weight, + intensity: intensity, + color: color, + opacity: opacity, + ); + + @override + final String id; + + @override + final String sourceId; + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + Expression? filter; + + @override + String? sourceLayerId; + + @override + PropertyValue radius; + + @override + PropertyValue weight; + + @override + PropertyValue intensity; + + @override + PropertyValue? color; + + @override + PropertyValue opacity; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/hillshade_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/hillshade_style_layer.dart new file mode 100644 index 000000000..94f16871f --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/hillshade_style_layer.dart @@ -0,0 +1,76 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [HillshadeStyleLayer]. +class HillshadeStyleLayerWebView extends StyleLayerWebView + implements HillshadeStyleLayer { + /// Creates a hillshade style layer. + HillshadeStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.illuminationDirection, + required this.illuminationAltitude, + required this.illuminationAnchor, + required this.exaggeration, + required this.shadowColor, + required this.highlightColor, + required this.accentColor, + required this.method, + }); + + @override + Map get layout => createHillshadeLayout(visible: visible); + + @override + Map get paint => createHillshadePaint( + illuminationDirection: illuminationDirection, + illuminationAltitude: illuminationAltitude, + illuminationAnchor: illuminationAnchor, + exaggeration: exaggeration, + shadowColor: shadowColor, + highlightColor: highlightColor, + accentColor: accentColor, + method: method, + ); + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + PropertyValue illuminationDirection; + + @override + PropertyValue illuminationAltitude; + + @override + PropertyValue illuminationAnchor; + + @override + PropertyValue exaggeration; + + @override + PropertyValue shadowColor; + + @override + PropertyValue highlightColor; + + @override + PropertyValue accentColor; + + @override + PropertyValue method; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/line_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/line_style_layer.dart new file mode 100644 index 000000000..2fbdc323b --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/line_style_layer.dart @@ -0,0 +1,126 @@ +part of 'style_layer.dart'; + +/// A line style layer. +class LineStyleLayerWebView extends StyleLayerWebView + implements LineStyleLayer { + /// Creates a line style layer. + LineStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.sourceLayerId, + required this.filter, + required this.sortKey, + required this.translate, + required this.translateAnchor, + required this.cap, + required this.join, + required this.miterLimit, + required this.roundLimit, + required this.opacity, + required this.color, + required this.width, + required this.gapWidth, + required this.offset, + required this.blur, + required this.dashArray, + required this.pattern, + required this.gradient, + }); + + @override + Map get layout => createLineLayout( + visible: visible, + cap: cap, + join: join, + miterLimit: miterLimit, + roundLimit: roundLimit, + sortKey: sortKey, + ); + + @override + Map get paint => createLinePaint( + opacity: opacity, + color: color, + translate: translate, + translateAnchor: translateAnchor, + width: width, + gapWidth: gapWidth, + offset: offset, + blur: blur, + dashArray: dashArray, + pattern: pattern, + gradient: gradient, + ); + + @override + String? sourceLayerId; + + @override + Expression? filter; + + @override + PropertyValue? sortKey; + + @override + PropertyValue translate; + + @override + PropertyValue translateAnchor; + + @override + PropertyValue cap; + + @override + PropertyValue join; + + @override + PropertyValue miterLimit; + + @override + PropertyValue roundLimit; + + @override + PropertyValue opacity; + + @override + PropertyValue color; + + @override + PropertyValue width; + + @override + PropertyValue gapWidth; + + @override + PropertyValue offset; + + @override + PropertyValue blur; + + @override + PropertyValue>? dashArray; + + @override + PropertyValue? pattern; + + @override + PropertyValue? gradient; + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/raster_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/raster_style_layer.dart new file mode 100644 index 000000000..d2d213e8e --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/raster_style_layer.dart @@ -0,0 +1,76 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [RasterStyleLayer]. +class RasterStyleLayerWebView extends StyleLayerWebView + implements RasterStyleLayer { + /// Creates a raster style layer. + RasterStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.opacity, + required this.hueRotate, + required this.brightnessMin, + required this.brightnessMax, + required this.saturation, + required this.contrast, + required this.resampling, + required this.fadeDuration, + }); + + @override + Map get layout => createRasterLayout(visible: visible); + + @override + Map get paint => createRasterPaint( + opacity: opacity, + hueRotate: hueRotate, + brightnessMin: brightnessMin, + brightnessMax: brightnessMax, + saturation: saturation, + contrast: contrast, + resampling: resampling, + fadeDuration: fadeDuration, + ); + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + PropertyValue opacity; + + @override + PropertyValue hueRotate; + + @override + PropertyValue brightnessMin; + + @override + PropertyValue brightnessMax; + + @override + PropertyValue saturation; + + @override + PropertyValue contrast; + + @override + PropertyValue resampling; + + @override + PropertyValue fadeDuration; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/style_layer.dart new file mode 100644 index 000000000..9819200d0 --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/style_layer.dart @@ -0,0 +1,22 @@ +import 'package:flutter/painting.dart'; +import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; + +part 'background_style_layer.dart'; +part 'circle_style_layer.dart'; +part 'color_relief_style_layer.dart'; +part 'fill_extrusion_style_layer.dart'; +part 'fill_style_layer.dart'; +part 'heatmap_style_layer.dart'; +part 'hillshade_style_layer.dart'; +part 'line_style_layer.dart'; +part 'raster_style_layer.dart'; +part 'symbol_style_layer.dart'; + +/// WebView implementation of [StyleLayer]. +abstract class StyleLayerWebView implements StyleLayer { + /// Get the layout properties of the layer. + Map get layout; + + /// Get the paint properties of the layer. + Map get paint; +} diff --git a/packages/maplibre_webview/lib/src/style/layers/symbol_style_layer.dart b/packages/maplibre_webview/lib/src/style/layers/symbol_style_layer.dart new file mode 100644 index 000000000..00450d6bd --- /dev/null +++ b/packages/maplibre_webview/lib/src/style/layers/symbol_style_layer.dart @@ -0,0 +1,336 @@ +part of 'style_layer.dart'; + +/// WebView implementation of [SymbolStyleLayer]. +class SymbolStyleLayerWebView extends StyleLayerWebView + implements SymbolStyleLayer { + /// Creates a symbol style layer. + SymbolStyleLayerWebView({ + required this.id, + required this.sourceId, + required this.visible, + required this.minZoom, + required this.maxZoom, + required this.sourceLayerId, + required this.filter, + required this.sortKey, + required this.placement, + required this.spacing, + required this.avoidEdges, + required this.zOrder, + required this.iconAllowOverlap, + required this.iconOverlap, + required this.iconIgnorePlacement, + required this.iconOptional, + required this.iconRotationAlignment, + required this.iconSize, + required this.iconTextFit, + required this.iconTextFitPadding, + required this.iconImage, + required this.iconRotate, + required this.iconPadding, + required this.iconKeepUpright, + required this.iconOffset, + required this.iconAnchor, + required this.iconPitchAlignment, + required this.textPitchAlignment, + required this.textRotationAlignment, + required this.textField, + required this.textFont, + required this.textSize, + required this.textMaxWidth, + required this.textLineHeight, + required this.textLetterSpacing, + required this.textJustify, + required this.textRadialOffset, + required this.textVariableAnchor, + required this.textVariableAnchorOffset, + required this.textAnchor, + required this.textMaxAngle, + required this.textWritingMode, + required this.textRotate, + required this.textPadding, + required this.textKeepUpright, + required this.textTransform, + required this.textOffset, + required this.textAllowOverlap, + required this.textOverlap, + required this.textIgnorePlacement, + required this.textOptional, + required this.iconOpacity, + required this.iconColor, + required this.iconHaloColor, + required this.iconHaloWidth, + required this.iconHaloBlur, + required this.iconTranslate, + required this.iconTranslateAnchor, + required this.textOpacity, + required this.textColor, + required this.textHaloColor, + required this.textHaloWidth, + required this.textHaloBlur, + required this.textTranslate, + required this.textTranslateAnchor, + }); + + @override + Map get layout => createSymbolLayout( + visible: visible, + sortKey: sortKey, + placement: placement, + spacing: spacing, + avoidEdges: avoidEdges, + zOrder: zOrder, + iconAllowOverlap: iconAllowOverlap, + iconOverlap: iconOverlap, + iconIgnorePlacement: iconIgnorePlacement, + iconOptional: iconOptional, + iconRotationAlignment: iconRotationAlignment, + iconSize: iconSize, + iconTextFit: iconTextFit, + iconTextFitPadding: iconTextFitPadding, + iconImage: iconImage, + iconRotate: iconRotate, + iconPadding: iconPadding, + iconKeepUpright: iconKeepUpright, + iconOffset: iconOffset, + iconAnchor: iconAnchor, + iconPitchAlignment: iconPitchAlignment, + textPitchAlignment: textPitchAlignment, + textRotationAlignment: textRotationAlignment, + textField: textField, + textFont: textFont, + textSize: textSize, + textMaxWidth: textMaxWidth, + textLineHeight: textLineHeight, + textLetterSpacing: textLetterSpacing, + textJustify: textJustify, + textRadialOffset: textRadialOffset, + textVariableAnchor: textVariableAnchor, + textVariableAnchorOffset: textVariableAnchorOffset, + textAnchor: textAnchor, + textMaxAngle: textMaxAngle, + textWritingMode: textWritingMode, + textRotate: textRotate, + textPadding: textPadding, + textKeepUpright: textKeepUpright, + textTransform: textTransform, + textOffset: textOffset, + textAllowOverlap: textAllowOverlap, + textOverlap: textOverlap, + textIgnorePlacement: textIgnorePlacement, + textOptional: textOptional, + ); + + @override + Map get paint => createSymbolPaint( + iconOpacity: iconOpacity, + iconColor: iconColor, + iconHaloColor: iconHaloColor, + iconHaloWidth: iconHaloWidth, + iconHaloBlur: iconHaloBlur, + iconTranslate: iconTranslate, + iconTranslateAnchor: iconTranslateAnchor, + textOpacity: textOpacity, + textColor: textColor, + textHaloColor: textHaloColor, + textHaloWidth: textHaloWidth, + textHaloBlur: textHaloBlur, + textTranslate: textTranslate, + textTranslateAnchor: textTranslateAnchor, + ); + + @override + String? sourceLayerId; + + @override + Expression? filter; + + @override + PropertyValue? sortKey; + + @override + PropertyValue placement; + + @override + PropertyValue spacing; + + @override + PropertyValue avoidEdges; + + @override + PropertyValue zOrder; + + @override + PropertyValue iconAllowOverlap; + + @override + PropertyValue iconOverlap; + + @override + PropertyValue iconIgnorePlacement; + + @override + PropertyValue iconOptional; + + @override + PropertyValue iconRotationAlignment; + + @override + PropertyValue iconSize; + + @override + PropertyValue iconTextFit; + + @override + PropertyValue iconTextFitPadding; + + @override + PropertyValue? iconImage; + + @override + PropertyValue iconRotate; + + @override + PropertyValue iconPadding; + + @override + PropertyValue iconKeepUpright; + + @override + PropertyValue iconOffset; + + @override + PropertyValue iconAnchor; + + @override + PropertyValue iconPitchAlignment; + + @override + PropertyValue textPitchAlignment; + + @override + PropertyValue textRotationAlignment; + + @override + PropertyValue textField; + + @override + PropertyValue> textFont; + + @override + PropertyValue textSize; + + @override + PropertyValue textMaxWidth; + + @override + PropertyValue textLineHeight; + + @override + PropertyValue textLetterSpacing; + + @override + PropertyValue textJustify; + + @override + PropertyValue textRadialOffset; + + @override + PropertyValue>? textVariableAnchor; + + @override + PropertyValue>? textVariableAnchorOffset; + + @override + PropertyValue textAnchor; + + @override + PropertyValue textMaxAngle; + + @override + PropertyValue>? textWritingMode; + + @override + PropertyValue textRotate; + + @override + PropertyValue textPadding; + + @override + PropertyValue textKeepUpright; + + @override + PropertyValue textTransform; + + @override + PropertyValue textOffset; + + @override + PropertyValue textAllowOverlap; + + @override + PropertyValue? textOverlap; + + @override + PropertyValue textIgnorePlacement; + + @override + PropertyValue textOptional; + + @override + PropertyValue iconOpacity; + + @override + PropertyValue iconColor; + + @override + PropertyValue iconHaloColor; + + @override + PropertyValue iconHaloWidth; + + @override + PropertyValue iconHaloBlur; + + @override + PropertyValue iconTranslate; + + @override + PropertyValue iconTranslateAnchor; + + @override + PropertyValue textOpacity; + + @override + PropertyValue textColor; + + @override + PropertyValue textHaloColor; + + @override + PropertyValue textHaloWidth; + + @override + PropertyValue textHaloBlur; + + @override + PropertyValue textTranslate; + + @override + PropertyValue textTranslateAnchor; + + @override + bool visible; + + @override + double minZoom; + + @override + double maxZoom; + + @override + final String id; + + @override + final String sourceId; +} diff --git a/packages/maplibre_webview/lib/src/style_controller.dart b/packages/maplibre_webview/lib/src/style_controller.dart index 8d2fec44b..846b08d8b 100644 --- a/packages/maplibre_webview/lib/src/style_controller.dart +++ b/packages/maplibre_webview/lib/src/style_controller.dart @@ -6,6 +6,7 @@ import 'dart:ui'; import 'package:flutter_inappwebview/flutter_inappwebview.dart'; import 'package:maplibre_platform_interface/maplibre_platform_interface.dart'; import 'package:maplibre_webview/src/magic_numbers.dart'; +import 'package:maplibre_webview/src/style/layers/style_layer.dart'; import 'package:maplibre_webview/src/websocket.dart'; /// Implementation of the [StyleController] for platforms using a web view. @@ -49,11 +50,20 @@ class StyleControllerWebView extends StyleController { String? aboveLayerId, int? atIndex, }) async { - final sourceLayerSnippet = - layer is StyleLayerWithSource && layer.sourceLayerId != null - ? '"source-layer": "${layer.sourceLayerId}",' - : ''; + final sourceLayerSnippet = switch (layer) { + StyleLayerWithSource() => 'source: "${layer.sourceId}",', + _ => '', + }; + final vectorLayerSnippet = switch (layer) { + StyleLayerWithVectorSource() when layer.sourceLayerId != null => + ''' + "source-layer": "${layer.sourceLayerId}", + ${layer.filter != null ? 'filter: ${layer.filter},' : ''} +''', + _ => '', + }; final belowArg = belowLayerId != null ? "'$belowLayerId'" : 'undefined'; + final layerWebView = layer as StyleLayerWebView; await webViewController.callAsyncJavaScript( functionBody: ''' @@ -71,13 +81,12 @@ class StyleControllerWebView extends StyleController { BackgroundStyleLayer() => 'background', _ => throw UnsupportedError('Unsupported layer type: ${layer.runtimeType}'), }}", - paint: ${jsonEncode(layer.paint)}, - layout: ${jsonEncode(layer.layout)}, - ${layer.filter != null ? 'filter: ${layer.filter},' : ''} + layout: ${jsonEncode(layerWebView.layout)}, + paint: ${jsonEncode(layerWebView.paint)}, minzoom: ${layer.minZoom}, maxzoom: ${layer.maxZoom}, - ${layer is StyleLayerWithSource ? 'source: "${layer.sourceId}",' : ''} $sourceLayerSnippet + $vectorLayerSnippet }; if (${layer is StyleLayerWithSource ? 'true' : 'false'} && !window.map.getSource(layer.source)) { throw new Error(`Source "\${layer.source}" not found for layer "${layer.id}"`);