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 276ce423..b921c5c0 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,6 +4,7 @@ import android.content.Context import android.view.LayoutInflater import android.view.View import com.esri.arcgisruntime.ArcGISRuntimeEnvironment +import com.esri.arcgisruntime.concurrent.ListenableFuture import com.esri.arcgisruntime.geometry.GeometryEngine import com.esri.arcgisruntime.geometry.Point import com.esri.arcgisruntime.geometry.PointCollection @@ -60,6 +61,8 @@ internal class ArcgisMapView( private val defaultGraphicsOverlay = GraphicsOverlay() private val graphicsParser = GraphicsParser(binding) + private val initialZoom: Int + private lateinit var zoomStreamHandler: ZoomStreamHandler private lateinit var centerPositionStreamHandler: CenterPositionStreamHandler @@ -72,6 +75,8 @@ internal class ArcgisMapView( mapOptions.apiKey?.let(ArcGISRuntimeEnvironment::setApiKey) mapOptions.licenseKey?.let(ArcGISRuntimeEnvironment::setLicense) + initialZoom = mapOptions.zoom.roundToInt() + mapView = view.findViewById(R.id.mapView) map.apply { @@ -183,35 +188,21 @@ internal class ArcgisMapView( } private fun onStartLocationDisplayDataSource(result: MethodChannel.Result) { - val future = mapView.locationDisplay.locationDataSource.startAsync() - future.addDoneListener { - try { - result.success(future.get()) - } catch (e: Exception) { - result.error("Error", e.message, null) - } - } + result.finishWithFuture { mapView.locationDisplay.locationDataSource.startAsync() } } private fun onStopLocationDisplayDataSource(result: MethodChannel.Result) { - val future = mapView.locationDisplay.locationDataSource.stopAsync() - future.addDoneListener { - try { - result.success(future.get()) - } catch (e: Exception) { - result.error("Error", e.message, null) - } - } + result.finishWithFuture { mapView.locationDisplay.locationDataSource.stopAsync() } } private fun onSetLocationDisplayDefaultSymbol(call: MethodCall, result: MethodChannel.Result) { - operationWithSymbol(call, result) { symbol -> + finishOperationWithSymbol(call, result) { symbol -> mapView.locationDisplay.defaultSymbol = symbol } } private fun onSetLocationDisplayAccuracySymbol(call: MethodCall, result: MethodChannel.Result) { - operationWithSymbol(call, result) { symbol -> + finishOperationWithSymbol(call, result) { symbol -> mapView.locationDisplay.accuracySymbol = symbol } } @@ -220,7 +211,7 @@ internal class ArcgisMapView( call: MethodCall, result: MethodChannel.Result ) { - operationWithSymbol(call, result) { symbol -> + finishOperationWithSymbol(call, result) { symbol -> mapView.locationDisplay.pingAnimationSymbol = symbol } } @@ -233,8 +224,8 @@ internal class ArcgisMapView( val active = call.arguments as Boolean mapView.locationDisplay.isUseCourseSymbolOnMovement = active result.success(true) - } catch (e: Exception) { - result.error("missing_data", "Invalid arguments.", null) + } catch (e: Throwable) { + result.finishWithError(e) } } @@ -250,12 +241,8 @@ internal class ArcgisMapView( dataSource.setNewLocation(position) result.success(true) - } catch (e: Exception) { - result.error( - "invalid_state", - "Expected ManualLocationDataSource", - null - ) + } catch (e: Throwable) { + result.finishWithError(e) } } @@ -274,8 +261,8 @@ internal class ArcgisMapView( try { mapView.locationDisplay.locationDataSource = ManualLocationDisplayDataSource() result.success(true) - } catch (e: Exception) { - result.error("Error", "Setting datasource on mapview failed", null) + } catch (e: Throwable) { + result.finishWithError(e, info = "Setting datasource on mapview failed") } } @@ -283,8 +270,8 @@ internal class ArcgisMapView( try { mapView.locationDisplay.locationDataSource = AndroidLocationDataSource(context) result.success(true) - } catch (e: Exception) { - result.error("Error", "Setting datasource on mapview failed", null) + } catch (e: Throwable) { + result.finishWithError(e, "Setting datasource on mapview failed") } } @@ -294,22 +281,6 @@ internal class ArcgisMapView( } - private fun operationWithSymbol( - call: MethodCall, - result: MethodChannel.Result, - function: (Symbol) -> Unit - ) { - try { - val map = call.arguments as Map - val symbol = graphicsParser.parseSymbol(map) - function(symbol) - result.success(true) - } catch (e: Throwable) { - result.error("unknown_error", "Error while adding graphic. $e)", null) - return - } - } - private fun setupEventChannel() { zoomStreamHandler = ZoomStreamHandler() centerPositionStreamHandler = CenterPositionStreamHandler() @@ -334,21 +305,17 @@ internal class ArcgisMapView( return } - val lodFactor = call.argument("lodFactor")!! - val currentZoomLevel = getZoomLevel(mapView) - val totalZoomLevel = currentZoomLevel + lodFactor - if (totalZoomLevel > mapOptions.maxZoom) { - return - } - val newScale = getMapScale(totalZoomLevel) - val future = mapView.setViewpointScaleAsync(newScale) - future.addDoneListener { - try { - val isSuccessful = future.get() - result.success(isSuccessful) - } catch (e: Exception) { - result.error("Error", e.message, null) + try { + val lodFactor = call.argument("lodFactor")!! + val currentZoomLevel = getZoomLevel(mapView) + val totalZoomLevel = currentZoomLevel + lodFactor + if (totalZoomLevel > mapOptions.maxZoom) { + return } + val newScale = getMapScale(totalZoomLevel) + result.finishWithFuture { mapView.setViewpointScaleAsync(newScale) } + } catch (e: Throwable) { + result.finishWithError(e) } } @@ -362,159 +329,175 @@ internal class ArcgisMapView( return } - val lodFactor = call.argument("lodFactor")!! - val currentZoomLevel = getZoomLevel(mapView) - val totalZoomLevel = currentZoomLevel - lodFactor - if (totalZoomLevel < mapOptions.minZoom) { - return - } - val newScale = getMapScale(totalZoomLevel) - val future = mapView.setViewpointScaleAsync(newScale) - future.addDoneListener { - try { - result.success(future.get()) - } catch (e: Exception) { - result.error("Error", e.message, e) + try { + val lodFactor = call.argument("lodFactor")!! + val currentZoomLevel = getZoomLevel(mapView) + val totalZoomLevel = currentZoomLevel - lodFactor + if (totalZoomLevel < mapOptions.minZoom) { + return } + val newScale = getMapScale(totalZoomLevel) + result.finishWithFuture { mapView.setViewpointScaleAsync(newScale) } + } catch (e: Throwable) { + result.finishWithError(e) } } private fun onAddViewPadding(call: MethodCall, result: MethodChannel.Result) { - val optionParams = call.arguments as Map - val viewPadding = optionParams.parseToClass() - - // 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 - ) + try { + val optionParams = call.arguments as Map + val viewPadding = optionParams.parseToClass() + + // 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 + ) - result.success(true) + result.success(true) + } catch (e: Throwable) { + result.finishWithError(e) + } } private fun onSetInteraction(call: MethodCall, result: MethodChannel.Result) { - val enabled = call.argument("enabled")!! - - setMapInteraction(enabled = enabled) + try { + val enabled = call.argument("enabled")!! + setMapInteraction(enabled = enabled) - result.success(true) + result.success(true) + } catch (e: Throwable) { + result.finishWithError(e) + } } private fun onAddGraphic(call: MethodCall, result: MethodChannel.Result) { - val graphicArguments = call.arguments as Map - lateinit var newGraphic: List try { - newGraphic = graphicsParser.parse(graphicArguments) - } catch (e: Throwable) { - result.error("unknown_error", "Error while adding graphic. $e)", null) - return - } - - val existingIds = - defaultGraphicsOverlay.graphics.mapNotNull { it.attributes["id"] as? String } - val newIds = newGraphic.mapNotNull { it.attributes["id"] as? String } - - if (existingIds.any(newIds::contains)) { - result.success(false) - return - } + val graphicArguments = call.arguments as Map + val newGraphic: List = graphicsParser.parse(graphicArguments) + val existingIds = + defaultGraphicsOverlay.graphics.mapNotNull { it.attributes["id"] as? String } + val newIds = newGraphic.mapNotNull { it.attributes["id"] as? String } + if (existingIds.any(newIds::contains)) { + result.success(false) + return + } - defaultGraphicsOverlay.graphics.addAll(newGraphic) + defaultGraphicsOverlay.graphics.addAll(newGraphic) - updateMap() - result.success(true) + when (val future = updateMap()) { + null -> result.success(false) + else -> result.finishWithFuture { future } + } + } catch (e: Throwable) { + result.finishWithError(e) + } } private fun onRemoveGraphic(call: MethodCall, result: MethodChannel.Result) { - val graphicId = call.arguments as String + try { + val graphicId = call.arguments as String - val graphicsToRemove = defaultGraphicsOverlay.graphics.filter { graphic -> - val id = graphic.attributes["id"] as? String - graphicId == id - } + val graphicsToRemove = defaultGraphicsOverlay.graphics.filter { graphic -> + val id = graphic.attributes["id"] as? String + graphicId == id + } - // Don't use removeAll because this will not trigger a redraw. - graphicsToRemove.forEach(defaultGraphicsOverlay.graphics::remove) + // Don't use removeAll because this will not trigger a redraw. + graphicsToRemove.forEach(defaultGraphicsOverlay.graphics::remove) - updateMap() - result.success(true) + updateMap() + result.success(true) + } catch (e: Throwable) { + result.finishWithError(e) + } } private fun onMoveCamera(call: MethodCall, result: MethodChannel.Result) { - val arguments = call.arguments as Map - val point = (arguments["point"] as Map).parseToClass() - - val zoomLevel = call.argument("zoomLevel") + try { + val arguments = call.arguments as Map + val point = (arguments["point"] as Map).parseToClass() - val animationOptionMap = (arguments["animationOptions"] as Map?) + val zoomLevel = call.argument("zoomLevel") - val animationOptions = - if (animationOptionMap.isNullOrEmpty()) null - else animationOptionMap.parseToClass() + val animationOptionMap = (arguments["animationOptions"] as Map?) - val scale = if (zoomLevel != null) { - getMapScale(zoomLevel) - } else { - mapView.mapScale - } + val animationOptions = + if (animationOptionMap.isNullOrEmpty()) null + else animationOptionMap.parseToClass() - val initialViewPort = Viewpoint(point.latitude, point.longitude, scale) - val future = mapView.setViewpointAsync( - initialViewPort, - (animationOptions?.duration?.toFloat() ?: 0F) / 1000, - animationOptions?.animationCurve ?: AnimationCurve.LINEAR, - ) + val scale = if (zoomLevel != null) { + getMapScale(zoomLevel) + } else if (!mapView.mapScale.isNaN()) { + mapView.mapScale + } else { + getMapScale(initialZoom) + } - future.addDoneListener { - try { - result.success(future.get()) - } catch (e: Throwable) { - result.error("Error", e.message, e) + val initialViewPort = Viewpoint(point.latitude, point.longitude, scale) + result.finishWithFuture { + mapView.setViewpointAsync( + initialViewPort, + (animationOptions?.duration?.toFloat() ?: 0F) / 1000, + animationOptions?.animationCurve ?: AnimationCurve.LINEAR, + ) } + } catch (e: Throwable) { + result.finishWithError(e) } } 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) + try { + 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() + ) - future.addDoneListener { - try { - result.success(future.get()) - } catch (e: Exception) { - result.error("Error", e.message, e) + result.finishWithFuture { + if (padding != null) mapView.setViewpointGeometryAsync(polyline.extent, padding) + else mapView.setViewpointGeometryAsync(polyline.extent) } + } catch (e: Throwable) { + result.finishWithError(e) } } private fun onToggleBaseMap(call: MethodCall, result: MethodChannel.Result) { - val newStyle = gson.fromJson( - call.arguments as String, - object : TypeToken() {}.type - ) - map.basemap = Basemap(newStyle) - result.success(true) + try { + val newStyle = gson.fromJson( + call.arguments as String, + object : TypeToken() {}.type + ) + map.basemap = Basemap(newStyle) + result.success(true) + } catch (e: Throwable) { + result.finishWithError(e) + } } private fun onRetryLoad(result: MethodChannel.Result) { - mapView.map?.retryLoadAsync() - result.success(true) + try { + mapView.map?.retryLoadAsync() + result.success(true) + } catch (e: Exception) { + result.finishWithError(e) + } } /** @@ -533,11 +516,11 @@ internal class ArcgisMapView( * The corresponding issue in the esri forum can be found here: * https://community.esri.com/t5/arcgis-runtime-sdk-for-android-questions/mapview-graphicsoverlays-add-does-not-update-the/m-p/1240825#M5931 */ - private fun updateMap() { + private fun updateMap(): ListenableFuture? { if (mapView.mapScale.isNaN()) { - return + return null } - mapView.setViewpointScaleAsync(mapView.mapScale) + return mapView.setViewpointScaleAsync(mapView.mapScale) } /** @@ -559,6 +542,48 @@ internal class ArcgisMapView( } } + // region helper methods + + private fun MethodChannel.Result.finishWithFuture(function: () -> ListenableFuture<*>) { + try { + val future = function() + future.addDoneListener { + try { + future.get() + success(true) + } catch (e: Throwable) { + finishWithError(e) + } + } + } catch (e: Throwable) { + finishWithError(e) + } + } + + private fun MethodChannel.Result.finishWithError(e: Throwable, info: String? = null) { + val msg = StringBuilder().apply { + if (info != null) append(info) + append(e.localizedMessage ?: e.message ?: "$e") + }.toString() + error("unknown_error", msg, null) + } + + + private fun finishOperationWithSymbol( + call: MethodCall, + result: MethodChannel.Result, + function: (Symbol) -> Unit + ) { + try { + val map = call.arguments as Map + val symbol = graphicsParser.parseSymbol(map) + function(symbol) + result.success(true) + } catch (e: Throwable) { + result.finishWithError(e, info = "Error while adding graphic.") + } + } + // endregion } diff --git a/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapViewFactory.kt b/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapViewFactory.kt index 8daa76c4..9dcccf68 100644 --- a/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapViewFactory.kt +++ b/arcgis_map_sdk_android/android/src/main/kotlin/dev/fluttercommunity/arcgis_map_sdk_android/ArcgisMapViewFactory.kt @@ -6,6 +6,7 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin.FlutterPluginBinding import io.flutter.plugin.common.StandardMessageCodec import io.flutter.plugin.platform.PlatformView import io.flutter.plugin.platform.PlatformViewFactory +import dev.fluttercommunity.arcgis_map_sdk_android.ArcgisMapView class ArcgisMapViewFactory(private val flutterPluginBinding: FlutterPluginBinding) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { diff --git a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift index 45ba2f65..8875e386 100644 --- a/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift +++ b/arcgis_map_sdk_ios/ios/Classes/ArcgisMapView.swift @@ -17,6 +17,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { private var mapScaleObservation: NSKeyValueObservation? private var mapVisibleAreaObservation: NSKeyValueObservation? + private let initialZoom: Int + private var mapView: AGSMapView private let map = AGSMap() private let graphicsOverlay = AGSGraphicsOverlay() @@ -68,6 +70,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } } + initialZoom = Int(mapOptions.zoom) + mapView = AGSMapView.init(frame: frame) super.init() @@ -81,8 +85,8 @@ class ArcgisMapView: NSObject, FlutterPlatformView { map.basemap = AGSBasemap(baseLayers: layers, referenceLayers: nil) } - map.minScale = getMapScale(mapOptions.minZoom) - map.maxScale = getMapScale(mapOptions.maxZoom) + map.minScale = convertZoomLevelToMapScale(mapOptions.minZoom) + map.maxScale = convertZoomLevelToMapScale(mapOptions.maxZoom) mapView.map = map mapView.graphicsOverlays.add(defaultGraphicsOverlay) @@ -92,7 +96,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { guard let self = self else { return } - let newZoom = self.getZoomLevel(self.mapView.mapScale) + let newZoom = self.convertScaleToZoomLevel(self.mapView.mapScale) self.zoomStreamHandler.addZoom(zoom: newZoom) } } @@ -113,7 +117,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { let viewpoint = AGSViewpoint( latitude: mapOptions.initialCenter.latitude, longitude: mapOptions.initialCenter.longitude, - scale: getMapScale(Int(mapOptions.zoom)) + scale: convertZoomLevelToMapScale(Int(mapOptions.zoom)) ) mapView.setViewpoint(viewpoint) @@ -161,13 +165,22 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let lodFactor = (call.arguments! as! Dictionary)["lodFactor"]! as! Int - let currentZoomLevel = getZoomLevel(mapView.mapScale) + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } + + guard let lodFactor = args["lodFactor"] as? Int else { + result(FlutterError(code: "missing_data", message: "lodFactor not provided", details: nil)) + return + } + + let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) let totalZoomLevel = currentZoomLevel + lodFactor - if (totalZoomLevel > getZoomLevel(map.maxScale)) { + if (totalZoomLevel > convertScaleToZoomLevel(map.maxScale)) { return } - let newScale = getMapScale(totalZoomLevel) + let newScale = convertZoomLevelToMapScale(totalZoomLevel) mapView.setViewpointScale(newScale) { _ in result(true) } @@ -179,65 +192,101 @@ class ArcgisMapView: NSObject, FlutterPlatformView { return } - let lodFactor = (call.arguments! as! Dictionary)["lodFactor"]! as! Int - let currentZoomLevel = getZoomLevel(mapView.mapScale) + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } + + guard let lodFactor = args["lodFactor"] as? Int else { + result(FlutterError(code: "missing_data", message: "lodFactor not provided", details: nil)) + return + } + + let currentZoomLevel = convertScaleToZoomLevel(mapView.mapScale) let totalZoomLevel = currentZoomLevel - lodFactor - if (totalZoomLevel < getZoomLevel(map.minScale)) { + if (totalZoomLevel < convertScaleToZoomLevel(map.minScale)) { return } - let newScale = getMapScale(totalZoomLevel) + let newScale = convertZoomLevelToMapScale(totalZoomLevel) mapView.setViewpointScale(newScale) { success in result(success) } } private func onAddViewPadding(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let dict = call.arguments as! Dictionary - let padding: ViewPadding = try! JsonUtil.objectOfJson(dict) - - mapView.contentInset = UIEdgeInsets( - top: padding.top, - left: padding.left, - bottom: padding.bottom, - right: padding.right - ) + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } + + do { + let padding: ViewPadding = try JsonUtil.objectOfJson(args) - result(true) + mapView.contentInset = UIEdgeInsets( + top: padding.top, + left: padding.left, + bottom: padding.bottom, + right: padding.right + ) + + result(true) + } catch { + result(FlutterError(code: "error", message: "Parsing data failed. Provided: \(args)", details: nil)) + } } private func onMoveCamera(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let dict = call.arguments as! Dictionary - let point: LatLng = try! JsonUtil.objectOfJson(dict["point"] as! Dictionary) - let zoomLevel = dict["zoomLevel"] as? Int - - let animationDict = dict["animationOptions"] as? Dictionary - let animationOptions: AnimationOptions? = animationDict == nil ? nil : try? JsonUtil.objectOfJson(animationDict!) + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } + do { + let point: LatLng = try JsonUtil.objectOfJson(args["point"] as! Dictionary) + let zoomLevel = args["zoomLevel"] as? Int + let animationDict = args["animationOptions"] as? Dictionary + let animationOptions: AnimationOptions? = animationDict == nil ? nil : try JsonUtil.objectOfJson(animationDict!) - let scale = zoomLevel != nil ? getMapScale(zoomLevel!) : mapView.mapScale + let scale: Double + + if let zoomLevel = zoomLevel { + scale = convertZoomLevelToMapScale(zoomLevel) + } else { + scale = mapView.mapScale.isNaN ? convertZoomLevelToMapScale(initialZoom) : mapView.mapScale + } - mapView.setViewpoint( - AGSViewpoint(center: point.toAGSPoint(), scale: scale), - duration: (animationOptions?.duration ?? 0) / 1000, - curve: animationOptions?.arcgisAnimationCurve() ?? .linear - ) { success in - result(success) + mapView.setViewpoint( + AGSViewpoint(center: point.toAGSPoint(), scale: scale), + duration: (animationOptions?.duration ?? 0) / 1000, + curve: animationOptions?.arcgisAnimationCurve() ?? .linear + ) { success in + result(success) + } + } catch { + result(FlutterError(code: "error", message: "Error onMoveCamera. Provided: \(args)", details: nil)) } } private func onMoveCameraToPoints(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let dict = call.arguments as! Dictionary + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } - 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()) }) + do { + let payload: MoveToPointsPayload = try JsonUtil.objectOfJson(args) + 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) + if(payload.padding != nil) { + mapView.setViewpointGeometry(polyline.extent, padding: payload.padding!) { success in + result(success) + } + } else { + mapView.setViewpointGeometry(polyline.extent) { success in + result(success) + } } + } catch { + result(FlutterError(code: "error", message: "Error onMoveCameraToPoints. Provided: \(args)", details: nil)) } } @@ -281,7 +330,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } private func onRemoveGraphic(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let graphicId = call.arguments as! String + guard let graphicId = call.arguments as? String else { + result(FlutterError(code: "missing_data", message: "graphicId not provided", details: nil)) + return + } + let newGraphics = defaultGraphicsOverlay.graphics.filter({ element in let graphic = element as! AGSGraphic let id = graphic.attributes["id"] as? String @@ -295,7 +348,11 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } private func onToggleBaseMap(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let baseMapString = call.arguments as! String + guard let baseMapString = call.arguments as? String else { + result(FlutterError(code: "missing_data", message: "baseMapString not provided", details: nil)) + return + } + map.basemap = AGSBasemap(style: parseBaseMapStyle(baseMapString)) result(true) @@ -311,8 +368,16 @@ class ArcgisMapView: NSObject, FlutterPlatformView { } private func onSetInteraction(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { - let enabled = (call.arguments! as! Dictionary)["enabled"]! as! Bool - + guard let args = call.arguments as? [String: Any] else { + result(FlutterError(code: "missing_data", message: "Invalid arguments", details: nil)) + return + } + + guard let enabled = args["enabled"] as? Bool else { + result(FlutterError(code: "missing_data", message: "enabled arguments", details: nil)) + return + } + setMapInteractive(enabled) result(true) } @@ -340,7 +405,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { * Convert map scale to zoom level * https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool * */ - private func getZoomLevel(_ scale: Double) -> Int { + private func convertScaleToZoomLevel(_ scale: Double) -> Int { let result = -1.443 * log(scale) + 29.14 return Int(result.rounded()) } @@ -349,7 +414,7 @@ class ArcgisMapView: NSObject, FlutterPlatformView { * Convert zoom level to map scale * https://developers.arcgis.com/documentation/mapping-apis-and-services/reference/zoom-levels-and-scale/#conversion-tool * */ - private func getMapScale(_ zoomLevel: Int) -> Double { + private func convertZoomLevelToMapScale(_ zoomLevel: Int) -> Double { 591657527 * (exp(-0.693 * Double(zoomLevel))) } @@ -607,6 +672,11 @@ extension AGSBasemapStyle { } } +struct MoveToPointsPayload : Codable { + let points : [LatLng] + let padding : Double? +} + extension AGSLoadStatus { func jsonValue() -> String { switch self { @@ -625,8 +695,3 @@ extension AGSLoadStatus { } } } - -struct MoveToPointsPayload : Codable { - let points : [LatLng] - let padding : Double? -} diff --git a/example/lib/main.dart b/example/lib/main.dart index af6e5531..dc5e48b1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,6 +1,5 @@ import 'dart:async'; import 'dart:core'; -import 'dart:io'; import 'package:arcgis_example/location_indicator_example_page.dart'; import 'package:arcgis_example/map_elements.dart'; diff --git a/example/pubspec.lock b/example/pubspec.lock index 0c7245f7..f212c28c 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -79,10 +79,10 @@ packages: dependency: transitive description: name: collection - sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted - version: "1.17.2" + version: "1.18.0" crypto: dependency: transitive description: @@ -222,10 +222,10 @@ packages: dependency: transitive description: name: meta - sha256: "3c74dbf8763d36539f114c799d8a2d87343b5067e9d796ca22b5eb8437090ee3" + sha256: a6e590c838b18133bb482a2745ad77c5bb7715fb0451209e1a7567d416678b8e url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" path: dependency: transitive description: @@ -267,18 +267,18 @@ packages: dependency: transitive description: name: stack_trace - sha256: c3c7d8edb15bee7f0f74debd4b9c5f3c2ea86766fe4178eb2a18eb30a0bdaed5 + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: "83615bee9045c1d322bbbd1ba209b7a749c2cbcdcb3fdd1df8eb488b3279c1c8" + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" string_scanner: dependency: transitive description: @@ -299,10 +299,10 @@ packages: dependency: transitive description: name: test_api - sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" url: "https://pub.dev" source: hosted - version: "0.6.0" + version: "0.6.1" typed_data: dependency: transitive description: @@ -331,10 +331,10 @@ packages: dependency: transitive description: name: web - sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + sha256: afe077240a270dcfd2aafe77602b4113645af95d0ad31128cc02bce5ac5d5152 url: "https://pub.dev" source: hosted - version: "0.1.4-beta" + version: "0.3.0" sdks: - dart: ">=3.1.0 <4.0.0" + dart: ">=3.2.0-194.0.dev <4.0.0" flutter: ">=3.10.0"