From e52fb73bd9a0cd5c4920493f8acc82919b6ab3ce Mon Sep 17 00:00:00 2001 From: sbergmair <76773309+sbergmair@users.noreply.github.com> Date: Mon, 11 Dec 2023 07:57:10 +0100 Subject: [PATCH] Move and zoom camera to make points visible (#56) * implement feature * implement feature * add example on button click * reformat * review * update README.md and add button for example only on mobile --- arcgis_map_sdk/README.md | 1 + .../lib/src/arcgis_map_controller.dart | 11 +++ .../arcgis_map_sdk_android/ArcgisMapView.kt | 81 +++++++++++++------ .../ios/Classes/ArcgisMapView.swift | 25 +++++- .../src/method_channel_arcgis_map_plugin.dart | 15 ++++ .../arcgis_map_sdk_platform_interface.dart | 8 ++ example/ios/Podfile.lock | 2 +- example/lib/main.dart | 14 ++++ 8 files changed, 129 insertions(+), 28 deletions(-) diff --git a/arcgis_map_sdk/README.md b/arcgis_map_sdk/README.md index 8c636895..9d9ea5e7 100644 --- a/arcgis_map_sdk/README.md +++ b/arcgis_map_sdk/README.md @@ -108,6 +108,7 @@ Checkout the example app `example/lib/main.dart` for more details. | addViewPadding | ✅ | ✅ | ✅ | | toggleBaseMap | ✅ | ✅ | ✅ | | moveCamera | ✅ | ✅ | ✅ | +| moveCameraToPoints | | ✅ | ✅ | | zoomIn | ✅ | ✅ | ✅ | | zoomOut | ✅ | ✅ | ✅ | | getZoom | ✅ | ✅ | ✅ | diff --git a/arcgis_map_sdk/lib/src/arcgis_map_controller.dart b/arcgis_map_sdk/lib/src/arcgis_map_controller.dart index 8b84a44d..28893f99 100644 --- a/arcgis_map_sdk/lib/src/arcgis_map_controller.dart +++ b/arcgis_map_sdk/lib/src/arcgis_map_controller.dart @@ -149,6 +149,17 @@ class ArcgisMapController { ); } + Future moveCameraToPoints({ + required List points, + double? padding, + }) { + return ArcgisMapPlatform.instance.moveCameraToPoints( + mapId: mapId, + points: points, + padding: padding, + ); + } + Future setInteraction({required bool isEnabled}) { return ArcgisMapPlatform.instance .setInteraction(mapId, isEnabled: isEnabled); diff --git a/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapView.kt b/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapView.kt index c7086c52..d662044f 100644 --- a/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapView.kt +++ b/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapView.kt @@ -4,8 +4,12 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import com.esri.arcgisruntime.ArcGISRuntimeEnvironment +import com.esri.arcgisruntime.geometry.Geometry import com.esri.arcgisruntime.geometry.GeometryEngine +import com.esri.arcgisruntime.geometry.Multipoint import com.esri.arcgisruntime.geometry.Point +import com.esri.arcgisruntime.geometry.PointCollection +import com.esri.arcgisruntime.geometry.Polyline import com.esri.arcgisruntime.geometry.SpatialReferences import com.esri.arcgisruntime.layers.ArcGISVectorTiledLayer import com.esri.arcgisruntime.mapping.ArcGISMap @@ -37,10 +41,10 @@ import kotlin.math.roundToInt * A starting point for documentation can be found here: https://developers.arcgis.com/android/maps-2d/tutorials/display-a-map/ * */ internal class ArcgisMapView( - context: Context, - private val viewId: Int, - private val binaryMessenger: BinaryMessenger, - private val mapOptions: ArcgisMapOptions, + context: Context, + private val viewId: Int, + private val binaryMessenger: BinaryMessenger, + private val mapOptions: ArcgisMapOptions, ) : PlatformView { private val view: View = LayoutInflater.from(context).inflate(R.layout.vector_map_view, null) @@ -52,7 +56,7 @@ internal class ArcgisMapView( private lateinit var centerPositionStreamHandler: CenterPositionStreamHandler private val methodChannel = - MethodChannel(binaryMessenger, "dev.fluttercommunity.arcgis_map_sdk/$viewId") + MethodChannel(binaryMessenger, "dev.fluttercommunity.arcgis_map_sdk/$viewId") override fun getView(): View = view @@ -81,18 +85,18 @@ internal class ArcgisMapView( mapView.addViewpointChangedListener { val center = mapView.visibleArea.extent.center val wgs84Center = - GeometryEngine.project(center, SpatialReferences.getWgs84()) as Point + GeometryEngine.project(center, SpatialReferences.getWgs84()) as Point centerPositionStreamHandler.add( - LatLng( - longitude = wgs84Center.x, - latitude = wgs84Center.y - ) + LatLng( + longitude = wgs84Center.x, + latitude = wgs84Center.y + ) ) } val viewPoint = Viewpoint( - mapOptions.initialCenter.latitude, mapOptions.initialCenter.longitude, - getMapScale(mapOptions.zoom.roundToInt()), + mapOptions.initialCenter.latitude, mapOptions.initialCenter.longitude, + getMapScale(mapOptions.zoom.roundToInt()), ) mapView.setViewpoint(viewPoint) @@ -114,6 +118,7 @@ internal class ArcgisMapView( "add_view_padding" -> onAddViewPadding(call = call, result = result) "set_interaction" -> onSetInteraction(call = call, result = result) "move_camera" -> onMoveCamera(call = call, result = result) + "move_camera_to_points" -> onMoveCameraToPoints(call = call, result = result) "add_graphic" -> onAddGraphic(call = call, result = result) "remove_graphic" -> onRemoveGraphic(call = call, result = result) "toggle_base_map" -> onToggleBaseMap(call = call, result = result) @@ -127,10 +132,10 @@ internal class ArcgisMapView( centerPositionStreamHandler = CenterPositionStreamHandler() EventChannel(binaryMessenger, "dev.fluttercommunity.arcgis_map_sdk/$viewId/zoom") - .setStreamHandler(zoomStreamHandler) + .setStreamHandler(zoomStreamHandler) EventChannel(binaryMessenger, "dev.fluttercommunity.arcgis_map_sdk/$viewId/centerPosition") - .setStreamHandler(centerPositionStreamHandler) + .setStreamHandler(centerPositionStreamHandler) } private fun onZoomIn(call: MethodCall, result: MethodChannel.Result) { @@ -176,10 +181,10 @@ internal class ArcgisMapView( // https://developers.arcgis.com/android/api-reference/reference/com/esri/arcgisruntime/mapping/view/MapView.html#setViewInsets(double,double,double,double) mapView.setViewInsets( - viewPadding.left, - viewPadding.top, - viewPadding.right, - viewPadding.bottom + viewPadding.left, + viewPadding.top, + viewPadding.right, + viewPadding.bottom ) result.success(true) @@ -204,7 +209,7 @@ internal class ArcgisMapView( } val existingIds = - defaultGraphicsOverlay.graphics.mapNotNull { it.attributes["id"] as? String } + defaultGraphicsOverlay.graphics.mapNotNull { it.attributes["id"] as? String } val newIds = newGraphic.mapNotNull { it.attributes["id"] as? String } if (existingIds.any(newIds::contains)) { @@ -244,8 +249,8 @@ internal class ArcgisMapView( val animationOptionMap = (arguments["animationOptions"] as Map?) val animationOptions = - if (animationOptionMap == null || animationOptionMap.isEmpty()) null - else animationOptionMap.parseToClass() + if (animationOptionMap == null || animationOptionMap.isEmpty()) null + else animationOptionMap.parseToClass() val scale = if (zoomLevel != null) { getMapScale(zoomLevel) @@ -255,9 +260,9 @@ internal class ArcgisMapView( val initialViewPort = Viewpoint(point.latitude, point.longitude, scale) val future = mapView.setViewpointAsync( - initialViewPort, - (animationOptions?.duration?.toFloat() ?: 0F) / 1000, - animationOptions?.animationCurve ?: AnimationCurve.LINEAR, + initialViewPort, + (animationOptions?.duration?.toFloat() ?: 0F) / 1000, + animationOptions?.animationCurve ?: AnimationCurve.LINEAR, ) future.addDoneListener { @@ -269,10 +274,34 @@ internal class ArcgisMapView( } } + private fun onMoveCameraToPoints(call: MethodCall, result: MethodChannel.Result) { + val arguments = call.arguments as Map + val latLongs = (arguments["points"] as ArrayList>) + .map { p -> parseToClass(p) } + + val padding = arguments["padding"] as Double? + + val polyline = Polyline( + PointCollection(latLongs.map { latLng -> Point(latLng.longitude, latLng.latitude) }), + SpatialReferences.getWgs84() + ) + + val future = if (padding != null) mapView.setViewpointGeometryAsync(polyline.extent, padding) + else mapView.setViewpointGeometryAsync(polyline.extent) + + future.addDoneListener { + try { + result.success(future.get()) + } catch (e: Exception) { + result.error("Error", e.message, e) + } + } + } + private fun onToggleBaseMap(call: MethodCall, result: MethodChannel.Result) { val newStyle = gson.fromJson( - call.arguments as String, - object : TypeToken() {}.type + call.arguments as String, + object : TypeToken() {}.type ) map.basemap = Basemap(newStyle) result.success(true) diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift index bc376210..44b7d077 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift @@ -123,11 +123,12 @@ class ArcgisMapView: NSObject, FlutterPlatformView { case "add_view_padding": onAddViewPadding(call, result) case "set_interaction": onSetInteraction(call, result) case "move_camera": onMoveCamera(call, result) + case "move_camera_to_points": onMoveCameraToPoints(call, result) case "add_graphic": onAddGraphic(call, result) case "remove_graphic": onRemoveGraphic(call, result) case "toggle_base_map" : onToggleBaseMap(call, result) default: - result(FlutterError(code: "Unimplemented", message: "No method matching the name\(call.method)", details: nil)) + result(FlutterError(code: "Unimplemented", message: "No method matching the name \(call.method)", details: nil)) } }) } @@ -190,6 +191,23 @@ class ArcgisMapView: NSObject, FlutterPlatformView { result(success) } } + + private func onMoveCameraToPoints(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { + let dict = call.arguments as! Dictionary + + let payload: MoveToPointsPayload = try! JsonUtil.objectOfJson(dict) + let polyline = AGSPolyline(points: payload.points.map { latLng in AGSPoint(x: latLng.longitude, y:latLng.latitude, spatialReference: .wgs84()) }) + + if(payload.padding != nil) { + mapView.setViewpointGeometry(polyline.extent, padding: payload.padding!) { success in + result(success) + } + } else { + mapView.setViewpointGeometry(polyline.extent) { success in + result(success) + } + } + } private func onAddGraphic(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { let parser = GraphicsParser() @@ -448,3 +466,8 @@ extension AGSBasemapStyle { } } } + +struct MoveToPointsPayload : Codable { + let points : [LatLng] + let padding : Double? +} diff --git a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart index 552f6c89..52a30b75 100644 --- a/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart +++ b/arcgis_map_sdk_method_channel/lib/src/method_channel_arcgis_map_plugin.dart @@ -137,6 +137,21 @@ class MethodChannelArcgisMapPlugin extends ArcgisMapPlatform { ).then((value) => value!); } + @override + Future moveCameraToPoints({ + required List points, + required int mapId, + double? padding, + }) { + return _methodChannelBuilder(mapId).invokeMethod( + "move_camera_to_points", + { + "points": points.map((p) => p.toMap()).toList(), + "padding": padding, + }, + ); + } + @override Future zoomIn({ required int lodFactor, diff --git a/arcgis_map_sdk_platform_interface/lib/src/arcgis_map_sdk_platform_interface.dart b/arcgis_map_sdk_platform_interface/lib/src/arcgis_map_sdk_platform_interface.dart index 7344db61..eeee8903 100644 --- a/arcgis_map_sdk_platform_interface/lib/src/arcgis_map_sdk_platform_interface.dart +++ b/arcgis_map_sdk_platform_interface/lib/src/arcgis_map_sdk_platform_interface.dart @@ -138,6 +138,14 @@ class ArcgisMapPlatform extends PlatformInterface { throw UnimplementedError('moveCamera() has not been implemented.'); } + Future moveCameraToPoints({ + required List points, + required int mapId, + double? padding, + }) { + throw UnimplementedError('moveCameraToPoints() has not been implemented.'); + } + Future zoomIn({ required int lodFactor, required int mapId, diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock index 390c17b8..449844cb 100644 --- a/example/ios/Podfile.lock +++ b/example/ios/Podfile.lock @@ -30,4 +30,4 @@ SPEC CHECKSUMS: PODFILE CHECKSUM: cc1f88378b4bfcf93a6ce00d2c587857c6008d3b -COCOAPODS: 1.12.1 +COCOAPODS: 1.14.2 diff --git a/example/lib/main.dart b/example/lib/main.dart index f0433577..cd16d248 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -317,6 +317,13 @@ class _ExampleMapState extends State { _controller?.removeGraphic(layerId: layerId, objectId: objectId); } + void _makePolylineVisible({required List points}) { + _controller?.moveCameraToPoints( + points: points, + padding: 30, + ); + } + void _addPolygon({ required String layerId, required PolygonGraphic graphic, @@ -650,6 +657,13 @@ class _ExampleMapState extends State { ], ), ), + if (!kIsWeb) + ElevatedButton( + onPressed: () => _makePolylineVisible( + points: [_firstPinCoordinates, _secondPinCoordinates], + ), + child: const Text('Zoom to polyline'), + ), Row( children: [ const Text(