@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
@@ -0,0 +1,19 @@
1
+ package com.margelo.nitro.nitromap.providers.google
2
+
3
+ /**
4
+ * Type-safe marker tag data. Replaces loose `Any?` tag union
5
+ * (String | ClusterUserData | "__user_location__") with exhaustive
6
+ * sealed class for compile-time safety.
7
+ *
8
+ * Set on `Marker.tag` and matched with `when` in GoogleMapDelegate.
9
+ */
10
+ sealed class MarkerTagData {
11
+ /** Regular user-added marker with its ID. */
12
+ data class Regular(val markerId: String) : MarkerTagData()
13
+
14
+ /** Cluster icon representing multiple markers. */
15
+ data class Cluster(val markerIds: List<String>, val count: Int) : MarkerTagData()
16
+
17
+ /** Custom user location marker. */
18
+ data object UserLocation : MarkerTagData()
19
+ }
@@ -0,0 +1,116 @@
1
+ import CoreLocation
2
+ import Foundation
3
+
4
+ /// Provider-agnostic location manager that wraps CLLocationManager.
5
+ /// Lives at the NitroMap facade level, not tied to any specific map SDK.
6
+ /// Provides location updates + compass heading for custom user location markers.
7
+ class NitroLocationManager: NSObject, CLLocationManagerDelegate {
8
+
9
+ // MARK: - Properties
10
+
11
+ private let locationManager = CLLocationManager()
12
+ private var isRunning = false
13
+
14
+ var onLocationUpdate: ((_ location: CLLocation, _ heading: CLHeading?) -> Void)?
15
+
16
+ /// Called when location services fail
17
+ var onLocationError: ((_ code: String, _ message: String) -> Void)?
18
+
19
+ /// Latest heading (kept for marker rotation)
20
+ private(set) var latestHeading: CLHeading?
21
+ /// Latest location
22
+ private(set) var latestLocation: CLLocation?
23
+
24
+ // MARK: - Init
25
+
26
+ override init() {
27
+ super.init()
28
+ locationManager.delegate = self
29
+ locationManager.desiredAccuracy = kCLLocationAccuracyBest
30
+ locationManager.distanceFilter = 1 // Update every 1 meter
31
+ locationManager.headingFilter = 1 // Update every 1 degree
32
+ }
33
+
34
+ // MARK: - Public API
35
+
36
+ func startUpdating() {
37
+ guard !isRunning else { return }
38
+ isRunning = true
39
+
40
+ // Request permission if not yet determined
41
+ let status = locationManager.authorizationStatus
42
+ if status == .notDetermined {
43
+ locationManager.requestWhenInUseAuthorization()
44
+ }
45
+
46
+ locationManager.startUpdatingLocation()
47
+ locationManager.startUpdatingHeading()
48
+ }
49
+
50
+ func stopUpdating() {
51
+ guard isRunning else { return }
52
+ isRunning = false
53
+ locationManager.stopUpdatingLocation()
54
+ locationManager.stopUpdatingHeading()
55
+ latestLocation = nil
56
+ latestHeading = nil
57
+ }
58
+
59
+ // MARK: - CLLocationManagerDelegate
60
+
61
+ func locationManager(
62
+ _ manager: CLLocationManager,
63
+ didUpdateLocations locations: [CLLocation]
64
+ ) {
65
+ guard let location = locations.last else { return }
66
+ latestLocation = location
67
+ onLocationUpdate?(location, latestHeading)
68
+ }
69
+
70
+ func locationManager(
71
+ _ manager: CLLocationManager,
72
+ didUpdateHeading newHeading: CLHeading
73
+ ) {
74
+ latestHeading = newHeading
75
+ // Fire update with latest location + new heading
76
+ if let location = latestLocation {
77
+ onLocationUpdate?(location, newHeading)
78
+ }
79
+ }
80
+
81
+ func locationManager(
82
+ _ manager: CLLocationManager,
83
+ didChangeAuthorization status: CLAuthorizationStatus
84
+ ) {
85
+ // If permission just granted, start updating
86
+ if isRunning && (status == .authorizedWhenInUse || status == .authorizedAlways) {
87
+ locationManager.startUpdatingLocation()
88
+ locationManager.startUpdatingHeading()
89
+ }
90
+ }
91
+
92
+ func locationManager(
93
+ _ manager: CLLocationManager,
94
+ didFailWithError error: Error
95
+ ) {
96
+ let code: String
97
+ if let clError = error as? CLError {
98
+ switch clError.code {
99
+ case .denied:
100
+ code = "LOCATION_PERMISSION_DENIED"
101
+ case .locationUnknown:
102
+ code = "LOCATION_UNKNOWN"
103
+ case .network:
104
+ code = "LOCATION_NETWORK_ERROR"
105
+ default:
106
+ code = "LOCATION_ERROR"
107
+ }
108
+ } else {
109
+ code = "LOCATION_ERROR"
110
+ }
111
+ onLocationError?(code, error.localizedDescription)
112
+ #if DEBUG
113
+ NSLog("[NitroLocationManager] Error: %@", error.localizedDescription)
114
+ #endif
115
+ }
116
+ }
@@ -33,8 +33,6 @@ final class MarkerIconCache {
33
33
  }
34
34
 
35
35
  @objc private func handleMemoryWarning() {
36
- // Clear half the cache on memory warning instead of all
37
- // This keeps frequently used icons while freeing memory
38
36
  #if DEBUG
39
37
  print("[MarkerIconCache] Memory warning received, clearing caches")
40
38
  #endif
@@ -255,7 +253,7 @@ class MarkerIconFactory {
255
253
 
256
254
  // Post notification for markers to refresh their icons
257
255
  NotificationCenter.default.post(
258
- name: Notification.Name("MarkerIconLoaded"),
256
+ name: GoogleMapProvider.markerIconLoadedNotification,
259
257
  object: nil,
260
258
  userInfo: ["cacheKey": cacheKey, "icon": finalIcon]
261
259
  )
@@ -33,16 +33,20 @@ class PriceMarkerRenderer {
33
33
  )
34
34
  }
35
35
 
36
+ let bg = PriceMarkerRenderer.variantToUIColor(config.backgroundColor) ?? .white
37
+ let text = PriceMarkerRenderer.variantToUIColor(config.textColor)
38
+ ?? UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1)
39
+
36
40
  return Style(
37
41
  price: config.price,
38
42
  currency: config.currency,
39
43
  selected: config.selected,
40
- backgroundColor: PriceMarkerRenderer.variantToUIColor(config.backgroundColor) ?? .white,
41
- selectedBackgroundColor: PriceMarkerRenderer.variantToUIColor(config.selectedBackgroundColor)
42
- ?? UIColor(red: 255/255, green: 59/255, blue: 48/255, alpha: 1),
43
- textColor: PriceMarkerRenderer.variantToUIColor(config.textColor)
44
- ?? UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1),
45
- selectedTextColor: PriceMarkerRenderer.variantToUIColor(config.selectedTextColor) ?? .white,
44
+ backgroundColor: bg,
45
+ // Fall back to normal colors when consumer doesn't provide selection colors.
46
+ // This way selectMarker() only shows the info window without changing the icon.
47
+ selectedBackgroundColor: PriceMarkerRenderer.variantToUIColor(config.selectedBackgroundColor) ?? bg,
48
+ textColor: text,
49
+ selectedTextColor: PriceMarkerRenderer.variantToUIColor(config.selectedTextColor) ?? text,
46
50
  fontSize: CGFloat(config.fontSize ?? 10),
47
51
  paddingHorizontal: CGFloat(config.paddingHorizontal ?? 8),
48
52
  paddingVertical: CGFloat(config.paddingVertical ?? 6),
@@ -1,8 +1,9 @@
1
1
  import Foundation
2
2
  import NitroModules
3
3
  import UIKit
4
+ import CoreLocation
4
5
 
5
- /// HybridNitroMap - Google Maps powered map view.
6
+ /// HybridNitroMap - Provider-agnostic map view facade.
6
7
  ///
7
8
  /// Threading contract:
8
9
  /// - All UI/SDK operations are dispatched to main thread HERE.
@@ -23,11 +24,11 @@ class HybridNitroMap: HybridNitroMapSpec {
23
24
  /// The provider is created lazily on first `afterUpdate()` or first
24
25
  /// imperative method call. This avoids premature init before the view
25
26
  /// has valid bounds.
26
- private var _provider: GoogleMapProvider?
27
+ private var _provider: MapProviderProtocol?
27
28
 
28
29
  /// Returns the provider, initializing it if needed.
29
30
  /// Must be called from the main thread.
30
- private var mapProvider: GoogleMapProvider {
31
+ private var mapProvider: MapProviderProtocol {
31
32
  if _provider == nil {
32
33
  initializeProvider()
33
34
  }
@@ -35,7 +36,7 @@ class HybridNitroMap: HybridNitroMapSpec {
35
36
  }
36
37
 
37
38
  private func initializeProvider() {
38
- let provider = GoogleMapProvider()
39
+ let provider = MapProviderFactory.create(for: self.provider ?? .google)
39
40
  provider.setup()
40
41
 
41
42
  let providerView = provider.mapView
@@ -47,18 +48,34 @@ class HybridNitroMap: HybridNitroMapSpec {
47
48
  applyCurrentSettings(to: provider)
48
49
  }
49
50
 
50
- private func applyCurrentSettings(to provider: GoogleMapProvider) {
51
- provider.initialRegion = initialRegion
51
+ /// Returns `nil` when the region is the JS sentinel (negative deltas).
52
+ private func filterSentinel(_ r: Region?) -> Region? {
53
+ guard let r = r, r.latitudeDelta >= 0, r.longitudeDelta >= 0 else { return nil }
54
+ return r
55
+ }
56
+
57
+ private func applyCurrentSettings(to provider: MapProviderProtocol) {
58
+ provider.initialRegion = filterSentinel(initialRegion)
59
+ provider.region = filterSentinel(region)
52
60
  provider.showsUserLocation = showsUserLocation
53
61
  provider.zoomEnabled = zoomEnabled
54
62
  provider.scrollEnabled = scrollEnabled
55
63
  provider.rotateEnabled = rotateEnabled
56
64
  provider.pitchEnabled = pitchEnabled
57
65
  provider.mapType = mapType
58
- provider.showsMyLocationButton = showsMyLocationButton
59
66
  provider.clusterConfig = clusterConfig
60
67
  provider.customMapStyle = customMapStyle
61
68
  provider.darkMode = darkMode
69
+ provider.mapPadding = mapPadding
70
+ provider.showsTraffic = showsTraffic
71
+ provider.showsBuildings = showsBuildings
72
+ provider.showsCompass = showsCompass
73
+ provider.minZoom = minZoom
74
+ provider.maxZoom = maxZoom
75
+ provider.userTrackingMode = userTrackingMode
76
+ provider.userLocationImage = userLocationImage
77
+ provider.userLocationSize = userLocationSize
78
+ provider.userLocationAnchor = userLocationAnchor
62
79
 
63
80
  provider.onPress = onPress
64
81
  provider.onLongPress = onLongPress
@@ -71,6 +88,55 @@ class HybridNitroMap: HybridNitroMapSpec {
71
88
  provider.onMarkerDragEnd = onMarkerDragEnd
72
89
  provider.onClusterPress = onClusterPress
73
90
  provider.onError = onError
91
+ provider.onUserTrackingModeChange = onUserTrackingModeChange
92
+ provider.onMapIdle = onMapIdle
93
+ provider.onPolylinePress = onPolylinePress
94
+ provider.onPolygonPress = onPolygonPress
95
+ provider.onCirclePress = onCirclePress
96
+
97
+ // Start location manager if needed
98
+ if showsUserLocation == true {
99
+ startLocationManager()
100
+ }
101
+ }
102
+
103
+ // MARK: - Location Manager
104
+
105
+ private lazy var locationManager: NitroLocationManager = {
106
+ let mgr = NitroLocationManager()
107
+ mgr.onLocationUpdate = { [weak self] location, heading in
108
+ self?.handleLocationUpdate(location, heading: heading)
109
+ }
110
+ mgr.onLocationError = { [weak self] code, message in
111
+ self?.onUserLocationError?(MapError(code: code, message: message))
112
+ }
113
+ return mgr
114
+ }()
115
+
116
+ private func startLocationManager() {
117
+ locationManager.startUpdating()
118
+ }
119
+
120
+ private func stopLocationManager() {
121
+ locationManager.stopUpdating()
122
+ }
123
+
124
+ private func handleLocationUpdate(_ location: CLLocation, heading: CLHeading?) {
125
+ // Forward to provider for marker/camera management
126
+ _provider?.updateUserLocation(location, heading: heading)
127
+
128
+ // Fire JS callback
129
+ let headingValue = heading?.trueHeading ?? (location.course >= 0 ? location.course : -1)
130
+ let event = UserLocationChangeEvent(
131
+ coordinate: Coordinate(
132
+ latitude: location.coordinate.latitude,
133
+ longitude: location.coordinate.longitude
134
+ ),
135
+ accuracy: location.horizontalAccuracy,
136
+ heading: headingValue,
137
+ speed: location.speed >= 0 ? location.speed : -1
138
+ )
139
+ onUserLocationChange?(event)
74
140
  }
75
141
 
76
142
  var view: UIView { return containerView }
@@ -88,11 +154,27 @@ class HybridNitroMap: HybridNitroMapSpec {
88
154
  // are pushed in bulk via applyCurrentSettings when provider inits).
89
155
 
90
156
  var initialRegion: Region? {
91
- didSet { _provider?.initialRegion = initialRegion }
157
+ didSet {
158
+ _provider?.initialRegion = filterSentinel(initialRegion)
159
+ }
160
+ }
161
+
162
+ var region: Region? {
163
+ didSet {
164
+ // Sentinel → nil (cleared); real value → forwarded as-is
165
+ _provider?.region = filterSentinel(region)
166
+ }
92
167
  }
93
168
 
94
169
  var showsUserLocation: Bool? {
95
- didSet { _provider?.showsUserLocation = showsUserLocation }
170
+ didSet {
171
+ _provider?.showsUserLocation = showsUserLocation
172
+ if showsUserLocation == true {
173
+ startLocationManager()
174
+ } else {
175
+ stopLocationManager()
176
+ }
177
+ }
96
178
  }
97
179
 
98
180
  var zoomEnabled: Bool? {
@@ -115,10 +197,6 @@ class HybridNitroMap: HybridNitroMapSpec {
115
197
  didSet { _provider?.mapType = mapType }
116
198
  }
117
199
 
118
- var showsMyLocationButton: Bool? {
119
- didSet { _provider?.showsMyLocationButton = showsMyLocationButton }
120
- }
121
-
122
200
  var clusterConfig: ClusterConfig? {
123
201
  didSet { _provider?.clusterConfig = clusterConfig }
124
202
  }
@@ -131,6 +209,46 @@ class HybridNitroMap: HybridNitroMapSpec {
131
209
  didSet { _provider?.darkMode = darkMode }
132
210
  }
133
211
 
212
+ var mapPadding: EdgePadding = EdgePadding(top: 0, right: 0, bottom: 0, left: 0) {
213
+ didSet { _provider?.mapPadding = mapPadding }
214
+ }
215
+
216
+ var showsTraffic: Bool? {
217
+ didSet { _provider?.showsTraffic = showsTraffic }
218
+ }
219
+
220
+ var showsBuildings: Bool? {
221
+ didSet { _provider?.showsBuildings = showsBuildings }
222
+ }
223
+
224
+ var showsCompass: Bool? {
225
+ didSet { _provider?.showsCompass = showsCompass }
226
+ }
227
+
228
+ var minZoom: Double = 1 {
229
+ didSet { _provider?.minZoom = minZoom }
230
+ }
231
+
232
+ var maxZoom: Double = 22 {
233
+ didSet { _provider?.maxZoom = maxZoom }
234
+ }
235
+
236
+ var userTrackingMode: UserTrackingMode? {
237
+ didSet { _provider?.userTrackingMode = userTrackingMode }
238
+ }
239
+
240
+ var userLocationImage: String = "" {
241
+ didSet { _provider?.userLocationImage = userLocationImage }
242
+ }
243
+
244
+ var userLocationSize: Double = 40 {
245
+ didSet { _provider?.userLocationSize = userLocationSize }
246
+ }
247
+
248
+ var userLocationAnchor: Point? {
249
+ didSet { _provider?.userLocationAnchor = userLocationAnchor }
250
+ }
251
+
134
252
  // MARK: - Callbacks
135
253
 
136
254
  var onPress: ((MapPressEvent) -> Void)? {
@@ -177,6 +295,30 @@ class HybridNitroMap: HybridNitroMapSpec {
177
295
  didSet { _provider?.onError = onError }
178
296
  }
179
297
 
298
+ var onUserLocationChange: ((_ event: UserLocationChangeEvent) -> Void)?
299
+
300
+ var onUserTrackingModeChange: ((_ mode: UserTrackingMode) -> Void)? {
301
+ didSet { _provider?.onUserTrackingModeChange = onUserTrackingModeChange }
302
+ }
303
+
304
+ var onMapIdle: (() -> Void)? {
305
+ didSet { _provider?.onMapIdle = onMapIdle }
306
+ }
307
+
308
+ var onPolylinePress: ((_ id: String) -> Void)? {
309
+ didSet { _provider?.onPolylinePress = onPolylinePress }
310
+ }
311
+
312
+ var onPolygonPress: ((_ id: String) -> Void)? {
313
+ didSet { _provider?.onPolygonPress = onPolygonPress }
314
+ }
315
+
316
+ var onCirclePress: ((_ id: String) -> Void)? {
317
+ didSet { _provider?.onCirclePress = onCirclePress }
318
+ }
319
+
320
+ var onUserLocationError: ((_ error: MapError) -> Void)?
321
+
180
322
  // MARK: - Initialization
181
323
 
182
324
  override init() {
@@ -216,6 +358,20 @@ class HybridNitroMap: HybridNitroMapSpec {
216
358
  }
217
359
  }
218
360
 
361
+ func fitToSuppliedMarkers(
362
+ markerIds: [String],
363
+ edgePadding: EdgePadding?,
364
+ animated: Bool?
365
+ ) throws {
366
+ DispatchQueue.main.async { [weak self] in
367
+ self?.mapProvider.fitToSuppliedMarkers(
368
+ markerIds,
369
+ edgePadding: edgePadding,
370
+ animated: animated
371
+ )
372
+ }
373
+ }
374
+
219
375
  func animateCamera(camera: Camera, duration: Double?) throws {
220
376
  DispatchQueue.main.async { [weak self] in
221
377
  self?.mapProvider.animateCamera(camera, duration: duration)
@@ -252,6 +408,32 @@ class HybridNitroMap: HybridNitroMapSpec {
252
408
  return .resolved(withResult: boundaries)
253
409
  }
254
410
 
411
+ func centerOnUserLocation() throws {
412
+ DispatchQueue.main.async { [weak self] in
413
+ self?._provider?.centerOnUserLocation()
414
+ }
415
+ }
416
+
417
+ func pointForCoordinate(coordinate: Coordinate) throws -> Point {
418
+ if Thread.isMainThread {
419
+ return mapProvider.pointForCoordinate(coordinate)
420
+ } else {
421
+ return DispatchQueue.main.sync {
422
+ return self.mapProvider.pointForCoordinate(coordinate)
423
+ }
424
+ }
425
+ }
426
+
427
+ func coordinateForPoint(point: Point) throws -> Coordinate {
428
+ if Thread.isMainThread {
429
+ return mapProvider.coordinateForPoint(point)
430
+ } else {
431
+ return DispatchQueue.main.sync {
432
+ return self.mapProvider.coordinateForPoint(point)
433
+ }
434
+ }
435
+ }
436
+
255
437
  // MARK: - Marker Methods
256
438
 
257
439
  func addMarker(marker: MarkerData) throws {
@@ -290,6 +472,12 @@ class HybridNitroMap: HybridNitroMapSpec {
290
472
  }
291
473
  }
292
474
 
475
+ func deselectMarker() {
476
+ DispatchQueue.main.async { [weak self] in
477
+ self?.mapProvider.deselectMarker()
478
+ }
479
+ }
480
+
293
481
  // MARK: - Clustering
294
482
 
295
483
  func setClusteringEnabled(enabled: Bool) throws {
@@ -329,4 +517,82 @@ class HybridNitroMap: HybridNitroMapSpec {
329
517
  func getCurrentRegion() -> Region {
330
518
  return mapProvider.getCurrentRegion()
331
519
  }
520
+
521
+ // MARK: - Polyline Methods
522
+
523
+ func addPolyline(polyline: PolylineData) throws {
524
+ DispatchQueue.main.async { [weak self] in
525
+ self?.mapProvider.addPolyline(polyline)
526
+ }
527
+ }
528
+
529
+ func updatePolyline(polyline: PolylineData) throws {
530
+ DispatchQueue.main.async { [weak self] in
531
+ self?.mapProvider.updatePolyline(polyline)
532
+ }
533
+ }
534
+
535
+ func removePolyline(id: String) throws {
536
+ DispatchQueue.main.async { [weak self] in
537
+ self?.mapProvider.removePolyline(id)
538
+ }
539
+ }
540
+
541
+ func clearPolylines() throws {
542
+ DispatchQueue.main.async { [weak self] in
543
+ self?.mapProvider.clearPolylines()
544
+ }
545
+ }
546
+
547
+ // MARK: - Polygon Methods
548
+
549
+ func addPolygon(polygon: PolygonData) throws {
550
+ DispatchQueue.main.async { [weak self] in
551
+ self?.mapProvider.addPolygon(polygon)
552
+ }
553
+ }
554
+
555
+ func updatePolygon(polygon: PolygonData) throws {
556
+ DispatchQueue.main.async { [weak self] in
557
+ self?.mapProvider.updatePolygon(polygon)
558
+ }
559
+ }
560
+
561
+ func removePolygon(id: String) throws {
562
+ DispatchQueue.main.async { [weak self] in
563
+ self?.mapProvider.removePolygon(id)
564
+ }
565
+ }
566
+
567
+ func clearPolygons() throws {
568
+ DispatchQueue.main.async { [weak self] in
569
+ self?.mapProvider.clearPolygons()
570
+ }
571
+ }
572
+
573
+ // MARK: - Circle Methods
574
+
575
+ func addCircle(circle: CircleData) throws {
576
+ DispatchQueue.main.async { [weak self] in
577
+ self?.mapProvider.addCircle(circle)
578
+ }
579
+ }
580
+
581
+ func updateCircle(circle: CircleData) throws {
582
+ DispatchQueue.main.async { [weak self] in
583
+ self?.mapProvider.updateCircle(circle)
584
+ }
585
+ }
586
+
587
+ func removeCircle(id: String) throws {
588
+ DispatchQueue.main.async { [weak self] in
589
+ self?.mapProvider.removeCircle(id)
590
+ }
591
+ }
592
+
593
+ func clearCircles() throws {
594
+ DispatchQueue.main.async { [weak self] in
595
+ self?.mapProvider.clearCircles()
596
+ }
597
+ }
332
598
  }
@@ -0,0 +1,45 @@
1
+ import Foundation
2
+ import GoogleMaps
3
+ import NitroModules
4
+
5
+ class HybridNitroMapConfig: HybridNitroMapConfigSpec {
6
+
7
+ // Static flag to track initialization across all instances
8
+ private static var isInitialized = false
9
+
10
+ func NitroMapInitialize(apiKey: String, provider: MapProvider) {
11
+ // Use static flag to prevent duplicate initialization
12
+ guard !HybridNitroMapConfig.isInitialized else {
13
+ #if DEBUG
14
+ print("[NitroMap] Already initialized, skipping...")
15
+ #endif
16
+ return
17
+ }
18
+
19
+ switch provider {
20
+ case .google:
21
+ // Set initialized BEFORE async dispatch to prevent race conditions
22
+ HybridNitroMapConfig.isInitialized = true
23
+ DispatchQueue.main.async {
24
+ GMSServices.provideAPIKey(apiKey)
25
+ #if DEBUG
26
+ print("[NitroMap] Google Maps initialized")
27
+ #endif
28
+ }
29
+ case .apple:
30
+ // Apple Maps uses MapKit (built-in) — no SDK initialization needed
31
+ HybridNitroMapConfig.isInitialized = true
32
+ #if DEBUG
33
+ print("[NitroMap] Apple Maps ready (no SDK init needed)")
34
+ #endif
35
+ case .yandex:
36
+ #if DEBUG
37
+ print("[NitroMap] Yandex Maps is not yet implemented")
38
+ #endif
39
+ }
40
+ }
41
+
42
+ func IsNitroMapInitialized() -> Bool {
43
+ return HybridNitroMapConfig.isInitialized
44
+ }
45
+ }