@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.
Files changed (222) hide show
  1. package/LICENSE +1 -1
  2. package/NitroMap.podspec +1 -1
  3. package/README.md +82 -9
  4. package/android/CMakeLists.txt +4 -1
  5. package/android/build.gradle +6 -2
  6. package/android/gradle.properties +4 -4
  7. package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
  8. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
  9. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
  10. package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
  11. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
  12. package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
  13. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
  14. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
  15. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
  16. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
  17. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
  18. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
  19. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
  20. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
  21. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
  22. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
  23. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
  24. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
  25. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
  26. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
  27. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
  28. package/ios/Location/NitroLocationManager.swift +116 -0
  29. package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
  30. package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
  31. package/ios/NitroMap.swift +279 -13
  32. package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
  33. package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
  34. package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
  35. package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
  36. package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
  37. package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
  38. package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
  39. package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
  40. package/ios/Providers/MapProviderFactory.swift +17 -0
  41. package/ios/Providers/MapProviderProtocol.swift +48 -1
  42. package/ios/Shared/ClusterConfig+Factory.swift +2 -2
  43. package/ios/Shared/MapStyleProvider.swift +6 -4
  44. package/ios/Shared/MarkerSelectionHandler.swift +4 -1
  45. package/ios/Utils/ColorValueExtension.swift +46 -67
  46. package/lib/module/components/ImageMarker.js +39 -29
  47. package/lib/module/components/ImageMarker.js.map +1 -1
  48. package/lib/module/components/Marker.js +118 -0
  49. package/lib/module/components/Marker.js.map +1 -0
  50. package/lib/module/components/NitroCircle.js +92 -0
  51. package/lib/module/components/NitroCircle.js.map +1 -0
  52. package/lib/module/components/NitroMap.js +216 -76
  53. package/lib/module/components/NitroMap.js.map +1 -1
  54. package/lib/module/components/NitroPolygon.js +137 -0
  55. package/lib/module/components/NitroPolygon.js.map +1 -0
  56. package/lib/module/components/NitroPolyline.js +115 -0
  57. package/lib/module/components/NitroPolyline.js.map +1 -0
  58. package/lib/module/components/PriceMarker.js +16 -29
  59. package/lib/module/components/PriceMarker.js.map +1 -1
  60. package/lib/module/context/NitroMapContext.js.map +1 -1
  61. package/lib/module/hooks/useNitroCircle.js +18 -0
  62. package/lib/module/hooks/useNitroCircle.js.map +1 -0
  63. package/lib/module/hooks/useNitroMarker.js +26 -9
  64. package/lib/module/hooks/useNitroMarker.js.map +1 -1
  65. package/lib/module/hooks/useNitroOverlay.js +59 -0
  66. package/lib/module/hooks/useNitroOverlay.js.map +1 -0
  67. package/lib/module/hooks/useNitroPolygon.js +18 -0
  68. package/lib/module/hooks/useNitroPolygon.js.map +1 -0
  69. package/lib/module/hooks/useNitroPolyline.js +18 -0
  70. package/lib/module/hooks/useNitroPolyline.js.map +1 -0
  71. package/lib/module/index.js +5 -0
  72. package/lib/module/index.js.map +1 -1
  73. package/lib/module/types/overlay.js +4 -0
  74. package/lib/module/types/overlay.js.map +1 -0
  75. package/lib/module/types/theme.js +4 -0
  76. package/lib/module/types/theme.js.map +1 -0
  77. package/lib/module/utils/colors.js +41 -13
  78. package/lib/module/utils/colors.js.map +1 -1
  79. package/lib/module/utils/validation.js +45 -0
  80. package/lib/module/utils/validation.js.map +1 -0
  81. package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
  82. package/lib/typescript/src/components/Marker.d.ts +34 -0
  83. package/lib/typescript/src/components/Marker.d.ts.map +1 -0
  84. package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
  85. package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
  86. package/lib/typescript/src/components/NitroMap.d.ts +60 -3
  87. package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
  88. package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
  89. package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
  90. package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
  91. package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
  92. package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
  93. package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
  94. package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
  95. package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
  96. package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
  97. package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
  98. package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
  99. package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
  100. package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
  101. package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
  102. package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
  103. package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
  104. package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
  105. package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
  106. package/lib/typescript/src/index.d.ts +15 -2
  107. package/lib/typescript/src/index.d.ts.map +1 -1
  108. package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
  109. package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
  110. package/lib/typescript/src/types/map.d.ts +34 -4
  111. package/lib/typescript/src/types/map.d.ts.map +1 -1
  112. package/lib/typescript/src/types/marker.d.ts +24 -36
  113. package/lib/typescript/src/types/marker.d.ts.map +1 -1
  114. package/lib/typescript/src/types/overlay.d.ts +84 -0
  115. package/lib/typescript/src/types/overlay.d.ts.map +1 -0
  116. package/lib/typescript/src/types/theme.d.ts +93 -0
  117. package/lib/typescript/src/types/theme.d.ts.map +1 -0
  118. package/lib/typescript/src/utils/colors.d.ts +6 -8
  119. package/lib/typescript/src/utils/colors.d.ts.map +1 -1
  120. package/lib/typescript/src/utils/validation.d.ts +12 -0
  121. package/lib/typescript/src/utils/validation.d.ts.map +1 -0
  122. package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
  123. package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
  124. package/nitrogen/generated/android/c++/JCoordinateRing.hpp +77 -0
  125. package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
  126. package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
  127. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  128. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +332 -21
  129. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
  130. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
  131. package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
  132. package/nitrogen/generated/android/c++/JPolygonData.hpp +133 -0
  133. package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
  134. package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
  135. package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
  136. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CoordinateRing.kt +38 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
  148. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
  149. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
  150. package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
  151. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
  152. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +178 -17
  153. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +18 -3
  154. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +252 -16
  155. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
  156. package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
  157. package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
  158. package/nitrogen/generated/ios/swift/CoordinateRing.swift +48 -0
  159. package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
  160. package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
  161. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  162. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
  163. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
  164. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
  165. package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
  166. package/nitrogen/generated/ios/swift/PolygonData.swift +167 -0
  167. package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
  168. package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
  169. package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
  170. package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
  171. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
  172. package/nitrogen/generated/shared/c++/CoordinateRing.hpp +77 -0
  173. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
  174. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
  175. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
  176. package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
  177. package/nitrogen/generated/shared/c++/PolygonData.hpp +117 -0
  178. package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
  179. package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
  180. package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
  181. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
  182. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
  183. package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
  184. package/package.json +36 -5
  185. package/src/components/ImageMarker.tsx +58 -42
  186. package/src/components/Marker.tsx +161 -0
  187. package/src/components/NitroCircle.tsx +183 -0
  188. package/src/components/NitroMap.tsx +328 -78
  189. package/src/components/NitroPolygon.tsx +229 -0
  190. package/src/components/NitroPolyline.tsx +208 -0
  191. package/src/components/PriceMarker.tsx +23 -48
  192. package/src/context/NitroMapContext.tsx +4 -0
  193. package/src/hooks/useNitroCircle.ts +25 -0
  194. package/src/hooks/useNitroMarker.ts +49 -10
  195. package/src/hooks/useNitroOverlay.ts +68 -0
  196. package/src/hooks/useNitroPolygon.ts +25 -0
  197. package/src/hooks/useNitroPolyline.ts +25 -0
  198. package/src/index.tsx +28 -2
  199. package/src/specs/NitroMap.nitro.ts +294 -5
  200. package/src/types/map.ts +36 -4
  201. package/src/types/marker.ts +24 -44
  202. package/src/types/overlay.ts +87 -0
  203. package/src/types/theme.ts +101 -0
  204. package/src/utils/colors.ts +48 -16
  205. package/src/utils/validation.ts +69 -0
  206. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
  207. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
  208. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
  209. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
  210. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
  211. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
  212. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
  213. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
  214. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
  215. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
  216. package/cpp/QuadTree.hpp +0 -246
  217. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
  218. package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
  219. package/ios/Providers/GoogleMapProvider.swift +0 -924
  220. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
  221. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
  222. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
@@ -1,912 +0,0 @@
1
- package com.margelo.nitro.nitromap.providers
2
-
3
- import android.Manifest
4
- import android.annotation.SuppressLint
5
- import android.content.Context
6
- import android.content.pm.PackageManager
7
- import android.os.Handler
8
- import android.os.Looper
9
- import android.view.View
10
- import android.view.ViewTreeObserver
11
- import androidx.core.content.ContextCompat
12
- import com.google.android.gms.maps.CameraUpdateFactory
13
- import com.google.android.gms.maps.GoogleMap
14
- import com.google.android.gms.maps.MapView
15
- import com.google.android.gms.maps.OnMapReadyCallback
16
- import com.google.android.gms.maps.model.*
17
- import com.margelo.nitro.core.Promise
18
- import com.margelo.nitro.nitromap.*
19
- import com.margelo.nitro.nitromap.clustering.Cluster
20
- import com.margelo.nitro.nitromap.clustering.NitroClusterEngine
21
- import kotlin.math.max
22
- import org.json.JSONArray
23
- import org.json.JSONObject
24
-
25
- /** User data attached to cluster markers for tap handling */
26
- data class ClusterUserData(val markerIds: List<String>, val count: Int)
27
-
28
- /** Google Maps implementation of MapProviderInterface */
29
- class GoogleMapProvider(private val context: Context) : MapProviderInterface, OnMapReadyCallback {
30
-
31
- private val _mapView: MapView = MapView(context)
32
- override val mapView: View
33
- get() = _mapView
34
-
35
- private var googleMap: GoogleMap? = null
36
- private val mainHandler = Handler(Looper.getMainLooper())
37
-
38
- // Clustering engine
39
- private val clusterEngine = NitroClusterEngine()
40
- private var clusterIconGenerator: ClusterIconGenerator? = null
41
-
42
- // Rendered markers on map
43
- private val renderedClusterMarkers = mutableListOf<Marker>()
44
- private val renderedSingleMarkers = mutableMapOf<String, Marker>()
45
- private val nonClusteredMarkers = mutableMapOf<String, Marker>()
46
-
47
- // Track clusterable marker data
48
- private val clusterableMarkerData = mutableMapOf<String, MarkerData>()
49
-
50
- // Track selected marker ID for visual state
51
- private var selectedMarkerId: String? = null
52
-
53
- // Track if we need to cluster after layout
54
- private var needsClusteringAfterLayout = false
55
- private var hasLayout = false
56
- private var initialRegionApplied = false
57
-
58
- // MARK: - Properties
59
-
60
- private var _initialRegion: Region? = null
61
- override var initialRegion: Region?
62
- get() = _initialRegion
63
- set(value) {
64
- _initialRegion = value
65
- value?.let { updateCameraToInitialRegion(it) }
66
- }
67
-
68
- private var _showsUserLocation: Boolean? = null
69
- override var showsUserLocation: Boolean?
70
- get() = _showsUserLocation
71
- @SuppressLint("MissingPermission")
72
- set(value) {
73
- _showsUserLocation = value
74
- googleMap?.let { map ->
75
- if (value == true && hasLocationPermission()) {
76
- try {
77
- map.isMyLocationEnabled = true
78
- } catch (e: SecurityException) {
79
- e.printStackTrace()
80
- }
81
- } else {
82
- try {
83
- map.isMyLocationEnabled = false
84
- } catch (e: SecurityException) {
85
- e.printStackTrace()
86
- }
87
- }
88
- }
89
- }
90
-
91
- private var _showsMyLocationButton: Boolean? = null
92
- override var showsMyLocationButton: Boolean?
93
- get() = _showsMyLocationButton
94
- set(value) {
95
- _showsMyLocationButton = value
96
- googleMap?.uiSettings?.isMyLocationButtonEnabled = value ?: false
97
- }
98
-
99
- private var _zoomEnabled: Boolean? = null
100
- override var zoomEnabled: Boolean?
101
- get() = _zoomEnabled
102
- set(value) {
103
- _zoomEnabled = value
104
- googleMap?.uiSettings?.isZoomGesturesEnabled = value ?: true
105
- }
106
-
107
- private var _scrollEnabled: Boolean? = null
108
- override var scrollEnabled: Boolean?
109
- get() = _scrollEnabled
110
- set(value) {
111
- _scrollEnabled = value
112
- googleMap?.uiSettings?.isScrollGesturesEnabled = value ?: true
113
- }
114
-
115
- private var _rotateEnabled: Boolean? = null
116
- override var rotateEnabled: Boolean?
117
- get() = _rotateEnabled
118
- set(value) {
119
- _rotateEnabled = value
120
- googleMap?.uiSettings?.isRotateGesturesEnabled = value ?: true
121
- }
122
-
123
- private var _pitchEnabled: Boolean? = null
124
- override var pitchEnabled: Boolean?
125
- get() = _pitchEnabled
126
- set(value) {
127
- _pitchEnabled = value
128
- googleMap?.uiSettings?.isTiltGesturesEnabled = value ?: true
129
- }
130
-
131
- private var _mapType: MapType? = null
132
- override var mapType: MapType?
133
- get() = _mapType
134
- set(value) {
135
- _mapType = value
136
- googleMap?.mapType = convertMapType(value)
137
- }
138
-
139
- private var _clusterConfig: ClusterConfig? = null
140
- override var clusterConfig: ClusterConfig?
141
- get() = _clusterConfig
142
- set(value) {
143
- _clusterConfig = value
144
- updateClusterConfig()
145
- }
146
-
147
- private var _darkMode: Boolean? = null
148
- override var darkMode: Boolean?
149
- get() = _darkMode
150
- set(value) {
151
- _darkMode = value
152
- applyDarkModeStyle()
153
- }
154
-
155
- private var _customMapStyle: Array<MapStyleElement>? = null
156
- override var customMapStyle: Array<MapStyleElement>?
157
- get() = _customMapStyle
158
- set(value) {
159
- _customMapStyle = value
160
- applyMapStyle()
161
- }
162
-
163
- // MARK: - Callbacks
164
-
165
- override var onMapReady: (() -> Unit)? = null
166
- override var onPress: ((event: MapPressEvent) -> Unit)? = null
167
- override var onLongPress: ((event: MapPressEvent) -> Unit)? = null
168
- override var onRegionChange: ((event: RegionChangeEvent) -> Unit)? = null
169
- override var onRegionChangeComplete: ((event: RegionChangeEvent) -> Unit)? = null
170
- override var onMarkerPress: ((event: MarkerPressEvent) -> Unit)? = null
171
- override var onMarkerDragStart: ((event: MarkerDragEvent) -> Unit)? = null
172
- override var onMarkerDrag: ((event: MarkerDragEvent) -> Unit)? = null
173
- override var onMarkerDragEnd: ((event: MarkerDragEvent) -> Unit)? = null
174
- override var onClusterPress: ((event: ClusterPressEvent) -> Unit)? = null
175
- override var onError: ((error: MapError) -> Unit)? = null
176
-
177
- // MARK: - Setup
178
-
179
- override fun setup() {
180
- _mapView.onCreate(null)
181
- _mapView.getMapAsync(this)
182
-
183
- clusterIconGenerator = ClusterIconGenerator(context)
184
- clusterEngine.setClusterRadius(60.0)
185
- clusterEngine.setMinClusterSize(2)
186
- clusterEngine.setMaxZoom(20.0)
187
-
188
- _mapView.viewTreeObserver.addOnGlobalLayoutListener(
189
- object : ViewTreeObserver.OnGlobalLayoutListener {
190
- override fun onGlobalLayout() {
191
- if (_mapView.width > 0 && _mapView.height > 0) {
192
- hasLayout = true
193
- if (needsClusteringAfterLayout) {
194
- needsClusteringAfterLayout = false
195
- performClustering()
196
- }
197
- }
198
- }
199
- }
200
- )
201
- }
202
-
203
- override fun onMapReady(map: GoogleMap) {
204
- googleMap = map
205
- applyAllProperties()
206
- setupMapListeners(map)
207
- onMapReady?.invoke()
208
- }
209
-
210
- @SuppressLint("MissingPermission")
211
- private fun applyAllProperties() {
212
- googleMap?.let { map ->
213
- map.uiSettings.isZoomGesturesEnabled = _zoomEnabled ?: true
214
- map.uiSettings.isScrollGesturesEnabled = _scrollEnabled ?: true
215
- map.uiSettings.isRotateGesturesEnabled = _rotateEnabled ?: true
216
- map.uiSettings.isTiltGesturesEnabled = _pitchEnabled ?: true
217
- map.uiSettings.isMyLocationButtonEnabled = _showsMyLocationButton ?: false
218
- map.mapType = convertMapType(_mapType)
219
-
220
- if (_showsUserLocation == true && hasLocationPermission()) {
221
- try {
222
- map.isMyLocationEnabled = true
223
- } catch (e: SecurityException) {
224
- e.printStackTrace()
225
- }
226
- }
227
-
228
- if (!initialRegionApplied) {
229
- _initialRegion?.let {
230
- updateCameraToInitialRegion(it)
231
- initialRegionApplied = true
232
- }
233
- }
234
- applyMapStyle()
235
- applyDarkModeStyle()
236
- }
237
- }
238
-
239
- private fun setupMapListeners(map: GoogleMap) {
240
- map.setOnMapClickListener { latLng ->
241
- val projection = map.projection
242
- val screenPoint = projection.toScreenLocation(latLng)
243
- onPress?.invoke(
244
- MapPressEvent(
245
- coordinate = Coordinate(latLng.latitude, latLng.longitude),
246
- position = Point(screenPoint.x.toDouble(), screenPoint.y.toDouble())
247
- )
248
- )
249
- }
250
-
251
- map.setOnMapLongClickListener { latLng ->
252
- val projection = map.projection
253
- val screenPoint = projection.toScreenLocation(latLng)
254
- onLongPress?.invoke(
255
- MapPressEvent(
256
- coordinate = Coordinate(latLng.latitude, latLng.longitude),
257
- position = Point(screenPoint.x.toDouble(), screenPoint.y.toDouble())
258
- )
259
- )
260
- }
261
-
262
- map.setOnCameraMoveListener {
263
- val camera = map.cameraPosition
264
- val bounds = map.projection.visibleRegion.latLngBounds
265
- onRegionChange?.invoke(
266
- RegionChangeEvent(
267
- region =
268
- Region(
269
- latitude = camera.target.latitude,
270
- longitude = camera.target.longitude,
271
- latitudeDelta =
272
- bounds.northeast.latitude -
273
- bounds.southwest.latitude,
274
- longitudeDelta =
275
- bounds.northeast.longitude -
276
- bounds.southwest.longitude
277
- ),
278
- isGesture = true
279
- )
280
- )
281
- }
282
-
283
- map.setOnCameraIdleListener {
284
- val camera = map.cameraPosition
285
- val bounds = map.projection.visibleRegion.latLngBounds
286
- onRegionChangeComplete?.invoke(
287
- RegionChangeEvent(
288
- region =
289
- Region(
290
- latitude = camera.target.latitude,
291
- longitude = camera.target.longitude,
292
- latitudeDelta =
293
- bounds.northeast.latitude -
294
- bounds.southwest.latitude,
295
- longitudeDelta =
296
- bounds.northeast.longitude -
297
- bounds.southwest.longitude
298
- ),
299
- isGesture = false
300
- )
301
- )
302
- performClustering()
303
- }
304
-
305
- map.setOnMarkerClickListener { marker ->
306
- when (val userData = marker.tag) {
307
- is ClusterUserData -> {
308
- onClusterPress?.invoke(
309
- ClusterPressEvent(
310
- coordinate =
311
- Coordinate(
312
- marker.position.latitude,
313
- marker.position.longitude
314
- ),
315
- markerIds = userData.markerIds.toTypedArray(),
316
- count = userData.count.toDouble()
317
- )
318
- )
319
- true
320
- }
321
- is String -> {
322
- onMarkerPress?.invoke(
323
- MarkerPressEvent(
324
- id = userData,
325
- coordinate =
326
- Coordinate(
327
- marker.position.latitude,
328
- marker.position.longitude
329
- )
330
- )
331
- )
332
- false
333
- }
334
- else -> false
335
- }
336
- }
337
-
338
- map.setOnMarkerDragListener(
339
- object : GoogleMap.OnMarkerDragListener {
340
- override fun onMarkerDragStart(marker: Marker) {
341
- val id = marker.tag as? String ?: return
342
- onMarkerDragStart?.invoke(
343
- MarkerDragEvent(
344
- id = id,
345
- coordinate =
346
- Coordinate(
347
- marker.position.latitude,
348
- marker.position.longitude
349
- )
350
- )
351
- )
352
- }
353
-
354
- override fun onMarkerDrag(marker: Marker) {
355
- val id = marker.tag as? String ?: return
356
- onMarkerDrag?.invoke(
357
- MarkerDragEvent(
358
- id = id,
359
- coordinate =
360
- Coordinate(
361
- marker.position.latitude,
362
- marker.position.longitude
363
- )
364
- )
365
- )
366
- }
367
-
368
- override fun onMarkerDragEnd(marker: Marker) {
369
- val id = marker.tag as? String ?: return
370
- onMarkerDragEnd?.invoke(
371
- MarkerDragEvent(
372
- id = id,
373
- coordinate =
374
- Coordinate(
375
- marker.position.latitude,
376
- marker.position.longitude
377
- )
378
- )
379
- )
380
- }
381
- }
382
- )
383
- }
384
-
385
- // MARK: - Clustering
386
-
387
- private fun updateClusterConfig() {
388
- clusterIconGenerator?.updateConfig(_clusterConfig)
389
- _clusterConfig?.let { config ->
390
- clusterEngine.setMinClusterSize(config.minimumClusterSize.toInt())
391
- clusterEngine.setMaxZoom(config.maxZoom)
392
- }
393
- performClustering()
394
- }
395
-
396
- override fun performClustering() {
397
- val map = googleMap ?: return
398
- if (_mapView.width <= 0 || _mapView.height <= 0) return
399
-
400
- val enabled = _clusterConfig?.enabled ?: true
401
- if (!enabled) {
402
- clearRenderedMarkers()
403
- renderAllMarkersIndividually()
404
- return
405
- }
406
-
407
- val bounds = map.projection.visibleRegion.latLngBounds
408
- val zoom = map.cameraPosition.zoom
409
-
410
- val result =
411
- clusterEngine.cluster(
412
- bounds = bounds,
413
- zoom = zoom,
414
- mapWidth = _mapView.width,
415
- mapHeight = _mapView.height
416
- )
417
-
418
- clearRenderedMarkers()
419
-
420
- for (clusterData in result.clusters) {
421
- renderCluster(clusterData)
422
- }
423
-
424
- for (markerPoint in result.singleMarkers) {
425
- clusterEngine.getMarkerData(markerPoint.id)?.let { renderSingleMarker(it) }
426
- }
427
- }
428
-
429
- private fun clearRenderedMarkers() {
430
- renderedClusterMarkers.forEach { it.remove() }
431
- renderedClusterMarkers.clear()
432
- renderedSingleMarkers.values.forEach { it.remove() }
433
- renderedSingleMarkers.clear()
434
- }
435
-
436
- private fun renderAllMarkersIndividually() {
437
- clusterableMarkerData.values.forEach { markerData ->
438
- createGoogleMarker(markerData)?.let { renderedSingleMarkers[markerData.id] = it }
439
- }
440
- }
441
-
442
- private fun renderCluster(clusterData: Cluster) {
443
- val map = googleMap ?: return
444
- val markerOptions =
445
- MarkerOptions()
446
- .position(LatLng(clusterData.latitude, clusterData.longitude))
447
- .anchor(0.5f, 0.5f)
448
-
449
- clusterIconGenerator?.getClusterIcon(clusterData.count)?.let { markerOptions.icon(it) }
450
-
451
- val marker = map.addMarker(markerOptions)
452
- marker?.tag = ClusterUserData(markerIds = clusterData.markerIds, count = clusterData.count)
453
- marker?.let { renderedClusterMarkers.add(it) }
454
- }
455
-
456
- private fun renderSingleMarker(markerData: MarkerData) {
457
- createGoogleMarker(markerData)?.let { renderedSingleMarkers[markerData.id] = it }
458
- }
459
-
460
- private fun createGoogleMarker(markerData: MarkerData): Marker? {
461
- val map = googleMap ?: return null
462
-
463
- val markerOptions =
464
- MarkerOptions()
465
- .position(
466
- LatLng(
467
- markerData.coordinate.latitude,
468
- markerData.coordinate.longitude
469
- )
470
- )
471
- .title(markerData.title)
472
- .snippet(markerData.description)
473
- .draggable(markerData.draggable)
474
- .alpha(markerData.opacity.toFloat())
475
- .rotation(markerData.rotation.toFloat())
476
- .zIndex(markerData.zIndex.toFloat())
477
- .anchor(markerData.anchor.x.toFloat(), markerData.anchor.y.toFloat())
478
-
479
- MarkerIconFactory.createIcon(context, markerData)?.let { markerOptions.icon(it) }
480
-
481
- val marker = map.addMarker(markerOptions)
482
- marker?.tag = markerData.id
483
- return marker
484
- }
485
-
486
- // MARK: - Marker Management
487
-
488
- override fun addMarker(marker: MarkerData) {
489
- runOnUiThread {
490
- addMarkerSync(marker)
491
- scheduleClusteringAfterLayout()
492
- }
493
- }
494
-
495
- override fun addMarkers(markers: Array<MarkerData>) {
496
- runOnUiThread {
497
- markers.forEach { addMarkerSync(it) }
498
- scheduleClusteringAfterLayout()
499
- }
500
- }
501
-
502
- private fun addMarkerSync(markerData: MarkerData) {
503
- removeMarkerSync(markerData.id)
504
-
505
- val clusterConfigEnabled = _clusterConfig?.enabled ?: true
506
- if (markerData.clusteringEnabled && clusterConfigEnabled) {
507
- clusterableMarkerData[markerData.id] = markerData
508
- clusterEngine.addMarker(markerData)
509
- } else {
510
- createGoogleMarker(markerData)?.let { nonClusteredMarkers[markerData.id] = it }
511
- }
512
- }
513
-
514
- override fun updateMarker(marker: MarkerData) {
515
- runOnUiThread {
516
- removeMarkerSync(marker.id)
517
- addMarkerSync(marker)
518
- scheduleClusteringAfterLayout()
519
- }
520
- }
521
-
522
- override fun removeMarker(id: String) {
523
- runOnUiThread {
524
- removeMarkerSync(id)
525
- scheduleClusteringAfterLayout()
526
- }
527
- }
528
-
529
- private fun removeMarkerSync(id: String) {
530
- clusterEngine.removeMarker(id)
531
- clusterableMarkerData.remove(id)
532
- renderedSingleMarkers[id]?.remove()
533
- renderedSingleMarkers.remove(id)
534
- nonClusteredMarkers[id]?.remove()
535
- nonClusteredMarkers.remove(id)
536
- }
537
-
538
- override fun selectMarker(id: String) {
539
- runOnUiThread {
540
- selectedMarkerId?.let { previousId ->
541
- if (previousId != id) updateMarkerSelectionState(previousId, false)
542
- }
543
- selectedMarkerId = id
544
- updateMarkerSelectionState(id, true)
545
- renderedSingleMarkers[id]?.showInfoWindow() ?: nonClusteredMarkers[id]?.showInfoWindow()
546
- }
547
- }
548
-
549
- private fun updateMarkerSelectionState(id: String, selected: Boolean) {
550
- val data = clusterableMarkerData[id] ?: return
551
- if (data.config.style != MarkerStyle.PRICEMARKER) return
552
- val priceConfig = data.config.priceMarker ?: return
553
-
554
- val updatedPriceConfig =
555
- PriceMarkerStyle(
556
- price = priceConfig.price,
557
- currency = priceConfig.currency,
558
- selected = selected,
559
- backgroundColor = priceConfig.backgroundColor,
560
- selectedBackgroundColor = priceConfig.selectedBackgroundColor,
561
- textColor = priceConfig.textColor,
562
- selectedTextColor = priceConfig.selectedTextColor,
563
- fontSize = priceConfig.fontSize,
564
- paddingHorizontal = priceConfig.paddingHorizontal,
565
- paddingVertical = priceConfig.paddingVertical,
566
- shadowOpacity = priceConfig.shadowOpacity
567
- )
568
-
569
- val updatedConfig =
570
- MarkerConfig(
571
- style = data.config.style,
572
- image = data.config.image,
573
- priceMarker = updatedPriceConfig,
574
-
575
- )
576
-
577
- val updatedData =
578
- MarkerData(
579
- id = data.id,
580
- coordinate = data.coordinate,
581
- title = data.title,
582
- description = data.description,
583
- draggable = data.draggable,
584
- opacity = data.opacity,
585
- rotation = data.rotation,
586
- zIndex = data.zIndex,
587
- anchor = data.anchor,
588
- clusteringEnabled = data.clusteringEnabled,
589
- config = updatedConfig,
590
- animation = data.animation
591
- )
592
-
593
- clusterableMarkerData[id] = updatedData
594
- clusterEngine.removeMarker(id)
595
- clusterEngine.addMarker(updatedData)
596
-
597
- (renderedSingleMarkers[id] ?: nonClusteredMarkers[id])?.let { marker ->
598
- MarkerIconFactory.createIcon(context, updatedData)?.let { marker.setIcon(it) }
599
- }
600
- }
601
-
602
- override fun clearMarkers() {
603
- runOnUiThread {
604
- clusterEngine.clearMarkers()
605
- clusterableMarkerData.clear()
606
- clearRenderedMarkers()
607
- nonClusteredMarkers.values.forEach { it.remove() }
608
- nonClusteredMarkers.clear()
609
- }
610
- }
611
-
612
- override fun setClusteringEnabled(enabled: Boolean) {
613
- runOnUiThread {
614
- val existing = _clusterConfig
615
- _clusterConfig =
616
- ClusterConfig(
617
- enabled = enabled,
618
- minimumClusterSize = existing?.minimumClusterSize ?: 2.0,
619
- maxZoom = existing?.maxZoom ?: 20.0,
620
- backgroundColor = existing?.backgroundColor
621
- ?: ColorUtils.defaultColorValue(0.0, 122.0, 255.0, 255.0),
622
- textColor = existing?.textColor
623
- ?: ColorUtils.defaultColorValue(255.0, 255.0, 255.0, 255.0),
624
- borderWidth = existing?.borderWidth ?: 2.0,
625
- borderColor = existing?.borderColor
626
- ?: ColorUtils.defaultColorValue(255.0, 255.0, 255.0, 255.0),
627
- animatesClusters = existing?.animatesClusters ?: true,
628
- animationDuration = existing?.animationDuration ?: 0.3,
629
- animationStyle = existing?.animationStyle
630
- ?: ClusterAnimationStyle.DEFAULT
631
- )
632
- performClustering()
633
- }
634
- }
635
-
636
- override fun refreshClusters() {
637
- runOnUiThread { performClustering() }
638
- }
639
-
640
- // MARK: - Camera Methods
641
-
642
- override fun animateToRegion(region: Region, duration: Double) {
643
- runOnUiThread {
644
- val map = googleMap ?: return@runOnUiThread
645
- val camera =
646
- CameraPosition.builder()
647
- .target(LatLng(region.latitude, region.longitude))
648
- .zoom(calculateZoom(region))
649
- .build()
650
- map.animateCamera(CameraUpdateFactory.newCameraPosition(camera), duration.toInt(), null)
651
- }
652
- }
653
-
654
- override fun animateCamera(camera: Camera, duration: Double) {
655
- runOnUiThread {
656
- val map = googleMap ?: return@runOnUiThread
657
- val cameraPosition =
658
- CameraPosition.builder()
659
- .target(LatLng(camera.center.latitude, camera.center.longitude))
660
- .zoom(camera.zoom.toFloat())
661
- .bearing(camera.heading.toFloat())
662
- .tilt(camera.pitch.toFloat())
663
- .build()
664
- map.animateCamera(
665
- CameraUpdateFactory.newCameraPosition(cameraPosition),
666
- duration.toInt(),
667
- null
668
- )
669
- }
670
- }
671
-
672
- override fun setCamera(camera: Camera) {
673
- runOnUiThread {
674
- val map = googleMap ?: return@runOnUiThread
675
- val cameraPosition =
676
- CameraPosition.builder()
677
- .target(LatLng(camera.center.latitude, camera.center.longitude))
678
- .zoom(camera.zoom.toFloat())
679
- .bearing(camera.heading.toFloat())
680
- .tilt(camera.pitch.toFloat())
681
- .build()
682
- map.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
683
- }
684
- }
685
-
686
- override fun getCamera(): Promise<Camera> = Promise.resolved(getCurrentCamera())
687
-
688
- override fun fitToCoordinates(
689
- coordinates: Array<Coordinate>,
690
- padding: EdgePadding,
691
- animated: Boolean
692
- ) {
693
- runOnUiThread {
694
- val map = googleMap ?: return@runOnUiThread
695
-
696
- if (coordinates.size < 2) {
697
- coordinates.firstOrNull()?.let {
698
- animateToRegion(
699
- Region(it.latitude, it.longitude, 0.05, 0.05),
700
- if (animated) 300.0 else 0.0
701
- )
702
- }
703
- return@runOnUiThread
704
- }
705
-
706
- val boundsBuilder = LatLngBounds.Builder()
707
- coordinates.forEach { boundsBuilder.include(LatLng(it.latitude, it.longitude)) }
708
- val bounds = boundsBuilder.build()
709
-
710
- val density = context.resources.displayMetrics.density
711
- val paddingPx = (padding.top * density).toInt()
712
- val cameraUpdate = CameraUpdateFactory.newLatLngBounds(bounds, paddingPx)
713
-
714
- if (animated) map.animateCamera(cameraUpdate) else map.moveCamera(cameraUpdate)
715
- }
716
- }
717
-
718
- override fun getMapBoundaries(): Promise<MapBoundaries> {
719
- val promise = Promise<MapBoundaries>()
720
- runOnUiThread {
721
- try {
722
- val map = googleMap
723
- if (map == null) {
724
- promise.resolve(
725
- MapBoundaries(
726
- northEast = Coordinate(0.0, 0.0),
727
- southWest = Coordinate(0.0, 0.0)
728
- )
729
- )
730
- return@runOnUiThread
731
- }
732
-
733
- val bounds = map.projection.visibleRegion.latLngBounds
734
- promise.resolve(
735
- MapBoundaries(
736
- northEast = Coordinate(bounds.northeast.latitude, bounds.northeast.longitude),
737
- southWest = Coordinate(bounds.southwest.latitude, bounds.southwest.longitude)
738
- )
739
- )
740
- } catch (e: Exception) {
741
- promise.reject(e)
742
- }
743
- }
744
- return promise
745
- }
746
-
747
- override fun getCurrentRegion(): Region {
748
- val map = googleMap ?: return Region(0.0, 0.0, 0.0, 0.0)
749
- val cam = map.cameraPosition
750
- val bounds = map.projection.visibleRegion.latLngBounds
751
- return Region(
752
- latitude = cam.target.latitude,
753
- longitude = cam.target.longitude,
754
- latitudeDelta = bounds.northeast.latitude - bounds.southwest.latitude,
755
- longitudeDelta = bounds.northeast.longitude - bounds.southwest.longitude
756
- )
757
- }
758
-
759
- // MARK: - Styling
760
-
761
- override fun setMapStyle(style: Array<MapStyleElement>?) {
762
- runOnUiThread {
763
- _customMapStyle = style
764
- applyMapStyle()
765
- }
766
- }
767
-
768
- override fun setIsDarkMode(enabled: Boolean) {
769
- runOnUiThread {
770
- _darkMode = enabled
771
- applyDarkModeStyle()
772
- }
773
- }
774
-
775
- private fun applyMapStyle() {
776
- val map = googleMap ?: return
777
- val styleElements = _customMapStyle
778
-
779
- if (styleElements == null || styleElements.isEmpty()) {
780
- map.setMapStyle(null)
781
- return
782
- }
783
-
784
- try {
785
- val jsonArray = JSONArray()
786
- for (element in styleElements) {
787
- val jsonObject = JSONObject()
788
- element.featureType?.let { jsonObject.put("featureType", it) }
789
- element.elementType?.let { jsonObject.put("elementType", it) }
790
-
791
- val stylersArray = JSONArray()
792
- for (styler in element.stylers) {
793
- val stylerObject = JSONObject()
794
- styler.color?.let { stylerObject.put("color", it) }
795
- styler.visibility?.let { stylerObject.put("visibility", it) }
796
- styler.weight?.let { stylerObject.put("weight", it) }
797
- styler.saturation?.let { stylerObject.put("saturation", it) }
798
- styler.lightness?.let { stylerObject.put("lightness", it) }
799
- styler.gamma?.let { stylerObject.put("gamma", it) }
800
- stylersArray.put(stylerObject)
801
- }
802
- jsonObject.put("stylers", stylersArray)
803
- jsonArray.put(jsonObject)
804
- }
805
-
806
- map.setMapStyle(MapStyleOptions(jsonArray.toString()))
807
- } catch (e: Exception) {
808
- e.printStackTrace()
809
- }
810
- }
811
-
812
- private fun applyDarkModeStyle() {
813
- val map = googleMap ?: return
814
- if (_customMapStyle != null && _customMapStyle!!.isNotEmpty()) return
815
-
816
- if (_darkMode == true) {
817
- try {
818
- val darkStyleJson =
819
- """[{"elementType":"geometry","stylers":[{"color":"#242f3e"}]},{"elementType":"labels.text.fill","stylers":[{"color":"#746855"}]},{"elementType":"labels.text.stroke","stylers":[{"color":"#242f3e"}]},{"featureType":"water","elementType":"geometry","stylers":[{"color":"#17263c"}]}]"""
820
- runOnUiThread { map.setMapStyle(MapStyleOptions(darkStyleJson)) }
821
- } catch (e: Exception) {
822
- e.printStackTrace()
823
- }
824
- } else {
825
- map.setMapStyle(null)
826
- }
827
- }
828
-
829
- // MARK: - Lifecycle
830
-
831
- override fun onResume() {
832
- _mapView.onResume()
833
- }
834
- override fun onPause() {
835
- _mapView.onPause()
836
- }
837
- override fun onDestroy() {
838
- _mapView.onDestroy()
839
- }
840
-
841
- // MARK: - Helpers
842
-
843
- private fun runOnUiThread(action: () -> Unit) {
844
- if (Looper.myLooper() == Looper.getMainLooper()) action() else mainHandler.post(action)
845
- }
846
-
847
- private fun scheduleClusteringAfterLayout() {
848
- if (hasLayout && _mapView.width > 0 && _mapView.height > 0) performClustering()
849
- else needsClusteringAfterLayout = true
850
- }
851
-
852
- private fun updateCameraToInitialRegion(region: Region) {
853
- val map = googleMap ?: return
854
- val camera =
855
- CameraPosition.builder()
856
- .target(LatLng(region.latitude, region.longitude))
857
- .zoom(calculateZoom(region))
858
- .build()
859
- map.moveCamera(CameraUpdateFactory.newCameraPosition(camera))
860
- }
861
-
862
- private fun calculateZoom(region: Region): Float {
863
- val delta = max(region.latitudeDelta, region.longitudeDelta)
864
- return when {
865
- delta < 0.01 -> 16f
866
- delta < 0.05 -> 14f
867
- delta < 0.1 -> 12f
868
- delta < 0.5 -> 10f
869
- delta < 1.0 -> 8f
870
- delta < 5.0 -> 6f
871
- else -> 4f
872
- }
873
- }
874
-
875
- private fun convertMapType(type: MapType?): Int =
876
- when (type) {
877
- MapType.SATELLITE -> GoogleMap.MAP_TYPE_SATELLITE
878
- MapType.HYBRID -> GoogleMap.MAP_TYPE_HYBRID
879
- MapType.STANDARD, null -> GoogleMap.MAP_TYPE_NORMAL
880
- }
881
-
882
- private fun getCurrentCamera(): Camera {
883
- val map =
884
- googleMap
885
- ?: return Camera(
886
- center = Coordinate(0.0, 0.0),
887
- pitch = 0.0,
888
- heading = 0.0,
889
- altitude = 0.0,
890
- zoom = 0.0
891
- )
892
- val cam = map.cameraPosition
893
- return Camera(
894
- center = Coordinate(cam.target.latitude, cam.target.longitude),
895
- pitch = cam.tilt.toDouble(),
896
- heading = cam.bearing.toDouble(),
897
- altitude = cam.zoom.toDouble() * 1000,
898
- zoom = cam.zoom.toDouble()
899
- )
900
- }
901
-
902
- private fun hasLocationPermission(): Boolean {
903
- return ContextCompat.checkSelfPermission(
904
- context,
905
- Manifest.permission.ACCESS_FINE_LOCATION
906
- ) == PackageManager.PERMISSION_GRANTED ||
907
- ContextCompat.checkSelfPermission(
908
- context,
909
- Manifest.permission.ACCESS_COARSE_LOCATION
910
- ) == PackageManager.PERMISSION_GRANTED
911
- }
912
- }