@maydon_tech/react-native-nitro-maps 0.1.4 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/NitroMap.podspec +1 -1
- package/README.md +82 -9
- package/android/CMakeLists.txt +4 -1
- package/android/build.gradle +6 -2
- package/android/gradle.properties +4 -4
- package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
- package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
- package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
- package/ios/Location/NitroLocationManager.swift +116 -0
- package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
- package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
- package/ios/NitroMap.swift +279 -13
- package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
- package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
- package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
- package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
- package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
- package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
- package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
- package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
- package/ios/Providers/MapProviderFactory.swift +17 -0
- package/ios/Providers/MapProviderProtocol.swift +48 -1
- package/ios/Shared/ClusterConfig+Factory.swift +2 -2
- package/ios/Shared/MapStyleProvider.swift +6 -4
- package/ios/Shared/MarkerSelectionHandler.swift +4 -1
- package/ios/Utils/ColorValueExtension.swift +46 -67
- package/lib/module/components/ImageMarker.js +39 -29
- package/lib/module/components/ImageMarker.js.map +1 -1
- package/lib/module/components/Marker.js +118 -0
- package/lib/module/components/Marker.js.map +1 -0
- package/lib/module/components/NitroCircle.js +92 -0
- package/lib/module/components/NitroCircle.js.map +1 -0
- package/lib/module/components/NitroMap.js +216 -76
- package/lib/module/components/NitroMap.js.map +1 -1
- package/lib/module/components/NitroPolygon.js +137 -0
- package/lib/module/components/NitroPolygon.js.map +1 -0
- package/lib/module/components/NitroPolyline.js +115 -0
- package/lib/module/components/NitroPolyline.js.map +1 -0
- package/lib/module/components/PriceMarker.js +16 -29
- package/lib/module/components/PriceMarker.js.map +1 -1
- package/lib/module/context/NitroMapContext.js.map +1 -1
- package/lib/module/hooks/useNitroCircle.js +18 -0
- package/lib/module/hooks/useNitroCircle.js.map +1 -0
- package/lib/module/hooks/useNitroMarker.js +26 -9
- package/lib/module/hooks/useNitroMarker.js.map +1 -1
- package/lib/module/hooks/useNitroOverlay.js +59 -0
- package/lib/module/hooks/useNitroOverlay.js.map +1 -0
- package/lib/module/hooks/useNitroPolygon.js +18 -0
- package/lib/module/hooks/useNitroPolygon.js.map +1 -0
- package/lib/module/hooks/useNitroPolyline.js +18 -0
- package/lib/module/hooks/useNitroPolyline.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types/overlay.js +4 -0
- package/lib/module/types/overlay.js.map +1 -0
- package/lib/module/types/theme.js +4 -0
- package/lib/module/types/theme.js.map +1 -0
- package/lib/module/utils/colors.js +41 -13
- package/lib/module/utils/colors.js.map +1 -1
- package/lib/module/utils/validation.js +45 -0
- package/lib/module/utils/validation.js.map +1 -0
- package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
- package/lib/typescript/src/components/Marker.d.ts +34 -0
- package/lib/typescript/src/components/Marker.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
- package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroMap.d.ts +60 -3
- package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
- package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
- package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
- package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
- package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
- package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
- package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
- package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
- package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
- package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
- package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
- package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +15 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
- package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
- package/lib/typescript/src/types/map.d.ts +34 -4
- package/lib/typescript/src/types/map.d.ts.map +1 -1
- package/lib/typescript/src/types/marker.d.ts +24 -36
- package/lib/typescript/src/types/marker.d.ts.map +1 -1
- package/lib/typescript/src/types/overlay.d.ts +84 -0
- package/lib/typescript/src/types/overlay.d.ts.map +1 -0
- package/lib/typescript/src/types/theme.d.ts +93 -0
- package/lib/typescript/src/types/theme.d.ts.map +1 -0
- package/lib/typescript/src/utils/colors.d.ts +6 -8
- package/lib/typescript/src/utils/colors.d.ts.map +1 -1
- package/lib/typescript/src/utils/validation.d.ts +12 -0
- package/lib/typescript/src/utils/validation.d.ts.map +1 -0
- package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
- package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
- package/nitrogen/generated/android/c++/JCoordinateRing.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
- package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
- package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +332 -21
- package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
- package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
- package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
- package/nitrogen/generated/android/c++/JPolygonData.hpp +133 -0
- package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
- package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
- package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
- package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CoordinateRing.kt +38 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
- package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
- package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +178 -17
- package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +18 -3
- package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +252 -16
- package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
- package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
- package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
- package/nitrogen/generated/ios/swift/CoordinateRing.swift +48 -0
- package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
- package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
- package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
- package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
- package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
- package/nitrogen/generated/ios/swift/PolygonData.swift +167 -0
- package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
- package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
- package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
- package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
- package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
- package/nitrogen/generated/shared/c++/CoordinateRing.hpp +77 -0
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
- package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
- package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
- package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
- package/nitrogen/generated/shared/c++/PolygonData.hpp +117 -0
- package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
- package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
- package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
- package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
- package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
- package/package.json +36 -5
- package/src/components/ImageMarker.tsx +58 -42
- package/src/components/Marker.tsx +161 -0
- package/src/components/NitroCircle.tsx +183 -0
- package/src/components/NitroMap.tsx +328 -78
- package/src/components/NitroPolygon.tsx +229 -0
- package/src/components/NitroPolyline.tsx +208 -0
- package/src/components/PriceMarker.tsx +23 -48
- package/src/context/NitroMapContext.tsx +4 -0
- package/src/hooks/useNitroCircle.ts +25 -0
- package/src/hooks/useNitroMarker.ts +49 -10
- package/src/hooks/useNitroOverlay.ts +68 -0
- package/src/hooks/useNitroPolygon.ts +25 -0
- package/src/hooks/useNitroPolyline.ts +25 -0
- package/src/index.tsx +28 -2
- package/src/specs/NitroMap.nitro.ts +294 -5
- package/src/types/map.ts +36 -4
- package/src/types/marker.ts +24 -44
- package/src/types/overlay.ts +87 -0
- package/src/types/theme.ts +101 -0
- package/src/utils/colors.ts +48 -16
- package/src/utils/validation.ts +69 -0
- package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
- package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
- package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
- package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
- package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
- package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
- package/cpp/QuadTree.hpp +0 -246
- package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
- package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
- package/ios/Providers/GoogleMapProvider.swift +0 -924
- package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
- package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
- package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
package com.margelo.nitro.nitromap.providers.google
|
|
2
|
+
|
|
3
|
+
import android.util.Log
|
|
4
|
+
import com.google.android.gms.maps.CameraUpdateFactory
|
|
5
|
+
import com.google.android.gms.maps.model.BitmapDescriptor
|
|
6
|
+
import com.google.android.gms.maps.model.LatLng
|
|
7
|
+
import com.google.android.gms.maps.model.Marker
|
|
8
|
+
import com.google.android.gms.maps.model.MarkerOptions
|
|
9
|
+
import com.margelo.nitro.nitromap.*
|
|
10
|
+
import com.margelo.nitro.nitromap.markers.MarkerSelectionHandler
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Marker CRUD + selection for GoogleMapProvider.
|
|
14
|
+
* Mirrors iOS GoogleMapProvider+Markers.swift.
|
|
15
|
+
*
|
|
16
|
+
* ALL markers are fed to the clustering engine (matches iOS behavior).
|
|
17
|
+
* The hideOnOverlap strategy needs visibility of all markers, including non-clusterable ones.
|
|
18
|
+
*
|
|
19
|
+
* Data structures:
|
|
20
|
+
* - markerData : source-of-truth for ALL marker data (survives detach)
|
|
21
|
+
* - clusteringManager : C++ spatial engine — receives ALL markers
|
|
22
|
+
* - nonClusteredMarkers : native markers for non-clusterable items (direct on map)
|
|
23
|
+
* - renderedSingleMarkers : native markers rendered by clustering pipeline
|
|
24
|
+
* - renderedClusterMarkers : native cluster icons rendered by clustering pipeline
|
|
25
|
+
*
|
|
26
|
+
* Threading: All native GoogleMap operations go through withMap {} for main-thread dispatch.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
// MARK: - Shared MarkerOptions builder (M-1: single function used by all callers)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Build MarkerOptions from MarkerData. Used by addMarkerSync, reapplyMarkers,
|
|
33
|
+
* selectMarker, and the clustering pipeline (GoogleMapProvider+Clustering.kt).
|
|
34
|
+
*/
|
|
35
|
+
fun GoogleMapProvider.buildMarkerOptions(data: MarkerData, icon: BitmapDescriptor? = null): MarkerOptions {
|
|
36
|
+
val opts = MarkerOptions()
|
|
37
|
+
.position(LatLng(data.coordinate.latitude, data.coordinate.longitude))
|
|
38
|
+
.title(data.title)
|
|
39
|
+
.snippet(data.description)
|
|
40
|
+
.draggable(data.draggable)
|
|
41
|
+
.alpha(data.opacity.toFloat())
|
|
42
|
+
.rotation(data.rotation.toFloat())
|
|
43
|
+
.zIndex(data.zIndex.toFloat())
|
|
44
|
+
.anchor(data.anchor.x.toFloat(), data.anchor.y.toFloat())
|
|
45
|
+
|
|
46
|
+
val resolvedIcon = icon ?: iconFactory?.createIcon(data)
|
|
47
|
+
if (resolvedIcon != null) opts.icon(resolvedIcon)
|
|
48
|
+
|
|
49
|
+
return opts
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Update existing native Marker properties in-place from MarkerData.
|
|
54
|
+
* Avoids remove/re-add flicker for cosmetic updates.
|
|
55
|
+
*/
|
|
56
|
+
fun GoogleMapProvider.applyMarkerProperties(marker: Marker, data: MarkerData) {
|
|
57
|
+
marker.position = LatLng(data.coordinate.latitude, data.coordinate.longitude)
|
|
58
|
+
marker.title = data.title
|
|
59
|
+
marker.snippet = data.description
|
|
60
|
+
marker.isDraggable = data.draggable
|
|
61
|
+
marker.alpha = data.opacity.toFloat()
|
|
62
|
+
marker.rotation = data.rotation.toFloat()
|
|
63
|
+
marker.zIndex = data.zIndex.toFloat()
|
|
64
|
+
marker.setAnchor(data.anchor.x.toFloat(), data.anchor.y.toFloat())
|
|
65
|
+
|
|
66
|
+
// Regenerate icon — cache handles deduplication (mirrors iOS updateGMSMarkerProperties)
|
|
67
|
+
iconFactory?.createIcon(data)?.let { marker.setIcon(it) }
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// M-5: Apply marker appear animation (matches iOS GMSMarker.appearAnimation)
|
|
71
|
+
// H-5: Use WeakReference to prevent animator from holding marker in memory after removal
|
|
72
|
+
// Semantic intent: 'appear' → platform's best native animation (alpha fade on Android)
|
|
73
|
+
// Alpha animation is used because position animation causes expensive map layout
|
|
74
|
+
// recalculations per frame, which degrades performance with many markers.
|
|
75
|
+
internal fun applyMarkerAnimation(marker: Marker, animation: MarkerAnimation, durationSec: Double = 0.3) {
|
|
76
|
+
if (animation == MarkerAnimation.NONE) return
|
|
77
|
+
|
|
78
|
+
// 'appear' — alpha fade-in
|
|
79
|
+
val weakMarker = java.lang.ref.WeakReference(marker)
|
|
80
|
+
val durationMs = (durationSec * 1000).toLong().coerceIn(50, 5000)
|
|
81
|
+
val targetAlpha = marker.alpha
|
|
82
|
+
marker.alpha = 0f
|
|
83
|
+
android.animation.ValueAnimator.ofFloat(0f, targetAlpha).apply {
|
|
84
|
+
duration = durationMs
|
|
85
|
+
addUpdateListener { anim ->
|
|
86
|
+
val m = weakMarker.get() ?: run { anim.cancel(); return@addUpdateListener }
|
|
87
|
+
try { m.alpha = anim.animatedValue as Float } catch (_: Exception) { anim.cancel() }
|
|
88
|
+
}
|
|
89
|
+
start()
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// MARK: - Marker CRUD
|
|
94
|
+
|
|
95
|
+
internal fun GoogleMapProvider.addMarkerInternal(marker: MarkerData) {
|
|
96
|
+
// M-1: Don't call performClustering() here — let onCameraIdle handle it naturally.
|
|
97
|
+
// This matches iOS behavior and avoids redundant clustering on each single add.
|
|
98
|
+
addMarkerSync(marker)
|
|
99
|
+
clusteringDirty = true
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
internal fun GoogleMapProvider.addMarkersInternal(markers: Array<MarkerData>) {
|
|
103
|
+
markers.forEach { addMarkerSync(it) }
|
|
104
|
+
clusteringDirty = true
|
|
105
|
+
performClustering()
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
internal fun GoogleMapProvider.updateMarkerInternal(marker: MarkerData) {
|
|
109
|
+
updateMarkerInPlace(marker)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
internal fun GoogleMapProvider.removeMarkerInternal(id: String) {
|
|
113
|
+
removeMarkerSync(id)
|
|
114
|
+
clusteringDirty = true
|
|
115
|
+
performClustering()
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
internal fun GoogleMapProvider.clearMarkersInternal() {
|
|
119
|
+
clearMarkersSync()
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// MARK: - Internal sync helpers
|
|
123
|
+
|
|
124
|
+
internal fun GoogleMapProvider.addMarkerSync(markerData: MarkerData) {
|
|
125
|
+
// Remove any existing marker with the same id first
|
|
126
|
+
removeMarkerSync(markerData.id)
|
|
127
|
+
|
|
128
|
+
// Store data (source of truth for ALL markers)
|
|
129
|
+
this.markerData[markerData.id] = markerData
|
|
130
|
+
|
|
131
|
+
// H-2: Add ALL markers to clustering engine (matches iOS behavior).
|
|
132
|
+
// hideOnOverlap strategy needs to see non-clusterable markers for overlap detection.
|
|
133
|
+
clusteringManager?.addMarker(markerData)
|
|
134
|
+
|
|
135
|
+
val shouldCluster = markerData.clusteringEnabled && clusterConfig?.enabled != false
|
|
136
|
+
|
|
137
|
+
if (!shouldCluster) {
|
|
138
|
+
// Non-clusterable → also place directly on the map (like iOS)
|
|
139
|
+
withMap { map ->
|
|
140
|
+
val icon = iconFactory?.createIcon(markerData)
|
|
141
|
+
val opts = buildMarkerOptions(markerData, icon)
|
|
142
|
+
val nativeMarker = map.addMarker(opts) ?: return@withMap
|
|
143
|
+
nativeMarker.tag = MarkerTagData.Regular(markerData.id)
|
|
144
|
+
nonClusteredMarkers[markerData.id] = nativeMarker
|
|
145
|
+
// M-5: Apply marker animation
|
|
146
|
+
if (markerData.animation != MarkerAnimation.NONE) {
|
|
147
|
+
applyMarkerAnimation(nativeMarker, markerData.animation, markerData.animationDuration)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
internal fun GoogleMapProvider.updateMarkerInPlace(markerData: MarkerData) {
|
|
154
|
+
val id = markerData.id
|
|
155
|
+
val existing = this.markerData[id]
|
|
156
|
+
if (existing == null) {
|
|
157
|
+
// Not found — add as new
|
|
158
|
+
addMarkerSync(markerData)
|
|
159
|
+
performClustering()
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// Check if clustering eligibility changed
|
|
164
|
+
val wasClusterable = existing.clusteringEnabled && clusterConfig?.enabled != false
|
|
165
|
+
val isClusterable = markerData.clusteringEnabled && clusterConfig?.enabled != false
|
|
166
|
+
|
|
167
|
+
if (wasClusterable != isClusterable) {
|
|
168
|
+
// Clustering state changed — full remove + re-add
|
|
169
|
+
removeMarkerSync(id)
|
|
170
|
+
addMarkerSync(markerData)
|
|
171
|
+
performClustering()
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Update stored data
|
|
176
|
+
this.markerData[id] = markerData
|
|
177
|
+
|
|
178
|
+
// Re-index in C++ engine only if coordinate changed
|
|
179
|
+
val coordChanged =
|
|
180
|
+
existing.coordinate.latitude != markerData.coordinate.latitude ||
|
|
181
|
+
existing.coordinate.longitude != markerData.coordinate.longitude
|
|
182
|
+
if (coordChanged) {
|
|
183
|
+
clusteringManager?.removeMarker(id)
|
|
184
|
+
clusteringManager?.addMarker(markerData)
|
|
185
|
+
performClustering()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Update rendered marker in-place (avoids remove/re-add flicker)
|
|
189
|
+
withMap { _ ->
|
|
190
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id] ?: return@withMap
|
|
191
|
+
applyMarkerProperties(nativeMarker, markerData)
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
internal fun GoogleMapProvider.removeMarkerSync(id: String) {
|
|
196
|
+
this.markerData.remove(id)
|
|
197
|
+
clusteringManager?.removeMarker(id)
|
|
198
|
+
|
|
199
|
+
// Remove native marker (must be on main thread via withMap)
|
|
200
|
+
withMap { _ ->
|
|
201
|
+
renderedSingleMarkers.remove(id)?.remove()
|
|
202
|
+
nonClusteredMarkers.remove(id)?.remove()
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
internal fun GoogleMapProvider.clearMarkersSync() {
|
|
207
|
+
this.markerData.clear()
|
|
208
|
+
clusteringManager?.clearMarkers()
|
|
209
|
+
selectedMarkerId = null
|
|
210
|
+
|
|
211
|
+
// Remove all native markers (must be on main thread via withMap)
|
|
212
|
+
withMap { _ ->
|
|
213
|
+
clearRenderedClusterMarkers()
|
|
214
|
+
for ((_, marker) in nonClusteredMarkers) { marker.remove() }
|
|
215
|
+
nonClusteredMarkers.clear()
|
|
216
|
+
}
|
|
217
|
+
Log.d("NitroMap", "clearMarkers")
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// MARK: - Selection
|
|
221
|
+
|
|
222
|
+
internal fun GoogleMapProvider.selectMarkerInternal(id: String) {
|
|
223
|
+
// Deselect any previously selected marker
|
|
224
|
+
if (selectedMarkerId != null && selectedMarkerId != id) {
|
|
225
|
+
deselectCurrentMarker()
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
selectedMarkerId = id
|
|
229
|
+
|
|
230
|
+
// Remove marker FROM clustering engine while selected —
|
|
231
|
+
// prevents clustering from destroying the native marker object
|
|
232
|
+
clusteringManager?.removeMarker(id)
|
|
233
|
+
|
|
234
|
+
// Update marker data to selected state
|
|
235
|
+
val data = this.markerData[id]
|
|
236
|
+
if (data != null) {
|
|
237
|
+
val updatedData = MarkerSelectionHandler.updateSelectionState(data, selected = true)
|
|
238
|
+
if (updatedData != null) {
|
|
239
|
+
this.markerData[id] = updatedData
|
|
240
|
+
withMap { _ ->
|
|
241
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id]
|
|
242
|
+
if (nativeMarker != null) {
|
|
243
|
+
iconFactory?.createIcon(updatedData)?.let { nativeMarker.setIcon(it) }
|
|
244
|
+
nativeMarker.zIndex = Float.MAX_VALUE
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
} else {
|
|
248
|
+
// Style doesn't support visual selection — just elevate zIndex
|
|
249
|
+
withMap { _ ->
|
|
250
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id]
|
|
251
|
+
nativeMarker?.zIndex = Float.MAX_VALUE
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Animate camera to marker + show info window
|
|
257
|
+
withMap { map ->
|
|
258
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id]
|
|
259
|
+
if (nativeMarker != null) {
|
|
260
|
+
map.animateCamera(
|
|
261
|
+
CameraUpdateFactory.newLatLng(nativeMarker.position),
|
|
262
|
+
300,
|
|
263
|
+
null
|
|
264
|
+
)
|
|
265
|
+
nativeMarker.showInfoWindow()
|
|
266
|
+
} else if (data != null) {
|
|
267
|
+
// Marker was inside a cluster — create a single marker for it
|
|
268
|
+
val opts = buildMarkerOptions(data)
|
|
269
|
+
opts.zIndex(Float.MAX_VALUE)
|
|
270
|
+
val marker = map.addMarker(opts)
|
|
271
|
+
if (marker != null) {
|
|
272
|
+
marker.tag = MarkerTagData.Regular(id)
|
|
273
|
+
renderedSingleMarkers[id] = marker
|
|
274
|
+
map.animateCamera(
|
|
275
|
+
CameraUpdateFactory.newLatLng(marker.position),
|
|
276
|
+
300,
|
|
277
|
+
null
|
|
278
|
+
)
|
|
279
|
+
marker.showInfoWindow()
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
Log.d("NitroMap", "selectMarker: $id")
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
internal fun GoogleMapProvider.deselectMarkerInternal() {
|
|
287
|
+
deselectCurrentMarker()
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
internal fun GoogleMapProvider.deselectCurrentMarker() {
|
|
291
|
+
val id = selectedMarkerId ?: return
|
|
292
|
+
selectedMarkerId = null
|
|
293
|
+
|
|
294
|
+
val data = this.markerData[id] ?: return
|
|
295
|
+
val updatedData = MarkerSelectionHandler.updateSelectionState(data, selected = false)
|
|
296
|
+
if (updatedData != null) {
|
|
297
|
+
this.markerData[id] = updatedData
|
|
298
|
+
withMap { _ ->
|
|
299
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id]
|
|
300
|
+
if (nativeMarker != null) {
|
|
301
|
+
iconFactory?.createIcon(updatedData)?.let { nativeMarker.setIcon(it) }
|
|
302
|
+
nativeMarker.zIndex = data.zIndex.toFloat()
|
|
303
|
+
nativeMarker.hideInfoWindow()
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} else {
|
|
307
|
+
withMap { _ ->
|
|
308
|
+
val nativeMarker = renderedSingleMarkers[id] ?: nonClusteredMarkers[id]
|
|
309
|
+
if (nativeMarker != null) {
|
|
310
|
+
nativeMarker.zIndex = data.zIndex.toFloat()
|
|
311
|
+
nativeMarker.hideInfoWindow()
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Add marker back into clustering engine
|
|
317
|
+
clusteringManager?.addMarker(data)
|
|
318
|
+
|
|
319
|
+
Log.d("NitroMap", "deselectMarker")
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// MARK: - Re-apply on map ready (called from applyAllSettings)
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Re-adds all stored markers to the (new) GoogleMap after reattach.
|
|
326
|
+
* Called from GoogleMapProvider.applyAllSettings().
|
|
327
|
+
*/
|
|
328
|
+
fun GoogleMapProvider.reapplyMarkers() {
|
|
329
|
+
if (markerData.isEmpty()) return
|
|
330
|
+
|
|
331
|
+
// Clear stale native references (old map instance)
|
|
332
|
+
nonClusteredMarkers.clear()
|
|
333
|
+
|
|
334
|
+
// H-2: Rebuild clustering engine from stored data — add ALL markers
|
|
335
|
+
clusteringManager?.clearMarkers()
|
|
336
|
+
|
|
337
|
+
for ((_, data) in markerData) {
|
|
338
|
+
// H-2: ALL markers go to clustering engine (matches iOS)
|
|
339
|
+
clusteringManager?.addMarker(data)
|
|
340
|
+
|
|
341
|
+
val shouldCluster = data.clusteringEnabled && clusterConfig?.enabled != false
|
|
342
|
+
if (!shouldCluster) {
|
|
343
|
+
// Re-create non-clustered marker
|
|
344
|
+
withMap { map ->
|
|
345
|
+
val opts = buildMarkerOptions(data)
|
|
346
|
+
val nativeMarker = map.addMarker(opts) ?: return@withMap
|
|
347
|
+
nativeMarker.tag = MarkerTagData.Regular(data.id)
|
|
348
|
+
if (data.id == selectedMarkerId) nativeMarker.zIndex = Float.MAX_VALUE
|
|
349
|
+
nonClusteredMarkers[data.id] = nativeMarker
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// Clustered markers will be rendered when performClustering runs
|
|
355
|
+
performClustering()
|
|
356
|
+
|
|
357
|
+
Log.d("NitroMap", "reapplyMarkers: ${markerData.size} markers")
|
|
358
|
+
}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
package com.margelo.nitro.nitromap.providers.google
|
|
2
|
+
|
|
3
|
+
import android.graphics.Color
|
|
4
|
+
import com.margelo.nitro.nitromap.*
|
|
5
|
+
import android.util.Log
|
|
6
|
+
import com.google.android.gms.maps.model.CircleOptions
|
|
7
|
+
import com.google.android.gms.maps.model.Dash
|
|
8
|
+
import com.google.android.gms.maps.model.Gap
|
|
9
|
+
import com.google.android.gms.maps.model.LatLng
|
|
10
|
+
import com.google.android.gms.maps.model.PatternItem
|
|
11
|
+
import com.google.android.gms.maps.model.PolygonOptions
|
|
12
|
+
import com.google.android.gms.maps.model.PolylineOptions
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Overlay CRUD for GoogleMapProvider — Polylines, Polygons, Circles.
|
|
16
|
+
*
|
|
17
|
+
* Mirrors iOS GoogleMapProvider+Overlays.swift.
|
|
18
|
+
*
|
|
19
|
+
* Data dictionaries (polylineData, polygonData, circleData) survive detach/reattach.
|
|
20
|
+
* Native overlay objects (renderedPolylines, renderedPolygons, renderedCircles) are
|
|
21
|
+
* recreated from data on reattach via reapplyOverlays().
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ============================================================================
|
|
25
|
+
// MARK: - Shared Color Helper (M-4: single resolveColor for all overlay code)
|
|
26
|
+
// ============================================================================
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Convert a MarkerColor to an Android ARGB int.
|
|
30
|
+
* Used by overlays and available to other files that need color conversion.
|
|
31
|
+
*/
|
|
32
|
+
fun MarkerColor.toArgb(): Int =
|
|
33
|
+
Color.argb(a.toInt(), r.toInt(), g.toInt(), b.toInt())
|
|
34
|
+
|
|
35
|
+
// ============================================================================
|
|
36
|
+
// MARK: - Shared Overlay Builders (M-3: extract from duplicated code)
|
|
37
|
+
// ============================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build PolylineOptions from PolylineData. Used by both addPolyline and reapplyOverlays.
|
|
41
|
+
*/
|
|
42
|
+
private fun GoogleMapProvider.buildPolylineOptions(data: PolylineData): PolylineOptions {
|
|
43
|
+
val opts = PolylineOptions()
|
|
44
|
+
|
|
45
|
+
for (coord in data.coordinates) {
|
|
46
|
+
opts.add(LatLng(coord.latitude, coord.longitude))
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
opts.width(data.strokeWidth.toFloat())
|
|
50
|
+
opts.color(data.strokeColor.toArgb())
|
|
51
|
+
opts.geodesic(data.geodesic)
|
|
52
|
+
opts.zIndex(data.zIndex.toFloat())
|
|
53
|
+
opts.clickable(data.tappable)
|
|
54
|
+
|
|
55
|
+
// Dashed line support
|
|
56
|
+
// M-6: Standardize density access
|
|
57
|
+
if (data.dashed) {
|
|
58
|
+
val density = context.resources.displayMetrics.density
|
|
59
|
+
val pattern: List<PatternItem> = listOf(
|
|
60
|
+
Dash(20f * density),
|
|
61
|
+
Gap(10f * density)
|
|
62
|
+
)
|
|
63
|
+
opts.pattern(pattern)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return opts
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Build PolygonOptions from PolygonData. Used by both addPolygon and reapplyOverlays.
|
|
71
|
+
*/
|
|
72
|
+
private fun buildPolygonOptions(data: PolygonData): PolygonOptions {
|
|
73
|
+
val opts = PolygonOptions()
|
|
74
|
+
|
|
75
|
+
for (coord in data.coordinates) {
|
|
76
|
+
opts.add(LatLng(coord.latitude, coord.longitude))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
opts.fillColor(data.fillColor.toArgb())
|
|
80
|
+
opts.strokeColor(data.strokeColor.toArgb())
|
|
81
|
+
opts.strokeWidth(data.strokeWidth.toFloat())
|
|
82
|
+
opts.zIndex(data.zIndex.toFloat())
|
|
83
|
+
opts.clickable(data.tappable)
|
|
84
|
+
|
|
85
|
+
// Add holes
|
|
86
|
+
for (ring in data.holes) {
|
|
87
|
+
val holePath = ring.coordinates.map { LatLng(it.latitude, it.longitude) }
|
|
88
|
+
opts.addHole(holePath)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return opts
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Build CircleOptions from CircleData. Used by both addCircle and reapplyOverlays.
|
|
96
|
+
*/
|
|
97
|
+
private fun buildCircleOptions(data: CircleData): CircleOptions {
|
|
98
|
+
return CircleOptions()
|
|
99
|
+
.center(LatLng(data.center.latitude, data.center.longitude))
|
|
100
|
+
.radius(data.radius)
|
|
101
|
+
.fillColor(data.fillColor.toArgb())
|
|
102
|
+
.strokeColor(data.strokeColor.toArgb())
|
|
103
|
+
.strokeWidth(data.strokeWidth.toFloat())
|
|
104
|
+
.zIndex(data.zIndex.toFloat())
|
|
105
|
+
.clickable(data.tappable)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================================
|
|
109
|
+
// MARK: - Polyline CRUD
|
|
110
|
+
// ============================================================================
|
|
111
|
+
|
|
112
|
+
internal fun GoogleMapProvider.addPolylineInternal(polyline: PolylineData) {
|
|
113
|
+
// Store data (survives detach)
|
|
114
|
+
polylineData[polyline.id] = polyline
|
|
115
|
+
|
|
116
|
+
// Remove existing + place new native Polyline (all on main thread)
|
|
117
|
+
withMap { map ->
|
|
118
|
+
renderedPolylines.remove(polyline.id)?.remove()
|
|
119
|
+
val opts = buildPolylineOptions(polyline)
|
|
120
|
+
val nativePolyline = map.addPolyline(opts)
|
|
121
|
+
nativePolyline.tag = polyline.id
|
|
122
|
+
renderedPolylines[polyline.id] = nativePolyline
|
|
123
|
+
Log.d("NitroMap", "addPolyline: ${polyline.id}")
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
internal fun GoogleMapProvider.updatePolylineInternal(polyline: PolylineData) {
|
|
128
|
+
addPolylineInternal(polyline)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
internal fun GoogleMapProvider.removePolylineInternal(id: String) {
|
|
132
|
+
polylineData.remove(id)
|
|
133
|
+
withMap { _ ->
|
|
134
|
+
renderedPolylines.remove(id)?.remove()
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
internal fun GoogleMapProvider.clearPolylinesInternal() {
|
|
139
|
+
polylineData.clear()
|
|
140
|
+
withMap { _ ->
|
|
141
|
+
renderedPolylines.values.forEach { it.remove() }
|
|
142
|
+
renderedPolylines.clear()
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================================================
|
|
147
|
+
// MARK: - Polygon CRUD
|
|
148
|
+
// ============================================================================
|
|
149
|
+
|
|
150
|
+
internal fun GoogleMapProvider.addPolygonInternal(polygon: PolygonData) {
|
|
151
|
+
// Store data (survives detach)
|
|
152
|
+
polygonData[polygon.id] = polygon
|
|
153
|
+
|
|
154
|
+
// Remove existing + place new native Polygon (all on main thread)
|
|
155
|
+
withMap { map ->
|
|
156
|
+
renderedPolygons.remove(polygon.id)?.remove()
|
|
157
|
+
val opts = buildPolygonOptions(polygon)
|
|
158
|
+
val nativePolygon = map.addPolygon(opts)
|
|
159
|
+
nativePolygon.tag = polygon.id
|
|
160
|
+
renderedPolygons[polygon.id] = nativePolygon
|
|
161
|
+
Log.d("NitroMap", "addPolygon: ${polygon.id}")
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
internal fun GoogleMapProvider.updatePolygonInternal(polygon: PolygonData) {
|
|
166
|
+
addPolygonInternal(polygon)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
internal fun GoogleMapProvider.removePolygonInternal(id: String) {
|
|
170
|
+
polygonData.remove(id)
|
|
171
|
+
withMap { _ ->
|
|
172
|
+
renderedPolygons.remove(id)?.remove()
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
internal fun GoogleMapProvider.clearPolygonsInternal() {
|
|
177
|
+
polygonData.clear()
|
|
178
|
+
withMap { _ ->
|
|
179
|
+
renderedPolygons.values.forEach { it.remove() }
|
|
180
|
+
renderedPolygons.clear()
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ============================================================================
|
|
185
|
+
// MARK: - Circle CRUD
|
|
186
|
+
// ============================================================================
|
|
187
|
+
|
|
188
|
+
internal fun GoogleMapProvider.addCircleInternal(circle: CircleData) {
|
|
189
|
+
// Store data (survives detach)
|
|
190
|
+
circleData[circle.id] = circle
|
|
191
|
+
|
|
192
|
+
// Remove existing + place new native Circle (all on main thread)
|
|
193
|
+
withMap { map ->
|
|
194
|
+
renderedCircles.remove(circle.id)?.remove()
|
|
195
|
+
val opts = buildCircleOptions(circle)
|
|
196
|
+
val nativeCircle = map.addCircle(opts)
|
|
197
|
+
nativeCircle.tag = circle.id
|
|
198
|
+
renderedCircles[circle.id] = nativeCircle
|
|
199
|
+
Log.d("NitroMap", "addCircle: ${circle.id}")
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
internal fun GoogleMapProvider.updateCircleInternal(circle: CircleData) {
|
|
204
|
+
addCircleInternal(circle)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
internal fun GoogleMapProvider.removeCircleInternal(id: String) {
|
|
208
|
+
circleData.remove(id)
|
|
209
|
+
withMap { _ ->
|
|
210
|
+
renderedCircles.remove(id)?.remove()
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
internal fun GoogleMapProvider.clearCirclesInternal() {
|
|
215
|
+
circleData.clear()
|
|
216
|
+
withMap { _ ->
|
|
217
|
+
renderedCircles.values.forEach { it.remove() }
|
|
218
|
+
renderedCircles.clear()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// MARK: - Reapply on MapView Reattach (M-3: uses shared builder helpers)
|
|
224
|
+
// ============================================================================
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Re-adds all stored overlays to the (new) GoogleMap after reattach.
|
|
228
|
+
* Called from GoogleMapProvider.applyAllSettings() after reapplyMarkers().
|
|
229
|
+
*/
|
|
230
|
+
fun GoogleMapProvider.reapplyOverlays() {
|
|
231
|
+
// Clear old native references (invalid after detach)
|
|
232
|
+
renderedPolylines.clear()
|
|
233
|
+
renderedPolygons.clear()
|
|
234
|
+
renderedCircles.clear()
|
|
235
|
+
|
|
236
|
+
// Re-create from stored data using shared builder helpers
|
|
237
|
+
if (polylineData.isNotEmpty()) {
|
|
238
|
+
polylineData.values.toList().forEach { data ->
|
|
239
|
+
withMap { map ->
|
|
240
|
+
val opts = buildPolylineOptions(data)
|
|
241
|
+
val native = map.addPolyline(opts)
|
|
242
|
+
native.tag = data.id
|
|
243
|
+
renderedPolylines[data.id] = native
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
Log.d("NitroMap", "reapplyOverlays: ${polylineData.size} polylines")
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (polygonData.isNotEmpty()) {
|
|
250
|
+
polygonData.values.toList().forEach { data ->
|
|
251
|
+
withMap { map ->
|
|
252
|
+
val opts = buildPolygonOptions(data)
|
|
253
|
+
val native = map.addPolygon(opts)
|
|
254
|
+
native.tag = data.id
|
|
255
|
+
renderedPolygons[data.id] = native
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
Log.d("NitroMap", "reapplyOverlays: ${polygonData.size} polygons")
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (circleData.isNotEmpty()) {
|
|
262
|
+
circleData.values.toList().forEach { data ->
|
|
263
|
+
withMap { map ->
|
|
264
|
+
val opts = buildCircleOptions(data)
|
|
265
|
+
val native = map.addCircle(opts)
|
|
266
|
+
native.tag = data.id
|
|
267
|
+
renderedCircles[data.id] = native
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
Log.d("NitroMap", "reapplyOverlays: ${circleData.size} circles")
|
|
271
|
+
}
|
|
272
|
+
}
|