diff --git a/android/src/main/java/com/mapbox/mapboxgl/Convert.java b/android/src/main/java/com/mapbox/mapboxgl/Convert.java index 77046fca2..9ac558620 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/Convert.java +++ b/android/src/main/java/com/mapbox/mapboxgl/Convert.java @@ -63,6 +63,15 @@ static CameraPosition toCameraPosition(Object o) { return builder.build(); } + static List toAnnotationOrder(Object o) { + final List data = toList(o); + List annotations = new ArrayList(); + for (int index = 0; index < data.size(); index++) { + annotations.add(toString(data.get(index))); + } + return annotations; + } + static boolean isScrollByCameraUpdate(Object o) { return toString(toList(o).get(0)).equals("scrollBy"); } diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java index 3efa88f4b..7cafa1058 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapBuilder.java @@ -17,6 +17,8 @@ import io.flutter.plugin.common.PluginRegistry; import java.util.concurrent.atomic.AtomicInteger; +import java.util.List; +import java.util.ArrayList; class MapboxMapBuilder implements MapboxMapOptionsSink { @@ -29,11 +31,12 @@ class MapboxMapBuilder implements MapboxMapOptionsSink { private int myLocationTrackingMode = 0; private int myLocationRenderMode = 0; private String styleString = Style.MAPBOX_STREETS; + private List annotationOrder = new ArrayList(); MapboxMapController build( int id, Context context, BinaryMessenger messenger, MapboxMapsPlugin.LifecycleProvider lifecycleProvider, String accessToken) { final MapboxMapController controller = - new MapboxMapController(id, context, messenger, lifecycleProvider, options, accessToken, styleString); + new MapboxMapController(id, context, messenger, lifecycleProvider, options, accessToken, styleString, annotationOrder); controller.init(); controller.setMyLocationEnabled(myLocationEnabled); controller.setMyLocationTrackingMode(myLocationTrackingMode); @@ -171,4 +174,8 @@ public void setAttributionButtonMargins(int x, int y) { (int) y, //bottom }); } + + public void setAnnotationOrder(List annotations) { + this.annotationOrder = annotations; + } } diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java index 955ca7364..f737ac5a3 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapController.java @@ -71,12 +71,10 @@ import java.io.IOException; import java.io.InputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; import java.util.List; -import java.util.Map; +import java.util.ArrayList; import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.MethodCall; @@ -132,6 +130,7 @@ final class MapboxMapController private LocationEngineCallback locationEngineCallback = null; private LocalizationPlugin localizationPlugin; private Style style; + private List annotationOrder = new ArrayList(); MapboxMapController( int id, @@ -140,7 +139,8 @@ final class MapboxMapController MapboxMapsPlugin.LifecycleProvider lifecycleProvider, MapboxMapOptions options, String accessToken, - String styleStringInitial) { + String styleStringInitial, + List annotationOrder) { MapBoxUtils.getMapbox(context, accessToken); this.id = id; this.context = context; @@ -154,6 +154,7 @@ final class MapboxMapController this.lifecycleProvider = lifecycleProvider; methodChannel = new MethodChannel(messenger, "plugins.flutter.io/mapbox_maps_" + id); methodChannel.setMethodCallHandler(this); + this.annotationOrder = annotationOrder; } @Override @@ -289,10 +290,25 @@ public void setStyleString(String styleString) { @Override public void onStyleLoaded(@NonNull Style style) { MapboxMapController.this.style = style; - enableLineManager(style); - enableSymbolManager(style); - enableCircleManager(style); - enableFillManager(style); + for(String annotationType : annotationOrder) { + switch (annotationType) { + case "AnnotationType.fill": + enableFillManager(style); + break; + case "AnnotationType.line": + enableLineManager(style); + break; + case "AnnotationType.circle": + enableCircleManager(style); + break; + case "AnnotationType.symbol": + enableSymbolManager(style); + break; + default: + throw new IllegalArgumentException("Unknown annotation type: " + annotationType + ", must be either 'fill', 'line', 'circle' or 'symbol'"); + } + } + if (myLocationEnabled) { enableLocationComponent(style); } diff --git a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapFactory.java b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapFactory.java index 2cf7b345e..4c2d378c9 100644 --- a/android/src/main/java/com/mapbox/mapboxgl/MapboxMapFactory.java +++ b/android/src/main/java/com/mapbox/mapboxgl/MapboxMapFactory.java @@ -4,13 +4,16 @@ import com.mapbox.mapboxsdk.camera.CameraPosition; -import java.util.Map; - import io.flutter.plugin.common.BinaryMessenger; import io.flutter.plugin.common.StandardMessageCodec; import io.flutter.plugin.platform.PlatformView; import io.flutter.plugin.platform.PlatformViewFactory; +import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.List; +import java.util.ArrayList; + public class MapboxMapFactory extends PlatformViewFactory { private final BinaryMessenger messenger; @@ -32,6 +35,10 @@ public PlatformView create(Context context, int id, Object args) { CameraPosition position = Convert.toCameraPosition(params.get("initialCameraPosition")); builder.setInitialCameraPosition(position); } + if (params.containsKey("annotationOrder")) { + List annotations = Convert.toAnnotationOrder(params.get("annotationOrder")); + builder.setAnnotationOrder(annotations); + } return builder.build(id, context, messenger, lifecycleProvider, (String) params.get("accessToken")); } } diff --git a/example/lib/annotation_order_maps.dart b/example/lib/annotation_order_maps.dart new file mode 100644 index 000000000..03263ad1d --- /dev/null +++ b/example/lib/annotation_order_maps.dart @@ -0,0 +1,159 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:mapbox_gl/mapbox_gl.dart'; + +import 'main.dart'; +import 'page.dart'; + +class AnnotationOrderPage extends ExamplePage { + AnnotationOrderPage() + : super(const Icon(Icons.layers), 'Annotation order maps'); + + @override + Widget build(BuildContext context) => AnnotationOrderBody(); +} + +class AnnotationOrderBody extends StatefulWidget { + AnnotationOrderBody(); + + @override + _AnnotationOrderBodyState createState() => _AnnotationOrderBodyState(); +} + +class _AnnotationOrderBodyState extends State { + MapboxMapController controllerOne; + MapboxMapController controllerTwo; + + final LatLng center = const LatLng(36.580664, 32.5563837); + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 5.0), + child: Text( + 'This map has polygones (fill) above all other anotations (default behavior)'), + ), + Center( + child: SizedBox( + width: 250.0, + height: 250.0, + child: MapboxMap( + accessToken: MapsDemo.ACCESS_TOKEN, + onMapCreated: onMapCreatedOne, + onStyleLoadedCallback: () => onStyleLoaded(controllerOne), + initialCameraPosition: CameraPosition( + target: center, + zoom: 5.0, + ), + annotationOrder: const [ + AnnotationType.line, + AnnotationType.symbol, + AnnotationType.circle, + AnnotationType.fill, + ], + ), + ), + ), + ], + ), + ), + ), + Card( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0.0), + child: Column( + children: [ + const Padding( + padding: EdgeInsets.only(bottom: 5.0, top: 5.0), + child: Text( + 'This map has polygones (fill) under all other anotations'), + ), + Center( + child: SizedBox( + width: 250.0, + height: 250.0, + child: MapboxMap( + accessToken: MapsDemo.ACCESS_TOKEN, + onMapCreated: onMapCreatedTwo, + onStyleLoadedCallback: () => onStyleLoaded(controllerTwo), + initialCameraPosition: CameraPosition( + target: center, + zoom: 5.0, + ), + annotationOrder: const [ + AnnotationType.fill, + AnnotationType.line, + AnnotationType.symbol, + AnnotationType.circle, + ], + ), + ), + ), + ], + ), + ), + ), + ], + ); + } + + void onMapCreatedOne(MapboxMapController controller) { + this.controllerOne = controller; + } + + void onMapCreatedTwo(MapboxMapController controller) { + this.controllerTwo = controller; + } + + void onStyleLoaded(MapboxMapController controller) { + controller.addSymbol( + SymbolOptions( + geometry: LatLng( + center.latitude, + center.longitude, + ), + iconImage: "airport-15", + ), + ); + controller.addLine( + LineOptions( + draggable: false, + lineColor: "#ff0000", + lineWidth: 7.0, + lineOpacity: 1, + geometry: [ + LatLng(35.3649902, 32.0593003), + LatLng(34.9475098, 31.1187944), + LatLng(36.7108154, 30.7040582), + LatLng(37.6995850, 33.6512083), + LatLng(35.8648682, 33.6969227), + LatLng(35.3814697, 32.0546447), + ], + ), + ); + controller.addFill( + FillOptions( + draggable: false, + fillColor: "#008888", + fillOpacity: 0.3, + geometry: [ + [ + LatLng(35.3649902, 32.0593003), + LatLng(34.9475098, 31.1187944), + LatLng(36.7108154, 30.7040582), + LatLng(37.6995850, 33.6512083), + LatLng(35.8648682, 33.6969227), + LatLng(35.3814697, 32.0546447), + ] + ], + ), + ); + } +} diff --git a/example/lib/main.dart b/example/lib/main.dart index cc11e665f..8b159e77e 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -9,6 +9,7 @@ import 'package:mapbox_gl_example/full_map.dart'; import 'package:mapbox_gl_example/offline_regions.dart'; import 'animate_camera.dart'; +import 'annotation_order_maps.dart'; import 'full_map.dart'; import 'line.dart'; import 'map_ui.dart'; @@ -32,6 +33,7 @@ final List _allPages = [ PlaceFillPage(), ScrollingMapPage(), OfflineRegionsPage(), + AnnotationOrderPage(), ]; class MapsDemo extends StatelessWidget { diff --git a/ios/Classes/MapboxMapController.swift b/ios/Classes/MapboxMapController.swift index ab303cfe5..c3167d24f 100644 --- a/ios/Classes/MapboxMapController.swift +++ b/ios/Classes/MapboxMapController.swift @@ -22,6 +22,8 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma private var lineAnnotationController: MGLLineAnnotationController? private var fillAnnotationController: MGLPolygonAnnotationController? + private var annotationOrder = [String]() + func view() -> UIView { return mapView } @@ -63,6 +65,9 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma mapView.setCenter(camera.centerCoordinate, zoomLevel: zoom, direction: camera.heading, animated: false) initialTilt = camera.pitch } + if let annotationOrderArg = args["annotationOrder"] as? [String] { + annotationOrder = annotationOrderArg + } } } @@ -671,22 +676,29 @@ class MapboxMapController: NSObject, FlutterPlatformView, MGLMapViewDelegate, Ma mapView.setCamera(camera, animated: false) } - lineAnnotationController = MGLLineAnnotationController(mapView: self.mapView) - lineAnnotationController!.annotationsInteractionEnabled = true - lineAnnotationController?.delegate = self - - symbolAnnotationController = MGLSymbolAnnotationController(mapView: self.mapView) - symbolAnnotationController!.annotationsInteractionEnabled = true - symbolAnnotationController?.delegate = self - - circleAnnotationController = MGLCircleAnnotationController(mapView: self.mapView) - circleAnnotationController!.annotationsInteractionEnabled = true - circleAnnotationController?.delegate = self + for annotationType in annotationOrder { + switch annotationType { + case "AnnotationType.fill": + fillAnnotationController = MGLPolygonAnnotationController(mapView: self.mapView) + fillAnnotationController!.annotationsInteractionEnabled = true + fillAnnotationController?.delegate = self + case "AnnotationType.line": + lineAnnotationController = MGLLineAnnotationController(mapView: self.mapView) + lineAnnotationController!.annotationsInteractionEnabled = true + lineAnnotationController?.delegate = self + case "AnnotationType.circle": + circleAnnotationController = MGLCircleAnnotationController(mapView: self.mapView) + circleAnnotationController!.annotationsInteractionEnabled = true + circleAnnotationController?.delegate = self + case "AnnotationType.symbol": + symbolAnnotationController = MGLSymbolAnnotationController(mapView: self.mapView) + symbolAnnotationController!.annotationsInteractionEnabled = true + symbolAnnotationController?.delegate = self + default: + print("Unknown annotation type: \(annotationType), must be either 'fill', 'line', 'circle' or 'symbol'") + } + } - fillAnnotationController = MGLPolygonAnnotationController(mapView: self.mapView) - fillAnnotationController!.annotationsInteractionEnabled = true - fillAnnotationController?.delegate = self - mapReadyResult?(nil) if let channel = channel { channel.invokeMethod("map#onStyleLoaded", arguments: nil) diff --git a/lib/src/mapbox_map.dart b/lib/src/mapbox_map.dart index dfe8f0c24..faf676829 100644 --- a/lib/src/mapbox_map.dart +++ b/lib/src/mapbox_map.dart @@ -4,6 +4,8 @@ part of mapbox_gl; +enum AnnotationType { fill, line, circle, symbol } + typedef void MapCreatedCallback(MapboxMapController controller); class MapboxMap extends StatefulWidget { @@ -37,9 +39,21 @@ class MapboxMap extends StatefulWidget { this.onCameraTrackingChanged, this.onCameraIdle, this.onMapIdle, + this.annotationOrder = const [ + AnnotationType.line, + AnnotationType.symbol, + AnnotationType.circle, + AnnotationType.fill, + ], }) : assert(initialCameraPosition != null), + assert(annotationOrder != null), + assert(annotationOrder.length == 4), super(key: key); + /// Defined the layer order of annotations displayed on map + /// (must contain all annotation types, 4 items) + final List annotationOrder; + /// If you want to use Mapbox hosted styles and map tiles, you need to provide a Mapbox access token. /// Obtain a free access token on [your Mapbox account page](https://www.mapbox.com/account/access-tokens/). /// The reccommended way is to use this parameter to set your access token, an alternative way to add your access tokens through external files is described in the plugin's wiki on Github. @@ -183,10 +197,13 @@ class _MapboxMapState extends State { @override Widget build(BuildContext context) { + final List annotationOrder = widget.annotationOrder.map((e) => e.toString()).toList(); + final Map creationParams = { 'initialCameraPosition': widget.initialCameraPosition?.toMap(), 'options': _MapboxMapOptions.fromWidget(widget).toMap(), 'accessToken': widget.accessToken, + 'annotationOrder': annotationOrder, }; return _mapboxGlPlatform.buildView( creationParams, onPlatformViewCreated, widget.gestureRecognizers); @@ -225,9 +242,9 @@ class _MapboxMapState extends State { if (_controller.isCompleted) { widget.onStyleLoadedCallback(); } else { - _controller.future.then((_) => widget.onStyleLoadedCallback()); - } - }, + _controller.future.then((_) => widget.onStyleLoadedCallback()); + } + }, onMapClick: widget.onMapClick, onUserLocationUpdated: widget.onUserLocationUpdated, onMapLongClick: widget.onMapLongClick,