From dc7cff24bbdc1608e1a36eaf836dc2810b621d0c Mon Sep 17 00:00:00 2001 From: liodali <16631886+liodali@users.noreply.github.com> Date: Sun, 3 Nov 2024 20:28:01 +0100 Subject: [PATCH] Improve VectorView in Android --- .../dali/flutter_osm_plugin/OsmFactory.kt | 58 +- .../map/FlutterMapLibreView.kt | 712 ++++++++++++++++++ .../{ => map}/FlutterOsmView.kt | 43 +- .../hamza/dali/flutter_osm_plugin/map/OSM.kt | 9 + .../{models => map}/OSMBase.kt | 56 +- .../maplibre/FlutterMapLibreView.kt | 389 ---------- .../dali/flutter_osm_plugin/models/Commons.kt | 9 + .../{maplibre => models}/ExtensionMapLibre.kt | 37 +- .../{ => models}/MapMedthodCall.kt | 2 +- .../overlays/CustomLocation.kt | 2 +- .../utilities/ExtensionOSM.kt | 5 +- 11 files changed, 885 insertions(+), 437 deletions(-) create mode 100644 android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterMapLibreView.kt rename android/src/main/kotlin/hamza/dali/flutter_osm_plugin/{ => map}/FlutterOsmView.kt (98%) create mode 100644 android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSM.kt rename android/src/main/kotlin/hamza/dali/flutter_osm_plugin/{models => map}/OSMBase.kt (68%) delete mode 100644 android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/FlutterMapLibreView.kt create mode 100644 android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/Commons.kt rename android/src/main/kotlin/hamza/dali/flutter_osm_plugin/{maplibre => models}/ExtensionMapLibre.kt (54%) rename android/src/main/kotlin/hamza/dali/flutter_osm_plugin/{ => models}/MapMedthodCall.kt (99%) diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/OsmFactory.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/OsmFactory.kt index 5b3dae7a..5d410f14 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/OsmFactory.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/OsmFactory.kt @@ -2,8 +2,12 @@ package hamza.dali.flutter_osm_plugin import android.app.Activity import android.content.Context +import hamza.dali.flutter_osm_plugin.map.FlutterMapLibreView +import hamza.dali.flutter_osm_plugin.map.FlutterOsmView +import hamza.dali.flutter_osm_plugin.map.OSM import hamza.dali.flutter_osm_plugin.models.CustomTile -import hamza.dali.flutter_osm_plugin.models.fromMapToCustomTile +import hamza.dali.flutter_osm_plugin.models.OSMTile +import hamza.dali.flutter_osm_plugin.models.VectorOSMTile import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.StandardMessageCodec @@ -14,7 +18,7 @@ open class OsmFactory( private val binaryMessenger: BinaryMessenger, private val provider: ProviderLifecycle, ) : PlatformViewFactory(StandardMessageCodec.INSTANCE) { - private lateinit var osmFlutterView: FlutterOsmView + private lateinit var osmFlutterView: OSM private var activity: Activity? = null private var binding: ActivityPluginBinding? = null @@ -24,28 +28,48 @@ open class OsmFactory( args: Any?, ): PlatformView { val keyUUID = (args as HashMap<*, *>)["uuid"] as String - var customTile: CustomTile? = null + var customTile: OSMTile? = null var enableRotationGesture = false + var isVector = false val staticMap = when { - args.containsKey("isStaticMap")-> args["isStaticMap"] as Boolean + args.containsKey("isStaticMap") -> args["isStaticMap"] as Boolean else -> false } - if ((args).containsKey("customTile")) { - customTile = CustomTile.fromMap(args["customTile"] as HashMap) + if (args.containsKey("isVectorTile")) { + isVector = args["isVectorTile"] as Boolean? == true } - if ((args).containsKey("enableRotationGesture")) { + customTile = when { + args.containsKey("customTile") && !isVector -> CustomTile.fromMap(args["customTile"] as HashMap) + args.containsKey("customTile") && isVector -> VectorOSMTile((args["customTile"] as HashMap)["serverStyleUrl"] as String) + else -> null + } + if (args.containsKey("enableRotationGesture")) { enableRotationGesture = args["enableRotationGesture"] as Boolean } - osmFlutterView = FlutterOsmView( - requireNotNull(context), - binaryMessenger, - viewId, - provider, - keyUUID, - customTile = customTile, - isEnabledRotationGesture = enableRotationGesture, - isStaticMap = staticMap - ) + osmFlutterView = when (isVector) { + true -> FlutterMapLibreView( + requireNotNull(context), + binaryMessenger, + viewId, + provider, + keyUUID, + customTile = customTile, + isEnabledRotationGesture = enableRotationGesture, + isStaticMap = staticMap + ) + + else -> FlutterOsmView( + requireNotNull(context), + binaryMessenger, + viewId, + provider, + keyUUID, + customTile = customTile as CustomTile, + isEnabledRotationGesture = enableRotationGesture, + isStaticMap = staticMap + ) + } + return osmFlutterView } diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterMapLibreView.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterMapLibreView.kt new file mode 100644 index 00000000..5d32f979 --- /dev/null +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterMapLibreView.kt @@ -0,0 +1,712 @@ +package hamza.dali.flutter_osm_plugin.map + +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.graphics.Bitmap +import android.os.Bundle +import android.util.Log +import android.view.View +import android.view.ViewGroup.LayoutParams.MATCH_PARENT +import android.widget.FrameLayout +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import hamza.dali.flutter_osm_plugin.ProviderLifecycle +import hamza.dali.flutter_osm_plugin.models.FlutterGeoPoint +import hamza.dali.flutter_osm_plugin.models.MapMethodChannelCall +import hamza.dali.flutter_osm_plugin.models.OSMTile +import hamza.dali.flutter_osm_plugin.models.OnClickSymbols +import hamza.dali.flutter_osm_plugin.models.OnMapMove +import hamza.dali.flutter_osm_plugin.models.VectorOSMTile +import hamza.dali.flutter_osm_plugin.models.toBoundingBox +import hamza.dali.flutter_osm_plugin.models.toBoundsLibre +import hamza.dali.flutter_osm_plugin.models.toGeoPoint +import hamza.dali.flutter_osm_plugin.models.toGeoPoints +import hamza.dali.flutter_osm_plugin.models.toList +import hamza.dali.flutter_osm_plugin.models.toLngLat +import hamza.dali.flutter_osm_plugin.models.toSymbols +import hamza.dali.flutter_osm_plugin.models.where +import hamza.dali.flutter_osm_plugin.utilities.toBitmap +import hamza.dali.flutter_osm_plugin.utilities.toGeoPoint +import hamza.dali.flutter_osm_plugin.utilities.toHashMap +import org.maplibre.android.plugins.annotation.* +import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding.OnSaveInstanceStateListener +import io.flutter.plugin.common.BinaryMessenger +import io.flutter.plugin.common.MethodCall +import io.flutter.plugin.common.MethodChannel +import io.flutter.plugin.common.MethodChannel.MethodCallHandler +import io.flutter.plugin.common.PluginRegistry +import io.flutter.plugin.platform.PlatformView +import org.maplibre.android.MapLibre +import org.maplibre.android.camera.CameraPosition +import org.maplibre.android.camera.CameraUpdate +import org.maplibre.android.maps.MapLibreMap +import org.maplibre.android.maps.MapView +import org.maplibre.android.maps.Style +import org.osmdroid.api.IGeoPoint +import org.osmdroid.util.BoundingBox +import org.osmdroid.util.GeoPoint + + +class FlutterMapLibreView( + private val context: Context, + private val binaryMessenger: BinaryMessenger, + private val idView: Int,//viewId + private val providerLifecycle: ProviderLifecycle, + private val keyArgMapSnapShot: String, + private val customTile: OSMTile?, + private val isEnabledRotationGesture: Boolean = false, + private val isStaticMap: Boolean = false, +) : OnSaveInstanceStateListener, PlatformView, MethodCallHandler, + PluginRegistry.ActivityResultListener, DefaultLifecycleObserver, OSMBase { + private lateinit var methodChannel: MethodChannel + private var activity: Activity? = null + private var mapView: MapView? = null + private var mapLibre: MapLibreMap? = null + private var markerManager: SymbolManager? = null + private var userLocationManager: SymbolManager? = null + private var markerStaticManager: SymbolManager? = null + private var shapeManager: FillManager? = null + private var lineManager: LineManager? = null + private var singleClickMarker: OnClickSymbols? = null + private var longClickMarker: OnClickSymbols? = null + private var mapClick: OnClickSymbols? = null + private var mapMove: OnMapMove? = null + private var userLocationChanged: OnClickSymbols? = null + + private var zoomStep = 1.0 + + private var mainLinearLayout: FrameLayout = FrameLayout(context).apply { + this.layoutParams = + FrameLayout.LayoutParams(FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)) + } + + init { + MapLibre.getInstance(context) // needs to be called before MapView gets created + mapView = MapView(context) + mainLinearLayout.addView(mapView) + providerLifecycle.getOSMLifecycle()?.addObserver(this) + } + + override fun onSaveInstanceState(bundle: Bundle) { + TODO("Not yet implemented") + } + + override fun onRestoreInstanceState(bundle: Bundle?) { + TODO("Not yet implemented") + } + + override fun getView(): View? = mainLinearLayout + override fun onCreate(owner: LifecycleOwner) { + super.onCreate(owner) + + methodChannel = MethodChannel(binaryMessenger, "plugins.dali.hamza/osmview_$idView") + methodChannel.setMethodCallHandler(this) + init(OSMInitConfiguration(GeoPoint(0.0, 0.0))) + mapView?.onCreate(null) + } + + override fun onResume(owner: LifecycleOwner) { + super.onResume(owner) + mapView?.onResume() + } + + override fun onStop(owner: LifecycleOwner) { + super.onStop(owner) + mapView?.onStop() + } + + override fun onDestroy(owner: LifecycleOwner) { + super.onDestroy(owner) + mapView?.onDestroy() + mapView = null + mapLibre = null + } + + override fun dispose() { + providerLifecycle.getOSMLifecycle()?.removeObserver(this) + mapView?.onDestroy() + mapView = null + mapLibre = null + } + + override fun onMethodCall( + call: MethodCall, result: MethodChannel.Result, + ) { + when (MapMethodChannelCall.Companion.fromMethodCall(call)) { + MapMethodChannelCall.Init -> { + mapView?.getMapAsync { + val args = call.arguments!! as HashMap<*, *> + val geoPoint = GeoPoint(args["lat"]!! as Double, args["lon"]!! as Double) + moveTo(geoPoint, true) + methodChannel.invokeMethod("map#init", true) + result.success(200) + } + } + + MapMethodChannelCall.LimitArea -> { + val list: List = (call.arguments as List<*>).filterIsInstance() + val box = BoundingBox( + list[0], list[1], list[2], list[3] + ) + setBoundingBox(box) + result.success(200) + } + + MapMethodChannelCall.LocationMarkers -> { + // update user marker and direction marker + result.success(200) + } + + MapMethodChannelCall.AddMarker -> { + val args = call.arguments as HashMap<*, *> + val point = (args["point"] as HashMap).toGeoPoint() + + addMarker(point, MarkerConfiguration.fromArgs(args, customMarkerIcon)) + result.success(200) + } + + MapMethodChannelCall.Bounds -> { + result.success(bounds().toHashMap()) + } + + MapMethodChannelCall.Center -> { + result.success(center().toHashMap()) + } + + MapMethodChannelCall.ChangeMarker -> { + result.success(200) + } + + MapMethodChannelCall.ChangeTile -> { + result.success(200) + } + + MapMethodChannelCall.ClearRoads -> { + result.success(200) + } + + MapMethodChannelCall.ClearShapes -> { + result.success(200) + } + + MapMethodChannelCall.CurrentLocation -> { + result.success(200) + } + + MapMethodChannelCall.DeactivateTrackMe -> { + result.success(200) + } + + MapMethodChannelCall.DefaultMarkerIcon -> { + customMarkerIcon = (call.arguments as ByteArray).toBitmap() + result.success(200) + } + + MapMethodChannelCall.DeleteMakers -> { + val args = call.arguments as List<*> + val geoPoints = args.filterIsInstance>().map { mapGeoP -> + (mapGeoP as HashMap).toGeoPoint() + } + geoPoints.forEach { geoPoint -> + removeMarker(geoPoint) + } + result.success(200) + } + + MapMethodChannelCall.DeleteRoad -> { + result.success(200) + } + + MapMethodChannelCall.DrawCircle -> { + result.success(200) + } + + MapMethodChannelCall.DrawMultiRoad -> { + result.success(200) + } + + MapMethodChannelCall.DrawRect -> { + result.success(200) + } + + MapMethodChannelCall.DrawRoadManually -> { + result.success(200) + } + + MapMethodChannelCall.GetMarkers -> { + val jsonGeoPs = markers().map { + it.toHashMap() + } + result.success(jsonGeoPs) + } + + MapMethodChannelCall.GetZoom -> { + result.success(zoom()) + result.success(200) + } + + MapMethodChannelCall.InfoWindowVisibility -> result.success(200) + MapMethodChannelCall.MapOrientation -> { + val rotate = call.arguments as Double? ?: 0.0 + + val cameraPosition = + CameraPosition.Builder().target(mapLibre?.cameraPosition?.target).zoom(zoom()) + .bearing(rotate) + .build() + mapLibre?.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return cameraPosition + } + }) + result.success(200) + } + + MapMethodChannelCall.MoveTo -> { + val args = call.arguments!! as HashMap<*, *> + val geoPoint = GeoPoint(args["lat"]!! as Double, args["lon"]!! as Double) + val animate = args["animate"] as Boolean? == true + moveTo(geoPoint, animate) + result.success(200) + } + + MapMethodChannelCall.RemoveCircle -> { + result.success(200) + } + + MapMethodChannelCall.RemoveLimitArea -> { + result.success(200) + } + + MapMethodChannelCall.RemoveMarkerPosition -> { + result.success(200) + } + + MapMethodChannelCall.RemoveRect -> { + result.success(200) + } + + MapMethodChannelCall.SetStepZoom -> { + zoomStep = call.arguments as Double + result.success(200) + } + + MapMethodChannelCall.SetZoom -> { + val args = call.arguments as HashMap<*, *> + when (args.containsKey("stepZoom")) { + true -> { + var zoomInput = args["stepZoom"] as Double + if (zoomInput == 0.0) { + zoomInput = zoomStep + } else if (zoomInput == -1.0) { + zoomInput = -zoomStep + } + zoomIn(zoomInput.toInt(), true) + } + + false -> { + if (args.containsKey("zoomLevel")) { + val level = args["zoomLevel"] as Double + setZoomLevel(level, true) + } + + } + } + result.success(200) + } + + MapMethodChannelCall.ShowZoomController -> { + result.success(200) + } + + MapMethodChannelCall.StartLocationUpdating -> { + result.success(200) + } + + MapMethodChannelCall.StaticPosition -> { + val args = call.arguments as HashMap<*, *> + val id = args["id"] as String? + val points = args["point"] as List>? + val geoPoints: MutableList = mutableListOf() + for (geoPointMap in points!!) { + val geoPoint = + GeoPoint(geoPointMap["lat"]!! as Double, geoPointMap["lon"]!! as Double) + val angle = when (geoPointMap.containsKey("angle")) { + true -> geoPointMap["angle"] as Double? ?: 0.0 + else -> 0.0 + } + geoPoints.add(FlutterGeoPoint(geoPoint, angle)) + } + + setStaticMarkers(id ?: "", geoPoints) + result.success(200) + } + + MapMethodChannelCall.StaticPositionIconMarker -> { + val hashMap: HashMap<*, *> = call.arguments as HashMap<*, *> + + try { + val key = (hashMap["id"] as String) + val bytes = (hashMap["bitmap"] as ByteArray) + setStaticMarkerIcons(key, bytes) + + } catch (e: java.lang.Exception) { + Log.e("id", hashMap["id"].toString()) + Log.e("err static point marker", e.stackTraceToString()) + result.error("400", "error to getBitmap static Position", "") + staticMarkerIcon = HashMap() + } + result.success(200) + } + + MapMethodChannelCall.StopLocationUpdating -> { + result.success(200) + } + + MapMethodChannelCall.ToggleLayers -> { + result.success(200) + } + + MapMethodChannelCall.TrackMe -> { + result.success(200) + } + + MapMethodChannelCall.UpdateMarker -> { + result.success(200) + } + + MapMethodChannelCall.UserPosition -> { + result.success(200) + } + + MapMethodChannelCall.ZoomConfiguration -> { + + result.success(200) + } + + MapMethodChannelCall.ZoomToRegion -> { + result.success(200) + } + + else -> result.notImplemented() + } + } + + override fun onActivityResult( + requestCode: Int, resultCode: Int, data: Intent?, + ): Boolean { + TODO("Not yet implemented") + } + + + override var customMarkerIcon: Bitmap? = null + + + override var customPersonMarkerIcon: Bitmap? = null + + + override var customArrowMarkerIcon: Bitmap? = null + override var staticMarkerIcon: HashMap = HashMap() + override val staticPoints: HashMap> = + HashMap>() + + + override fun init(configuration: OSMInitConfiguration) { + + mapView?.getMapAsync { map -> + mapLibre = map + mapLibre!!.setMinZoomPreference(configuration.minZoom) + mapLibre!!.setMaxZoomPreference(configuration.maxZoom) + if (configuration.bounds != null) { + mapLibre!!.setLatLngBoundsForCameraTarget(configuration.bounds.toBoundsLibre()) + } + val styleURL = when (configuration.customTile) { + + is VectorOSMTile -> configuration.customTile.style + else -> "https://tiles.openfreemap.org/styles/liberty" + } + val style = Style.Builder().fromUri(styleURL) + map.setStyle(style) { styleLoaded -> + markerManager = SymbolManager(mapView!!, mapLibre!!, map.style!!) + + lineManager = LineManager( + mapView!!, + mapLibre!!, + map.style!!, + markerManager?.layerId, + null, + ) + markerStaticManager = SymbolManager( + mapView!!, + mapLibre!!, + mapLibre!!.style!!, + null, + lineManager?.layerId + ) + + userLocationManager = SymbolManager( + mapView!!, mapLibre!!, mapLibre!!.style!!, null, markerManager?.layerId + ) + shapeManager = FillManager( + mapView!!, mapLibre!!, mapLibre!!.style!!, + lineManager?.layerId, null, + ) + + } + + map.cameraPosition = CameraPosition.Builder().target(configuration.point.toLngLat()) + .zoom(configuration.initZoom).build() + map.addOnMapClickListener { lng -> + mapClick?.invoke(lng.toGeoPoint()) + methodChannel.invokeMethod("receiveSinglePress", lng.toGeoPoint().toHashMap()) + true + } + map.addOnMapLongClickListener { lng -> + mapClick?.invoke(lng.toGeoPoint()) + methodChannel.invokeMethod("receiveLongPress", lng.toGeoPoint().toHashMap()) + true + } + map.addOnCameraMoveListener { + if (map.cameraPosition.target != null) { + val bounds = map.projection.visibleRegion.latLngBounds.toBoundingBox() + mapMove?.invoke(bounds, map.cameraPosition.target!!.toGeoPoint()) + } + val hashMap = HashMap() + hashMap["bounding"] = bounds().toHashMap() + hashMap["center"] = center().toHashMap() + methodChannel.invokeMethod("receiveRegionIsChanging", hashMap) + + } + + } + + } + + override fun zoomConfig(zoomConfig: OSMZoomConfiguration) { + zoomStep = zoomConfig.zoomStep + mapLibre?.setMinZoomPreference(zoomConfig.minZoom) + mapLibre?.setMaxZoomPreference(zoomConfig.maxZoom) + } + + override fun setBoundingBox(bounds: BoundingBox) { + mapLibre?.setLatLngBoundsForCameraTarget(bounds.toBoundsLibre()) + } + + override fun moveTo(point: IGeoPoint, animate: Boolean) { + mapView?.getMapAsync { map -> + val cameraPosition = + CameraPosition.Builder().target(point.toLngLat()).zoom(map.cameraPosition.zoom) + .build() + when (animate) { + true -> map.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return cameraPosition + } + }) + + else -> map.cameraPosition = cameraPosition + } + } + } + + override fun moveToBounds(bounds: BoundingBox, animate: Boolean) { + mapView?.getMapAsync { map -> + when (animate) { + true -> map.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return map.getCameraForLatLngBounds(bounds.toBoundsLibre()) + } + }) + + else -> map.setLatLngBoundsForCameraTarget(bounds.toBoundsLibre()) + } + } + } + + override fun addMarker(point: IGeoPoint, markerConfiguration: MarkerConfiguration) { + mapLibre!!.style!!.addImage(point.toString(), markerConfiguration.markerIcon.toBitmap()) + val symbolOp = SymbolOptions().withLatLng(point.toLngLat()).withIconImage(point.toString()) + .withIconAnchor("center") + markerManager?.create(symbolOp) + markerManager?.addClickListener(object : OnSymbolClickListener { + override fun onAnnotationClick(t: Symbol?): Boolean { + if (t != null) { + singleClickMarker?.invoke(t.latLng.toGeoPoint()) + } + return true + } + }) + markerManager?.addLongClickListener(object : OnSymbolLongClickListener { + override fun onAnnotationLongClick(t: Symbol?): Boolean { + if (t != null) { + singleClickMarker?.invoke(t.latLng.toGeoPoint()) + } + return true + } + }) + } + + override fun removeMarker(point: IGeoPoint) { + val symbol = markerManager?.annotations?.where { s -> + s.latLng.toGeoPoint() == point + } + if (symbol != null) { + mapLibre!!.style!!.removeImage(symbol.latLng.toGeoPoint().toString()) + } + markerManager?.delete(symbol) + } + + override fun setStaticMarkerIcons(id: String, icon: ByteArray) { + val bitmapIcon = icon.toBitmap() + mapLibre?.style?.addImage(id, bitmapIcon) + staticMarkerIcon[id] = bitmapIcon + if (staticPoints.containsKey(id)) { + markerStaticManager?.updateSource() + } + } + + + override fun setStaticMarkers( + id: String, markers: List, + ) { + if (staticPoints.contains(id)) { + staticPoints[id]?.clear() + } + staticPoints[id] = markers.toMutableList() + val symbolOptions = staticPoints[id]?.toSymbols(id) ?: emptyList() + val others: List = markerStaticManager?.annotations?.toList() ?: emptyList() + markerStaticManager?.create(symbolOptions) + markerStaticManager?.delete(others) + + } + + override fun drawPolyline( + polyline: List, animate: Boolean, + ): String { + TODO("Not yet implemented") + } + + override fun removePolyline(id: String) { + TODO("Not yet implemented") + } + + override fun drawEncodedPolyline(polylineEncoded: String): String { + TODO("Not yet implemented") + } + + override fun removeEncodedPolyline(id: String) { + TODO("Not yet implemented") + } + + override fun addShape(shapeConfiguration: OSMShapeConfiguration): String { + TODO("Not yet implemented") + } + + override fun removeShape(id: String) { + TODO("Not yet implemented") + } + + + override fun zoomIn(step: Int, animate: Boolean) { + mapView?.getMapAsync { map -> + val cameraPosition = + CameraPosition.Builder().zoom(map.cameraPosition.zoom + step) + .target(mapLibre!!.cameraPosition.target).build() + when (animate) { + true -> map.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return cameraPosition + } + }) + + else -> map.cameraPosition = cameraPosition + } + } + } + + override fun zoomOut(step: Int, animate: Boolean) { + mapView?.getMapAsync { map -> + val cameraPosition = + CameraPosition.Builder().zoom(map.cameraPosition.zoom - step) + .target(mapLibre!!.cameraPosition.target).build() + when (animate) { + true -> map.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return cameraPosition + } + }) + + else -> map.cameraPosition = cameraPosition + } + } + } + + override fun setZoomLevel(zoomLevel: Double, animate: Boolean) { + val camera = + CameraPosition.Builder().zoom(zoomLevel).target(mapLibre!!.cameraPosition.target) + .build() + when (animate) { + true -> mapLibre?.animateCamera(object : CameraUpdate { + override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { + return camera + } + }) + + else -> mapLibre?.cameraPosition = camera + } + } + + override fun zoom(): Double { + return mapLibre!!.cameraPosition.zoom + } + + override fun center(): IGeoPoint { + return mapLibre!!.cameraPosition.target!!.toGeoPoint() + } + + + override fun markers(): List = + markerManager?.annotations?.toGeoPoints() ?: emptyList() + + + override fun bounds(): BoundingBox { + return mapLibre!!.projection.visibleRegion.latLngBounds.toBoundingBox() + } + + override fun startLocation() { + TODO("Not yet implemented") + } + + + override fun stopLocation() { + TODO("Not yet implemented") + } + + override fun onMapClick(onClick: (IGeoPoint) -> Unit) { + mapClick = onClick + } + + override fun onMapMove(onMove: (BoundingBox, IGeoPoint) -> Unit) { + mapMove = onMove + } + + override fun onUserLocationChanged(onUserLocationChanged: (IGeoPoint) -> Unit) { + userLocationChanged = onUserLocationChanged + } + + override fun onMarkerSingleClick(onClick: (IGeoPoint) -> Unit) { + singleClickMarker = onClick + } + + override fun onMarkerLongClick(onClick: (IGeoPoint) -> Unit) { + longClickMarker = onClick + } + + override fun onPolylineClick(onClick: (IGeoPoint, String) -> Unit) { + TODO("Not yet implemented") + } + + override fun setActivity(activity: Activity) { + this.activity = activity + } +} \ No newline at end of file diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/FlutterOsmView.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterOsmView.kt similarity index 98% rename from android/src/main/kotlin/hamza/dali/flutter_osm_plugin/FlutterOsmView.kt rename to android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterOsmView.kt index e80e48fd..2ecf4452 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/FlutterOsmView.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/FlutterOsmView.kt @@ -1,4 +1,4 @@ -package hamza.dali.flutter_osm_plugin +package hamza.dali.flutter_osm_plugin.map import android.annotation.SuppressLint import android.app.Activity @@ -7,13 +7,11 @@ import android.content.Intent import android.content.SharedPreferences import android.graphics.Bitmap import android.graphics.Color -import android.graphics.Paint import android.location.LocationManager import android.location.LocationManager.GPS_PROVIDER import android.location.LocationManager.NETWORK_PROVIDER import android.os.Bundle import android.util.Log -import android.view.MotionEvent import android.view.View import android.view.ViewGroup.LayoutParams.MATCH_PARENT import android.widget.FrameLayout @@ -22,11 +20,13 @@ import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.coroutineScope import androidx.preference.PreferenceManager +import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin.Companion.CREATED import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin.Companion.DESTROYED import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin.Companion.PAUSED import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin.Companion.STARTED import hamza.dali.flutter_osm_plugin.FlutterOsmPlugin.Companion.STOPPED +import hamza.dali.flutter_osm_plugin.ProviderLifecycle import hamza.dali.flutter_osm_plugin.models.Anchor import hamza.dali.flutter_osm_plugin.models.CustomTile import hamza.dali.flutter_osm_plugin.models.FlutterGeoPoint @@ -36,6 +36,7 @@ import hamza.dali.flutter_osm_plugin.models.OSMShape import hamza.dali.flutter_osm_plugin.models.RoadConfig import hamza.dali.flutter_osm_plugin.models.RoadGeoPointInstruction import hamza.dali.flutter_osm_plugin.models.Shape +import hamza.dali.flutter_osm_plugin.models.VoidCallback import hamza.dali.flutter_osm_plugin.models.toRoadConfig import hamza.dali.flutter_osm_plugin.models.toRoadInstruction import hamza.dali.flutter_osm_plugin.models.toRoadOption @@ -58,8 +59,6 @@ import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.PluginRegistry -import io.flutter.plugin.platform.PlatformView import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers.Default import kotlinx.coroutines.Dispatchers.IO @@ -88,10 +87,11 @@ import org.osmdroid.views.overlay.Marker import org.osmdroid.views.overlay.Polygon import org.osmdroid.views.overlay.Polyline import org.osmdroid.views.overlay.gestures.RotationGestureOverlay +import kotlin.collections.get import kotlin.collections.set -typealias VoidCallback = () -> Unit + fun FlutterOsmView.configZoomMap(call: MethodCall, result: MethodChannel.Result) { @@ -124,12 +124,11 @@ class FlutterOsmView( private val customTile: CustomTile?, private val isEnabledRotationGesture: Boolean = false, private val isStaticMap: Boolean = false -) : OnSaveInstanceStateListener, PlatformView, MethodCallHandler, - PluginRegistry.ActivityResultListener, DefaultLifecycleObserver { +) : OnSaveInstanceStateListener, MethodCallHandler, + DefaultLifecycleObserver,OSM { internal var map: MapView? = null - private var keyMapSnapshot: String = keyArgMapSnapShot private lateinit var locationNewOverlay: CustomLocationManager private var customMarkerIcon: Bitmap? = null private var customPersonMarkerIcon: Bitmap? = null @@ -195,7 +194,7 @@ class FlutterOsmView( } - fun setActivity(activity: Activity) { + override fun setActivity(activity: Activity) { this.activity = activity } @@ -330,6 +329,7 @@ class FlutterOsmView( "initMap" -> { initPosition(call, result) } + "change#tile" -> { val args = call.arguments as HashMap? when (!args.isNullOrEmpty()) { @@ -437,7 +437,7 @@ class FlutterOsmView( } "map#center" -> { - result.success((map?.mapCenter as GeoPoint).toHashMap()) + result.success(map?.mapCenter?.toHashMap()) } "map#bounds" -> { @@ -682,8 +682,9 @@ class FlutterOsmView( result.success(null) } + @Suppress("UNCHECKED_CAST") private fun initPosition(methodCall: MethodCall, result: MethodChannel.Result) { - @Suppress("UNCHECKED_CAST") val args = methodCall.arguments!! as HashMap + val args = methodCall.arguments!! as HashMap val geoPoint = GeoPoint(args["lat"]!!, args["lon"]!!) val zoom = initZoom map!!.controller.setZoom(zoom) @@ -1565,7 +1566,7 @@ class FlutterOsmView( override fun onCreate(owner: LifecycleOwner) { super.onCreate(owner) - FlutterOsmPlugin.state.set(CREATED) + FlutterOsmPlugin.Companion.state.set(CREATED) methodChannel = MethodChannel(binaryMessenger, "plugins.dali.hamza/osmview_${id}") methodChannel.setMethodCallHandler(this) //eventChannel = EventChannel(binaryMessenger, "plugins.dali.hamza/osmview_stream_${id}") @@ -1587,10 +1588,10 @@ class FlutterOsmView( override fun onStart(owner: LifecycleOwner) { super.onStart(owner) - FlutterOsmPlugin.state.set(STARTED) + FlutterOsmPlugin.Companion.state.set(STARTED) Log.e("osm", "osm flutter plugin start") - activity = FlutterOsmPlugin.pluginBinding!!.activity - FlutterOsmPlugin.pluginBinding!!.addActivityResultListener(this) + activity = FlutterOsmPlugin.Companion.pluginBinding!!.activity + FlutterOsmPlugin.Companion.pluginBinding!!.addActivityResultListener(this) // context.applicationContext.registerReceiver( // checkGPSServiceBroadcast, // IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION) @@ -1601,7 +1602,7 @@ class FlutterOsmView( override fun onResume(owner: LifecycleOwner) { super.onResume(owner) - FlutterOsmPlugin.state.set(FlutterOsmPlugin.RESUMED) + FlutterOsmPlugin.Companion.state.set(FlutterOsmPlugin.Companion.RESUMED) Log.e("osm", "osm flutter plugin resume") if (map == null) { initMap() @@ -1612,7 +1613,7 @@ class FlutterOsmView( override fun onPause(owner: LifecycleOwner) { super.onPause(owner) - FlutterOsmPlugin.state.set(PAUSED) + FlutterOsmPlugin.Companion.state.set(PAUSED) map?.let { locationNewOverlay.disableFollowLocation() locationNewOverlay.onPause() @@ -1625,7 +1626,7 @@ class FlutterOsmView( override fun onStop(owner: LifecycleOwner) { super.onStop(owner) - FlutterOsmPlugin.state.set(STOPPED) + FlutterOsmPlugin.Companion.state.set(STOPPED) Log.e("osm", "osm flutter plugin stopped") //context.applicationContext.unregisterReceiver(checkGPSServiceBroadcast) job?.let { @@ -1640,7 +1641,7 @@ class FlutterOsmView( override fun onDestroy(owner: LifecycleOwner) { super.onDestroy(owner) locationNewOverlay.onDestroy() - FlutterOsmPlugin.pluginBinding!!.removeActivityResultListener(this) + FlutterOsmPlugin.Companion.pluginBinding!!.removeActivityResultListener(this) mainLinearLayout.removeAllViews() //map!!.onDetach() methodChannel.setMethodCallHandler(null) @@ -1649,7 +1650,7 @@ class FlutterOsmView( //configuration = null //eventChannel.setStreamHandler(null) map = null - FlutterOsmPlugin.state.set(DESTROYED) + FlutterOsmPlugin.Companion.state.set(DESTROYED) } diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSM.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSM.kt new file mode 100644 index 00000000..d2830a4f --- /dev/null +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSM.kt @@ -0,0 +1,9 @@ +package hamza.dali.flutter_osm_plugin.map + +import android.app.Activity +import io.flutter.plugin.common.PluginRegistry +import io.flutter.plugin.platform.PlatformView + + sealed interface OSM : PlatformView, PluginRegistry.ActivityResultListener { + fun setActivity(activity: Activity) +} \ No newline at end of file diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/OSMBase.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSMBase.kt similarity index 68% rename from android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/OSMBase.kt rename to android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSMBase.kt index 511a3007..0752438d 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/OSMBase.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/map/OSMBase.kt @@ -1,11 +1,17 @@ -package hamza.dali.flutter_osm_plugin.models +package hamza.dali.flutter_osm_plugin.map -import android.graphics.drawable.Drawable +import android.graphics.Bitmap +import hamza.dali.flutter_osm_plugin.models.Anchor +import hamza.dali.flutter_osm_plugin.models.FlutterGeoPoint +import hamza.dali.flutter_osm_plugin.models.OSMTile +import hamza.dali.flutter_osm_plugin.models.Shape import hamza.dali.flutter_osm_plugin.models.VectorOSMTile +import hamza.dali.flutter_osm_plugin.utilities.toByteArray import hamza.dali.flutter_osm_plugin.utilities.toGeoPoint import org.osmdroid.api.IGeoPoint import org.osmdroid.util.BoundingBox import org.osmdroid.util.GeoPoint +import kotlin.collections.get data class OSMInitConfiguration( @@ -77,6 +83,13 @@ data class OSMInitConfiguration( } } +data class OSMZoomConfiguration( + val minZoom: Double = 3.0, + val maxZoom: Double = 19.0, + val zoomStep: Double = 1.0, + val initZoom: Double = 10.0, +) + data class OSMShapeConfiguration( val point: List, val radius: Double, @@ -91,14 +104,47 @@ data class MarkerConfiguration( val markerRotate: Double, val markerAnchor: Anchor, val size: Double, -) +) { + companion object { + fun fromArgs(args: HashMap<*, *>, defaultIcon: Bitmap?): MarkerConfiguration { + + var iconBytes = defaultIcon.toByteArray() + if (args.containsKey("icon")) { + iconBytes = args["icon"] as ByteArray + + } + val angle = when ((args["point"] as HashMap<*, *>).containsKey("angle")) { + true -> (args["point"] as HashMap<*, *>)["angle"] as Double + else -> 0.0 + } + val anchor = when (args.containsKey("iconAnchor")) { + true -> Anchor(args["iconAnchor"] as HashMap) + else -> null + } + return MarkerConfiguration( + markerIcon = iconBytes!!, angle, Anchor(0.5f, 0.5f), 48.0 + ) + } + } +} + + -interface OSMBase { +interface OSMBase : OSM { + var customMarkerIcon: Bitmap? + var customPersonMarkerIcon: Bitmap? + var customArrowMarkerIcon: Bitmap? + var staticMarkerIcon: HashMap + val staticPoints: HashMap> fun init(configuration: OSMInitConfiguration) + fun zoomConfig(zoomConfig: OSMZoomConfiguration) + fun setBoundingBox(bounds: BoundingBox) fun moveTo(point: IGeoPoint, animate: Boolean) fun moveToBounds(bounds: BoundingBox, animate: Boolean) fun addMarker(point: IGeoPoint, markerConfiguration: MarkerConfiguration) fun removeMarker(point: IGeoPoint) + fun setStaticMarkerIcons(id: String, icon: ByteArray) + fun setStaticMarkers(id: String, markers: List) fun drawPolyline(polyline: List, animate: Boolean): String fun removePolyline(id: String) fun drawEncodedPolyline(polylineEncoded: String): String @@ -107,8 +153,10 @@ interface OSMBase { fun removeShape(id: String) fun zoomIn(step: Int, animate: Boolean) fun zoomOut(step: Int, animate: Boolean) + fun setZoomLevel(zoomLevel: Double, animate: Boolean) fun zoom(): Double fun center(): IGeoPoint + fun markers(): List fun bounds(): BoundingBox fun startLocation() fun stopLocation() diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/FlutterMapLibreView.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/FlutterMapLibreView.kt deleted file mode 100644 index ad284d15..00000000 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/FlutterMapLibreView.kt +++ /dev/null @@ -1,389 +0,0 @@ -package hamza.dali.flutter_osm_plugin.maplibre - -import android.content.Context -import android.content.Intent -import android.os.Bundle -import android.view.View -import android.view.ViewGroup.LayoutParams.MATCH_PARENT -import android.widget.FrameLayout -import androidx.lifecycle.DefaultLifecycleObserver -import androidx.lifecycle.LifecycleOwner -import hamza.dali.flutter_osm_plugin.MapMethodChannelCall -import hamza.dali.flutter_osm_plugin.ProviderLifecycle -import hamza.dali.flutter_osm_plugin.models.MarkerConfiguration -import hamza.dali.flutter_osm_plugin.models.OSMBase -import hamza.dali.flutter_osm_plugin.models.OSMInitConfiguration -import hamza.dali.flutter_osm_plugin.models.OSMShapeConfiguration -import hamza.dali.flutter_osm_plugin.models.OSMTile -import hamza.dali.flutter_osm_plugin.models.VectorOSMTile -import hamza.dali.flutter_osm_plugin.utilities.toBitmap -import org.maplibre.android.plugins.annotation.* -import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding.OnSaveInstanceStateListener -import io.flutter.plugin.common.BinaryMessenger -import io.flutter.plugin.common.MethodCall -import io.flutter.plugin.common.MethodChannel -import io.flutter.plugin.common.MethodChannel.MethodCallHandler -import io.flutter.plugin.common.PluginRegistry -import io.flutter.plugin.platform.PlatformView -import org.maplibre.android.camera.CameraPosition -import org.maplibre.android.camera.CameraUpdate -import org.maplibre.android.maps.MapLibreMap -import org.maplibre.android.maps.MapView -import org.osmdroid.api.IGeoPoint -import org.osmdroid.util.BoundingBox -import java.util.HashMap - -typealias OnClickSymbols = (IGeoPoint) -> Unit -typealias OnMapMove = (BoundingBox, IGeoPoint) -> Unit - -class FlutterMapLibreView( - private val context: Context, - private val binaryMessenger: BinaryMessenger, - private val id: Int,//viewId - private val providerLifecycle: ProviderLifecycle, - private val keyArgMapSnapShot: String, - private val customTile: OSMTile?, - private val isEnabledRotationGesture: Boolean = false, - private val isStaticMap: Boolean = false -) : OnSaveInstanceStateListener, PlatformView, MethodCallHandler, - PluginRegistry.ActivityResultListener, DefaultLifecycleObserver, OSMBase { - private lateinit var methodChannel: MethodChannel - private var mapView: MapView? = null - private var mapLibre: MapLibreMap? = null - private var markerManager: SymbolManager? = null - private var userLocationManager: SymbolManager? = null - private var markerStaticManager: SymbolManager? = null - private var shapeManager: FillManager? = null - private var lineManager: LineManager? = null - private var singleClickMarker: OnClickSymbols? = null - private var longClickMarker: OnClickSymbols? = null - private var mapClick: OnClickSymbols? = null - private var mapMove: OnMapMove? = null - private var userLocationChanged: OnClickSymbols? = null - private var mainLinearLayout: FrameLayout = FrameLayout(context).apply { - this.layoutParams = - FrameLayout.LayoutParams(FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)) - } - - init { - mapView = MapView(context) - providerLifecycle.getOSMLifecycle()?.addObserver(this) - } - - override fun onSaveInstanceState(bundle: Bundle) { - TODO("Not yet implemented") - } - - override fun onRestoreInstanceState(bundle: Bundle?) { - TODO("Not yet implemented") - } - - override fun getView(): View? = mainLinearLayout - - override fun dispose() { - providerLifecycle.getOSMLifecycle()?.removeObserver(this) - mapView?.onDestroy() - mapView = null - mapLibre = null - } - - override fun onMethodCall( - call: MethodCall, - result: MethodChannel.Result - ) { - when(MapMethodChannelCall.fromMethodCall(call)){ - MapMethodChannelCall.Init -> { - init(OSMInitConfiguration.fromMap(call.arguments as HashMap)) - result.success(200) - } - MapMethodChannelCall.LimitArea -> TODO() - MapMethodChannelCall.LocationMarkers -> TODO() - MapMethodChannelCall.AddMarker -> TODO() - MapMethodChannelCall.Bounds -> TODO() - MapMethodChannelCall.Center -> TODO() - MapMethodChannelCall.ChangeMarker -> TODO() - MapMethodChannelCall.ChangeTile -> TODO() - MapMethodChannelCall.ClearRoads -> TODO() - MapMethodChannelCall.ClearShapes -> TODO() - MapMethodChannelCall.CurrentLocation -> TODO() - MapMethodChannelCall.DeactivateTrackMe -> TODO() - MapMethodChannelCall.DefaultMarkerIcon -> TODO() - MapMethodChannelCall.DeleteMakers -> TODO() - MapMethodChannelCall.DeleteRoad -> TODO() - MapMethodChannelCall.DrawCircle -> TODO() - MapMethodChannelCall.DrawMultiRoad -> TODO() - MapMethodChannelCall.DrawRect -> TODO() - MapMethodChannelCall.DrawRoadManually -> TODO() - MapMethodChannelCall.GetMarkers -> TODO() - MapMethodChannelCall.GetZoom -> TODO() - MapMethodChannelCall.InfoWindowVisibility -> result.success(200) - MapMethodChannelCall.MapOrientation -> TODO() - MapMethodChannelCall.MoveTo -> TODO() - MapMethodChannelCall.RemoveCircle -> TODO() - MapMethodChannelCall.RemoveLimitArea -> TODO() - MapMethodChannelCall.RemoveMarkerPosition -> TODO() - MapMethodChannelCall.RemoveRect -> TODO() - MapMethodChannelCall.SetStepZoom -> TODO() - MapMethodChannelCall.SetZoom -> TODO() - MapMethodChannelCall.ShowZoomController -> TODO() - MapMethodChannelCall.StartLocationUpdating -> TODO() - MapMethodChannelCall.StaticPosition -> TODO() - MapMethodChannelCall.StaticPositionIconMarker -> TODO() - MapMethodChannelCall.StopLocationUpdating -> TODO() - MapMethodChannelCall.ToggleLayers -> TODO() - MapMethodChannelCall.TrackMe -> TODO() - MapMethodChannelCall.UpdateMarker -> TODO() - MapMethodChannelCall.UserPosition -> TODO() - MapMethodChannelCall.ZoomConfiguration -> TODO() - MapMethodChannelCall.ZoomToRegion -> TODO() - else -> result.notImplemented() - } - } - - override fun onActivityResult( - requestCode: Int, - resultCode: Int, - data: Intent? - ): Boolean { - TODO("Not yet implemented") - } - - override fun onCreate(owner: LifecycleOwner) { - super.onCreate(owner) - methodChannel = MethodChannel(binaryMessenger, "plugins.dali.hamza/osmview_${id}") - methodChannel.setMethodCallHandler(this) - mapView?.onCreate(null) - } - - override fun onResume(owner: LifecycleOwner) { - super.onResume(owner) - mapView?.onResume() - } - - override fun onStop(owner: LifecycleOwner) { - super.onStop(owner) - mapView?.onStop() - } - - override fun onDestroy(owner: LifecycleOwner) { - super.onDestroy(owner) - mapView?.onDestroy() - mapView = null - mapLibre = null - } - - override fun init(configuration: OSMInitConfiguration) { - - mapView?.getMapAsync { map -> - mapLibre = map - mapLibre!!.setMinZoomPreference(configuration.minZoom) - mapLibre!!.setMaxZoomPreference(configuration.maxZoom) - if (configuration.bounds != null) { - mapLibre!!.setLatLngBoundsForCameraTarget(configuration.bounds.toBoundsLibre()) - - } - when (configuration.customTile) { - null -> map.setStyle("https://tiles.openfreemap.org/styles/liberty") - is VectorOSMTile -> map.setStyle(configuration.customTile.style) - else -> map.setStyle("https://tiles.openfreemap.org/styles/liberty") - } - markerManager = SymbolManager(mapView!!, mapLibre!!, mapLibre!!.style!!) - userLocationManager = SymbolManager(mapView!!, mapLibre!!, mapLibre!!.style!!) - markerStaticManager = SymbolManager(mapView!!, mapLibre!!, mapLibre!!.style!!) - shapeManager = FillManager(mapView!!, mapLibre!!, mapLibre!!.style!!) - lineManager = LineManager(mapView!!, mapLibre!!, mapLibre!!.style!!) - map.cameraPosition = CameraPosition.Builder().target(configuration.point.toLngLat()) - .zoom(configuration.initZoom).build() - map.addOnMapClickListener { lng -> - mapClick?.invoke(lng.toGeoPoint()) - true - } - map.addOnCameraMoveListener { - if (map.cameraPosition.target != null) { - val bounds = map.projection.visibleRegion.latLngBounds.toBoundingBox() - mapMove?.invoke(bounds, map.cameraPosition.target!!.toGeoPoint()) - } - - } - - } - - } - - override fun moveTo(point: IGeoPoint, animate: Boolean) { - mapView?.getMapAsync { map -> - val cameraPosition = CameraPosition.Builder().target(point.toLngLat()) - .zoom(map.cameraPosition.zoom).build() - when (animate) { - true -> map.moveCamera(object : CameraUpdate { - override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { - return cameraPosition - } - - }) - - else -> map.cameraPosition = cameraPosition - } - } - } - - override fun moveToBounds(bounds: BoundingBox, animate: Boolean) { - mapView?.getMapAsync { map -> - when (animate) { - true -> map.moveCamera(object : CameraUpdate { - override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { - return map.getCameraForLatLngBounds(bounds.toBoundsLibre()) - } - }) - - else -> map.setLatLngBoundsForCameraTarget(bounds.toBoundsLibre()) - } - } - } - - override fun addMarker(point: IGeoPoint, markerConfiguration: MarkerConfiguration) { - - - mapLibre!!.style!!.addImage(point.toString(), markerConfiguration.markerIcon.toBitmap()) - - val symbolOp = SymbolOptions() - .withLatLng(point.toLngLat()) - .withIconImage(point.toString()) - .withIconAnchor("center") - markerManager?.create(symbolOp) - markerManager?.addClickListener(object : OnSymbolClickListener { - override fun onAnnotationClick(t: Symbol?): Boolean { - if (t != null) { - singleClickMarker?.invoke(t.latLng.toGeoPoint()) - } - return true - } - }) - markerManager?.addLongClickListener(object : OnSymbolLongClickListener { - override fun onAnnotationLongClick(t: Symbol?): Boolean { - if (t != null) { - singleClickMarker?.invoke(t.latLng.toGeoPoint()) - } - return true - } - }) - - } - - override fun removeMarker(point: IGeoPoint) { - val symbol = markerManager?.annotations?.where { s -> - s.latLng.toGeoPoint() == point - } - if (symbol != null) { - mapLibre!!.style!!.removeImage(symbol.latLng.toGeoPoint().toString()) - } - markerManager?.delete(symbol) - } - - override fun drawPolyline( - polyline: List, - animate: Boolean - ): String { - TODO("Not yet implemented") - } - - override fun removePolyline(id: String) { - TODO("Not yet implemented") - } - - override fun drawEncodedPolyline(polylineEncoded: String): String { - TODO("Not yet implemented") - } - - override fun removeEncodedPolyline(id: String) { - TODO("Not yet implemented") - } - - override fun addShape(shapeConfiguration: OSMShapeConfiguration): String { - TODO("Not yet implemented") - } - - override fun removeShape(id: String) { - TODO("Not yet implemented") - } - - - override fun zoomIn(step: Int, animate: Boolean) { - mapView?.getMapAsync { map -> - val cameraPosition = - CameraPosition.Builder().zoom(map.cameraPosition.zoom + step).build() - when (animate) { - true -> map.moveCamera(object : CameraUpdate { - override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { - return cameraPosition - } - }) - - else -> map.cameraPosition = - cameraPosition - } - } - } - - override fun zoomOut(step: Int, animate: Boolean) { - mapView?.getMapAsync { map -> - val cameraPosition = - CameraPosition.Builder().zoom(map.cameraPosition.zoom - step).build() - when (animate) { - true -> map.moveCamera(object : CameraUpdate { - override fun getCameraPosition(maplibreMap: MapLibreMap): CameraPosition? { - return cameraPosition - } - }) - - else -> map.cameraPosition = - cameraPosition - } - } - } - - override fun zoom(): Double { - return mapLibre!!.cameraPosition.zoom - } - - override fun center(): IGeoPoint { - return mapLibre!!.cameraPosition.target!!.toGeoPoint() - } - - override fun bounds(): BoundingBox { - return mapLibre!!.projection.visibleRegion.latLngBounds.toBoundingBox() - } - - override fun startLocation() { - TODO("Not yet implemented") - } - - - override fun stopLocation() { - TODO("Not yet implemented") - } - - override fun onMapClick(onClick: (IGeoPoint) -> Unit) { - mapClick = onClick - } - - override fun onMapMove(onMove: (BoundingBox, IGeoPoint) -> Unit) { - mapMove = onMove - } - - override fun onUserLocationChanged(onUserLocationChanged: (IGeoPoint) -> Unit) { - userLocationChanged = onUserLocationChanged - } - - override fun onMarkerSingleClick(onClick: (IGeoPoint) -> Unit) { - singleClickMarker = onClick - } - - override fun onMarkerLongClick(onClick: (IGeoPoint) -> Unit) { - longClickMarker = onClick - } - - override fun onPolylineClick(onClick: (IGeoPoint, String) -> Unit) { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/Commons.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/Commons.kt new file mode 100644 index 00000000..99205024 --- /dev/null +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/Commons.kt @@ -0,0 +1,9 @@ +package hamza.dali.flutter_osm_plugin.models + +import org.osmdroid.api.IGeoPoint +import org.osmdroid.util.BoundingBox + +typealias VoidCallback = () -> Unit + +typealias OnClickSymbols = (IGeoPoint) -> Unit +typealias OnMapMove = (BoundingBox, IGeoPoint) -> Unit \ No newline at end of file diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/ExtensionMapLibre.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/ExtensionMapLibre.kt similarity index 54% rename from android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/ExtensionMapLibre.kt rename to android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/ExtensionMapLibre.kt index 86d9482c..0e9a6501 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/maplibre/ExtensionMapLibre.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/ExtensionMapLibre.kt @@ -1,10 +1,11 @@ -package hamza.dali.flutter_osm_plugin.maplibre +package hamza.dali.flutter_osm_plugin.models import androidx.collection.LongSparseArray import androidx.collection.forEach import org.maplibre.android.geometry.LatLng import org.maplibre.android.geometry.LatLngBounds import org.maplibre.android.plugins.annotation.Symbol +import org.maplibre.android.plugins.annotation.SymbolOptions import org.osmdroid.api.IGeoPoint import org.osmdroid.util.BoundingBox import org.osmdroid.util.GeoPoint @@ -17,14 +18,25 @@ fun BoundingBox.toBoundsLibre(): LatLngBounds = LatLngBounds.fromLatLngs( LatLng(latSouth, lonWest) ).toList() ) + fun LatLngBounds.toBoundingBox(): BoundingBox = BoundingBox.fromGeoPoints( this.toLatLngs().toGeoPoints() ) + fun Array.toGeoPoints(): List { - return map { + return map { it.toGeoPoint() }.toList() } + +fun List.toSymbols(iconId: String): List { + return map { + SymbolOptions().withLatLng(it.geoPoint.toLngLat()) + .withIconRotate(it.angle.toFloat()) + .withIconImage(iconId) + }.toList() +} + fun LongSparseArray.where(f: (Symbol) -> Boolean): Symbol? { this.forEach { k, symbol -> if (f(symbol)) { @@ -33,4 +45,25 @@ fun LongSparseArray.where(f: (Symbol) -> Boolean): Symbol? { } return null +} + +fun LongSparseArray.toList(): List { + val list = mutableListOf() + this.forEach { k, symbol -> + list.add(symbol) + } + + return list.toList() +} +fun LongSparseArray.toGeoPoints(): List { + val list = mutableListOf() + this.forEach { k, symbol -> + list.add(symbol.latLng.toGeoPoint()) + } + + return list.toList() +} + +fun List.toGeoPoints(): List = map { + it.latLng.toGeoPoint() } \ No newline at end of file diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/MapMedthodCall.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/MapMedthodCall.kt similarity index 99% rename from android/src/main/kotlin/hamza/dali/flutter_osm_plugin/MapMedthodCall.kt rename to android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/MapMedthodCall.kt index c48c6101..ec316dfd 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/MapMedthodCall.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/models/MapMedthodCall.kt @@ -1,4 +1,4 @@ -package hamza.dali.flutter_osm_plugin +package hamza.dali.flutter_osm_plugin.models import io.flutter.plugin.common.MethodCall diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/overlays/CustomLocation.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/overlays/CustomLocation.kt index 5de62d47..8e44858f 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/overlays/CustomLocation.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/overlays/CustomLocation.kt @@ -13,7 +13,7 @@ import android.view.MotionEvent import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toBitmap import hamza.dali.flutter_osm_plugin.R -import hamza.dali.flutter_osm_plugin.VoidCallback +import hamza.dali.flutter_osm_plugin.models.VoidCallback import hamza.dali.flutter_osm_plugin.utilities.toGeoPoint import hamza.dali.flutter_osm_plugin.utilities.toHashMap import io.flutter.plugin.common.MethodChannel diff --git a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/utilities/ExtensionOSM.kt b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/utilities/ExtensionOSM.kt index 80d2fafd..2f4c86ec 100644 --- a/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/utilities/ExtensionOSM.kt +++ b/android/src/main/kotlin/hamza/dali/flutter_osm_plugin/utilities/ExtensionOSM.kt @@ -10,9 +10,10 @@ import android.graphics.Paint import android.graphics.PathEffect import android.location.Location import android.provider.Settings -import hamza.dali.flutter_osm_plugin.FlutterOsmView +import hamza.dali.flutter_osm_plugin.map.FlutterOsmView import hamza.dali.flutter_osm_plugin.models.RoadGeoPointInstruction import hamza.dali.flutter_osm_plugin.models.toMap +import org.osmdroid.api.IGeoPoint import org.osmdroid.bonuspack.routing.Road import org.osmdroid.tileprovider.tilesource.ITileSource import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase @@ -26,7 +27,7 @@ import org.osmdroid.views.overlay.advancedpolyline.MonochromaticPaintList import java.io.ByteArrayOutputStream -fun GeoPoint.toHashMap(): HashMap { +fun IGeoPoint.toHashMap(): HashMap { return HashMap().apply { this[Constants.latLabel] = latitude this[Constants.lonLabel] = longitude