@maydon_tech/react-native-nitro-maps 0.1.4 → 0.2.0

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 (217) 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/gradle.properties +4 -4
  6. package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
  7. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
  8. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
  9. package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
  10. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
  11. package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
  12. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
  13. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
  14. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
  15. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
  16. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
  17. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
  18. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
  19. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
  20. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
  21. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
  22. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
  23. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
  24. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
  25. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
  26. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
  27. package/ios/Location/NitroLocationManager.swift +116 -0
  28. package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
  29. package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
  30. package/ios/NitroMap.swift +279 -13
  31. package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
  32. package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
  33. package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
  34. package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
  35. package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
  36. package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
  37. package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
  38. package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
  39. package/ios/Providers/MapProviderFactory.swift +17 -0
  40. package/ios/Providers/MapProviderProtocol.swift +48 -1
  41. package/ios/Shared/ClusterConfig+Factory.swift +2 -2
  42. package/ios/Shared/MapStyleProvider.swift +6 -4
  43. package/ios/Shared/MarkerSelectionHandler.swift +4 -1
  44. package/ios/Utils/ColorValueExtension.swift +46 -67
  45. package/lib/module/components/ImageMarker.js +39 -29
  46. package/lib/module/components/ImageMarker.js.map +1 -1
  47. package/lib/module/components/Marker.js +118 -0
  48. package/lib/module/components/Marker.js.map +1 -0
  49. package/lib/module/components/NitroCircle.js +92 -0
  50. package/lib/module/components/NitroCircle.js.map +1 -0
  51. package/lib/module/components/NitroMap.js +216 -76
  52. package/lib/module/components/NitroMap.js.map +1 -1
  53. package/lib/module/components/NitroPolygon.js +135 -0
  54. package/lib/module/components/NitroPolygon.js.map +1 -0
  55. package/lib/module/components/NitroPolyline.js +115 -0
  56. package/lib/module/components/NitroPolyline.js.map +1 -0
  57. package/lib/module/components/PriceMarker.js +16 -29
  58. package/lib/module/components/PriceMarker.js.map +1 -1
  59. package/lib/module/context/NitroMapContext.js.map +1 -1
  60. package/lib/module/hooks/useNitroCircle.js +18 -0
  61. package/lib/module/hooks/useNitroCircle.js.map +1 -0
  62. package/lib/module/hooks/useNitroMarker.js +26 -9
  63. package/lib/module/hooks/useNitroMarker.js.map +1 -1
  64. package/lib/module/hooks/useNitroOverlay.js +59 -0
  65. package/lib/module/hooks/useNitroOverlay.js.map +1 -0
  66. package/lib/module/hooks/useNitroPolygon.js +18 -0
  67. package/lib/module/hooks/useNitroPolygon.js.map +1 -0
  68. package/lib/module/hooks/useNitroPolyline.js +18 -0
  69. package/lib/module/hooks/useNitroPolyline.js.map +1 -0
  70. package/lib/module/index.js +5 -0
  71. package/lib/module/index.js.map +1 -1
  72. package/lib/module/types/overlay.js +4 -0
  73. package/lib/module/types/overlay.js.map +1 -0
  74. package/lib/module/types/theme.js +4 -0
  75. package/lib/module/types/theme.js.map +1 -0
  76. package/lib/module/utils/colors.js +41 -13
  77. package/lib/module/utils/colors.js.map +1 -1
  78. package/lib/module/utils/validation.js +45 -0
  79. package/lib/module/utils/validation.js.map +1 -0
  80. package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
  81. package/lib/typescript/src/components/Marker.d.ts +34 -0
  82. package/lib/typescript/src/components/Marker.d.ts.map +1 -0
  83. package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
  84. package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
  85. package/lib/typescript/src/components/NitroMap.d.ts +60 -3
  86. package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
  87. package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
  88. package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
  89. package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
  90. package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
  91. package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
  92. package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
  93. package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
  94. package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
  95. package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
  96. package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
  97. package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
  98. package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
  100. package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
  101. package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
  102. package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
  103. package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
  104. package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
  105. package/lib/typescript/src/index.d.ts +15 -2
  106. package/lib/typescript/src/index.d.ts.map +1 -1
  107. package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
  108. package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
  109. package/lib/typescript/src/types/map.d.ts +34 -4
  110. package/lib/typescript/src/types/map.d.ts.map +1 -1
  111. package/lib/typescript/src/types/marker.d.ts +24 -36
  112. package/lib/typescript/src/types/marker.d.ts.map +1 -1
  113. package/lib/typescript/src/types/overlay.d.ts +75 -0
  114. package/lib/typescript/src/types/overlay.d.ts.map +1 -0
  115. package/lib/typescript/src/types/theme.d.ts +93 -0
  116. package/lib/typescript/src/types/theme.d.ts.map +1 -0
  117. package/lib/typescript/src/utils/colors.d.ts +6 -8
  118. package/lib/typescript/src/utils/colors.d.ts.map +1 -1
  119. package/lib/typescript/src/utils/validation.d.ts +12 -0
  120. package/lib/typescript/src/utils/validation.d.ts.map +1 -0
  121. package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
  122. package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
  123. package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
  124. package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
  125. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  126. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +328 -21
  127. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
  128. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
  129. package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
  130. package/nitrogen/generated/android/c++/JPolygonData.hpp +149 -0
  131. package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
  132. package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
  133. package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
  134. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
  135. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
  136. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
  147. package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
  148. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
  149. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +175 -17
  150. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +15 -3
  151. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +249 -16
  152. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
  153. package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
  154. package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
  155. package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
  156. package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
  157. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  158. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
  159. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
  160. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
  161. package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
  162. package/nitrogen/generated/ios/swift/PolygonData.swift +179 -0
  163. package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
  164. package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
  165. package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
  166. package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
  167. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
  168. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
  169. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
  170. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
  171. package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
  172. package/nitrogen/generated/shared/c++/PolygonData.hpp +114 -0
  173. package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
  174. package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
  175. package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
  176. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
  177. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
  178. package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
  179. package/package.json +36 -5
  180. package/src/components/ImageMarker.tsx +58 -42
  181. package/src/components/Marker.tsx +161 -0
  182. package/src/components/NitroCircle.tsx +183 -0
  183. package/src/components/NitroMap.tsx +328 -78
  184. package/src/components/NitroPolygon.tsx +229 -0
  185. package/src/components/NitroPolyline.tsx +208 -0
  186. package/src/components/PriceMarker.tsx +23 -48
  187. package/src/context/NitroMapContext.tsx +4 -0
  188. package/src/hooks/useNitroCircle.ts +25 -0
  189. package/src/hooks/useNitroMarker.ts +49 -10
  190. package/src/hooks/useNitroOverlay.ts +68 -0
  191. package/src/hooks/useNitroPolygon.ts +25 -0
  192. package/src/hooks/useNitroPolyline.ts +25 -0
  193. package/src/index.tsx +23 -2
  194. package/src/specs/NitroMap.nitro.ts +294 -5
  195. package/src/types/map.ts +36 -4
  196. package/src/types/marker.ts +24 -44
  197. package/src/types/overlay.ts +77 -0
  198. package/src/types/theme.ts +101 -0
  199. package/src/utils/colors.ts +48 -16
  200. package/src/utils/validation.ts +69 -0
  201. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
  202. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
  203. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
  204. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
  205. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
  206. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
  207. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
  208. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
  209. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
  210. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
  211. package/cpp/QuadTree.hpp +0 -246
  212. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
  213. package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
  214. package/ios/Providers/GoogleMapProvider.swift +0 -924
  215. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
  216. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
  217. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
@@ -5,6 +5,13 @@ import GoogleMapsUtils
5
5
  /// GMSMapViewDelegate implementation for GoogleMapProvider
6
6
  class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
7
7
 
8
+ // MARK: - Overlay UserData Keys
9
+ static let overlayTypeKey = "type"
10
+ static let overlayIdKey = "id"
11
+ static let overlayTypePolyline = "polyline"
12
+ static let overlayTypePolygon = "polygon"
13
+ static let overlayTypeCircle = "circle"
14
+
8
15
  weak var provider: GoogleMapProvider?
9
16
  private var isGesture: Bool = false
10
17
  private var lastGestureWasZoom: Bool = false
@@ -171,9 +178,13 @@ class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
171
178
  func mapView(_ mapView: GMSMapView, willMove gesture: Bool) {
172
179
  isGesture = gesture
173
180
  zoomAtWillMove = mapView.camera.zoom
174
- #if DEBUG
175
- NSLog("[GestureDebug] willMove gesture=%d zoom=%.4f", gesture, mapView.camera.zoom)
176
- #endif
181
+
182
+ // Auto-break follow/heading mode when user interacts with the map
183
+ if gesture, let provider = provider,
184
+ let mode = provider.userTrackingMode, mode != .none {
185
+ provider.userTrackingMode = .none
186
+ provider.onUserTrackingModeChange?(.none)
187
+ }
177
188
  }
178
189
 
179
190
  func mapView(_ mapView: GMSMapView, didChange position: GMSCameraPosition) {
@@ -199,25 +210,6 @@ class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
199
210
  func mapView(_ mapView: GMSMapView, idleAt position: GMSCameraPosition) {
200
211
  guard let provider = provider else { return }
201
212
 
202
- #if DEBUG
203
- let gestureType = lastGestureWasZoom ? "ZOOM" : "PAN"
204
- NSLog("[GestureDebug] idleAt zoom=%.4f gestureType=%@ cameraZoom=%.4f",
205
- position.zoom, gestureType, mapView.camera.zoom)
206
-
207
- // Schedule a check to compare zoom at idle vs zoom when clustering fires
208
- let idleZoom = mapView.camera.zoom
209
- DispatchQueue.main.asyncAfter(deadline: .now() + 0.11) {
210
- let clusterZoom = mapView.camera.zoom
211
- if abs(idleZoom - clusterZoom) > 0.001 {
212
- NSLog("[GestureDebug] ⚠️ ZOOM DRIFT: idle=%.4f clustering=%.4f delta=%.4f",
213
- idleZoom, clusterZoom, clusterZoom - idleZoom)
214
- } else {
215
- NSLog("[GestureDebug] ✅ Zoom stable: idle=%.4f clustering=%.4f",
216
- idleZoom, clusterZoom)
217
- }
218
- }
219
- #endif
220
-
221
213
  let event = RegionChangeEvent(
222
214
  region: provider.getCurrentRegion(),
223
215
  isGesture: isGesture
@@ -226,6 +218,28 @@ class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
226
218
  isGesture = false
227
219
  lastGestureWasZoom = false
228
220
  provider.performClustering()
221
+ provider.refreshDashedPolylines()
222
+ provider.onMapIdle?()
223
+ }
224
+
225
+ // MARK: - Overlay Tap Handler
226
+
227
+ func mapView(_ mapView: GMSMapView, didTap overlay: GMSOverlay) {
228
+ guard let provider = provider,
229
+ let userData = overlay.userData as? [String: String],
230
+ let type = userData[Self.overlayTypeKey],
231
+ let id = userData[Self.overlayIdKey] else { return }
232
+
233
+ switch type {
234
+ case Self.overlayTypePolyline:
235
+ provider.onPolylinePress?(id)
236
+ case Self.overlayTypePolygon:
237
+ provider.onPolygonPress?(id)
238
+ case Self.overlayTypeCircle:
239
+ provider.onCirclePress?(id)
240
+ default:
241
+ break
242
+ }
229
243
  }
230
244
 
231
245
  // MARK: - Marker Methods
@@ -247,7 +261,9 @@ class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
247
261
  )
248
262
  return true
249
263
  }
250
- // Regular marker
264
+ // Regular marker — fire callback and return true to prevent
265
+ // the default info window. Consumers use selectMarker() to
266
+ // show info windows programmatically when desired.
251
267
  if let markerId = marker.userData as? String {
252
268
  provider.onMarkerPress?(
253
269
  MarkerPressEvent(
@@ -307,4 +323,13 @@ class GoogleMapDelegate: NSObject, GMSMapViewDelegate {
307
323
  // FIX #10: Removed dead NitroClusterItem check — only String IDs are used.
308
324
  return marker.userData as? String
309
325
  }
326
+
327
+ // MARK: - Info Window
328
+
329
+ func mapView(_ mapView: GMSMapView, didCloseInfoWindowOf marker: GMSMarker) {
330
+ // SDK auto-dismisses the info window when user taps elsewhere.
331
+ // didTapAt may NOT fire for that tap — SDK consumes it.
332
+ // This is the correct place to revert the marker's selection state.
333
+ provider?.deselectCurrentMarker()
334
+ }
310
335
  }
@@ -0,0 +1,180 @@
1
+ import Foundation
2
+ import GoogleMaps
3
+
4
+ // MARK: - Camera Methods (SRP: Camera control only)
5
+
6
+ extension GoogleMapProvider {
7
+
8
+ func animateToRegion(_ region: Region, duration: Double?) {
9
+ let camera = GMSCameraPosition.camera(
10
+ withLatitude: region.latitude,
11
+ longitude: region.longitude,
12
+ zoom: calculateZoom(for: region)
13
+ )
14
+
15
+ let durationSeconds = (duration ?? 300) / 1000.0
16
+
17
+ CATransaction.begin()
18
+ CATransaction.setAnimationDuration(durationSeconds)
19
+ gmsMapView.animate(to: camera)
20
+ CATransaction.commit()
21
+ }
22
+
23
+ func fitToCoordinates(
24
+ _ coordinates: [Coordinate],
25
+ edgePadding: EdgePadding?,
26
+ animated: Bool?
27
+ ) {
28
+ guard coordinates.count >= 2 else {
29
+ if let first = coordinates.first {
30
+ animateToRegion(
31
+ Region(
32
+ latitude: first.latitude,
33
+ longitude: first.longitude,
34
+ latitudeDelta: 0.05,
35
+ longitudeDelta: 0.05
36
+ ),
37
+ duration: animated ?? true ? 300 : 0
38
+ )
39
+ }
40
+ return
41
+ }
42
+
43
+ let clCoordinates = coordinates.map {
44
+ CLLocationCoordinate2D(latitude: $0.latitude, longitude: $0.longitude)
45
+ }
46
+
47
+ var bounds = GMSCoordinateBounds(
48
+ coordinate: clCoordinates[0],
49
+ coordinate: clCoordinates[1]
50
+ )
51
+
52
+ for coord in clCoordinates.dropFirst(2) {
53
+ bounds = bounds.includingCoordinate(coord)
54
+ }
55
+
56
+ guard bounds.isValid else { return }
57
+
58
+ let padding = UIEdgeInsets(
59
+ top: CGFloat(edgePadding?.top ?? 50),
60
+ left: CGFloat(edgePadding?.left ?? 50),
61
+ bottom: CGFloat(edgePadding?.bottom ?? 50),
62
+ right: CGFloat(edgePadding?.right ?? 50)
63
+ )
64
+
65
+ let cameraUpdate = GMSCameraUpdate.fit(bounds, with: padding)
66
+
67
+ if animated ?? true {
68
+ gmsMapView.animate(with: cameraUpdate)
69
+ } else {
70
+ gmsMapView.moveCamera(cameraUpdate)
71
+ }
72
+ }
73
+
74
+ func fitToSuppliedMarkers(
75
+ _ markerIds: [String],
76
+ edgePadding: EdgePadding?,
77
+ animated: Bool?
78
+ ) {
79
+ let coordinates = markerIds.compactMap { id -> Coordinate? in
80
+ guard let data = clusterableMarkerData[id] else { return nil }
81
+ return Coordinate(latitude: data.coordinate.latitude, longitude: data.coordinate.longitude)
82
+ }
83
+ guard !coordinates.isEmpty else { return }
84
+ fitToCoordinates(coordinates, edgePadding: edgePadding, animated: animated)
85
+ }
86
+
87
+ func animateCamera(_ camera: Camera, duration: Double?) {
88
+ let gmsCamera = GMSCameraPosition.camera(
89
+ withLatitude: camera.center.latitude,
90
+ longitude: camera.center.longitude,
91
+ zoom: Float(camera.zoom),
92
+ bearing: camera.heading,
93
+ viewingAngle: camera.pitch
94
+ )
95
+
96
+ let durationSeconds = (duration ?? 300) / 1000.0
97
+
98
+ CATransaction.begin()
99
+ CATransaction.setAnimationDuration(durationSeconds)
100
+ gmsMapView.animate(to: gmsCamera)
101
+ CATransaction.commit()
102
+ }
103
+
104
+ func setCamera(_ camera: Camera) {
105
+ let gmsCamera = GMSCameraPosition.camera(
106
+ withLatitude: camera.center.latitude,
107
+ longitude: camera.center.longitude,
108
+ zoom: Float(camera.zoom),
109
+ bearing: camera.heading,
110
+ viewingAngle: camera.pitch
111
+ )
112
+ gmsMapView.camera = gmsCamera
113
+ }
114
+
115
+ func getCamera() -> Camera {
116
+ let cam = gmsMapView.camera
117
+ return Camera(
118
+ center: Coordinate(
119
+ latitude: cam.target.latitude,
120
+ longitude: cam.target.longitude
121
+ ),
122
+ pitch: cam.viewingAngle,
123
+ heading: cam.bearing,
124
+ altitude: MapStyleProvider.shared.altitudeFromZoom(Double(cam.zoom)),
125
+ zoom: Double(cam.zoom)
126
+ )
127
+ }
128
+
129
+ func getMapBoundaries() -> MapBoundaries? {
130
+ guard gmsMapView.frame.size.width > 0,
131
+ gmsMapView.frame.size.height > 0
132
+ else {
133
+ return nil
134
+ }
135
+
136
+ let visibleRegion = gmsMapView.projection.visibleRegion()
137
+ let bounds = GMSCoordinateBounds(region: visibleRegion)
138
+
139
+ guard bounds.isValid else { return nil }
140
+
141
+ return MapBoundaries(
142
+ northEast: Coordinate(
143
+ latitude: bounds.northEast.latitude,
144
+ longitude: bounds.northEast.longitude
145
+ ),
146
+ southWest: Coordinate(
147
+ latitude: bounds.southWest.latitude,
148
+ longitude: bounds.southWest.longitude
149
+ )
150
+ )
151
+ }
152
+
153
+ func getCurrentRegion() -> Region {
154
+ let cam = gmsMapView.camera
155
+ let visibleRegion = gmsMapView.projection.visibleRegion()
156
+ let bounds = GMSCoordinateBounds(region: visibleRegion)
157
+
158
+ return Region(
159
+ latitude: cam.target.latitude,
160
+ longitude: cam.target.longitude,
161
+ latitudeDelta: bounds.northEast.latitude - bounds.southWest.latitude,
162
+ longitudeDelta: bounds.northEast.longitude - bounds.southWest.longitude
163
+ )
164
+ }
165
+
166
+ func pointForCoordinate(_ coordinate: Coordinate) -> Point {
167
+ let clCoord = CLLocationCoordinate2D(
168
+ latitude: coordinate.latitude,
169
+ longitude: coordinate.longitude
170
+ )
171
+ let cgPoint = gmsMapView.projection.point(for: clCoord)
172
+ return Point(x: Double(cgPoint.x), y: Double(cgPoint.y))
173
+ }
174
+
175
+ func coordinateForPoint(_ point: Point) -> Coordinate {
176
+ let cgPoint = CGPoint(x: point.x, y: point.y)
177
+ let clCoord = gmsMapView.projection.coordinate(for: cgPoint)
178
+ return Coordinate(latitude: clCoord.latitude, longitude: clCoord.longitude)
179
+ }
180
+ }