@maydon_tech/react-native-nitro-maps 0.1.3 → 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 (218) 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/Clustering/ClusterIconRenderer.swift +3 -3
  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 +135 -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 +75 -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++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
  125. package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
  126. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  127. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +328 -21
  128. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
  129. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
  130. package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
  131. package/nitrogen/generated/android/c++/JPolygonData.hpp +149 -0
  132. package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
  133. package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
  134. package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
  135. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
  136. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
  148. package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
  149. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
  150. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +175 -17
  151. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +15 -3
  152. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +249 -16
  153. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
  154. package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
  155. package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
  156. package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
  157. package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
  158. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  159. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
  160. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
  161. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
  162. package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
  163. package/nitrogen/generated/ios/swift/PolygonData.swift +179 -0
  164. package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
  165. package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
  166. package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
  167. package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
  168. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
  169. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
  170. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
  171. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
  172. package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
  173. package/nitrogen/generated/shared/c++/PolygonData.hpp +114 -0
  174. package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
  175. package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
  176. package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
  177. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
  178. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
  179. package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
  180. package/package.json +36 -5
  181. package/src/components/ImageMarker.tsx +58 -42
  182. package/src/components/Marker.tsx +161 -0
  183. package/src/components/NitroCircle.tsx +183 -0
  184. package/src/components/NitroMap.tsx +328 -78
  185. package/src/components/NitroPolygon.tsx +229 -0
  186. package/src/components/NitroPolyline.tsx +208 -0
  187. package/src/components/PriceMarker.tsx +23 -48
  188. package/src/context/NitroMapContext.tsx +4 -0
  189. package/src/hooks/useNitroCircle.ts +25 -0
  190. package/src/hooks/useNitroMarker.ts +49 -10
  191. package/src/hooks/useNitroOverlay.ts +68 -0
  192. package/src/hooks/useNitroPolygon.ts +25 -0
  193. package/src/hooks/useNitroPolyline.ts +25 -0
  194. package/src/index.tsx +23 -2
  195. package/src/specs/NitroMap.nitro.ts +294 -5
  196. package/src/types/map.ts +36 -4
  197. package/src/types/marker.ts +24 -44
  198. package/src/types/overlay.ts +77 -0
  199. package/src/types/theme.ts +101 -0
  200. package/src/utils/colors.ts +48 -16
  201. package/src/utils/validation.ts +69 -0
  202. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
  203. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
  204. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
  205. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
  206. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
  207. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
  208. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
  209. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
  210. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
  211. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
  212. package/cpp/QuadTree.hpp +0 -246
  213. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
  214. package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
  215. package/ios/Providers/GoogleMapProvider.swift +0 -924
  216. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
  217. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
  218. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
@@ -1,252 +0,0 @@
1
- package com.margelo.nitro.nitromap
2
-
3
- import android.content.Context
4
- import android.graphics.*
5
- import android.util.Base64
6
- import com.google.android.gms.maps.model.BitmapDescriptor
7
- import com.google.android.gms.maps.model.BitmapDescriptorFactory
8
- import java.net.URL
9
- import kotlin.math.roundToInt
10
-
11
- /** Factory for creating marker icons from MarkerData configuration */
12
- object MarkerIconFactory {
13
-
14
- private var reactContext: com.facebook.react.bridge.ReactContext? = null
15
-
16
- /** Set the React context for view capture (call this from HybridNitroMap initialization) */
17
- fun setReactContext(context: com.facebook.react.bridge.ReactContext?) {
18
- reactContext = context
19
- }
20
-
21
- fun createIcon(context: Context, markerData: MarkerData): BitmapDescriptor? {
22
- return when (markerData.config.style) {
23
- MarkerStyle.IMAGE -> createImageMarkerIcon(context, markerData.config.image)
24
- MarkerStyle.PRICEMARKER ->
25
- createPriceMarkerStyleIcon(context, markerData.config.priceMarker)
26
- MarkerStyle.DEFAULT -> null
27
- }
28
- }
29
-
30
- private fun createPriceMarkerStyleIcon(
31
- context: Context,
32
- config: PriceMarkerStyle?
33
- ): BitmapDescriptor? {
34
- config ?: return null
35
-
36
- // Generate cache key (Flyweight pattern)
37
- val cacheKey =
38
- MarkerIconCache.priceMarkerKey(config.price, config.currency, config.selected)
39
-
40
- // Check cache first
41
- MarkerIconCache.getPriceMarker(cacheKey)?.let { cachedBitmap ->
42
- return BitmapDescriptorFactory.fromBitmap(cachedBitmap)
43
- }
44
-
45
- val density = context.resources.displayMetrics.density
46
- val priceText = "${config.price} ${config.currency}"
47
- val fontSize = (config.fontSize ?: 14.0) * density
48
- val paddingH = (config.paddingHorizontal ?: 12.0) * density
49
- val paddingV = (config.paddingVertical ?: 8.0) * density
50
-
51
- val textPaint =
52
- Paint(Paint.ANTI_ALIAS_FLAG).apply {
53
- textSize = fontSize.toFloat()
54
- typeface = Typeface.DEFAULT_BOLD
55
- textAlign = Paint.Align.LEFT
56
- }
57
-
58
- // Set colors based on selection state
59
- val bgColor =
60
- if (config.selected) {
61
- config.selectedBackgroundColor?.let { ColorUtils.fromVariant(it) }
62
- ?: Color.parseColor("#E53935") // Red for selected
63
- } else {
64
- config.backgroundColor?.let { ColorUtils.fromVariant(it) } ?: Color.WHITE
65
- }
66
-
67
- val textColor =
68
- if (config.selected) {
69
- config.selectedTextColor?.let { ColorUtils.fromVariant(it) } ?: Color.WHITE
70
- } else {
71
- config.textColor?.let { ColorUtils.fromVariant(it) } ?: Color.BLACK
72
- }
73
-
74
- textPaint.color = textColor
75
-
76
- val textBounds = Rect()
77
- textPaint.getTextBounds(priceText, 0, priceText.length, textBounds)
78
-
79
- val arrowHeight = 8 * density
80
- val width = textBounds.width() + paddingH * 2
81
- val height = textBounds.height() + paddingV * 2 + arrowHeight
82
- val cornerRadius = 8 * density
83
-
84
- val bitmap =
85
- Bitmap.createBitmap(
86
- width.roundToInt(),
87
- height.roundToInt(),
88
- Bitmap.Config.ARGB_8888
89
- )
90
- val canvas = Canvas(bitmap)
91
-
92
- val bgPaint =
93
- Paint(Paint.ANTI_ALIAS_FLAG).apply {
94
- color = bgColor
95
- style = Paint.Style.FILL
96
- }
97
-
98
- // Add shadow
99
- val shadowPaint =
100
- Paint(Paint.ANTI_ALIAS_FLAG).apply {
101
- color = Color.BLACK
102
- alpha = ((config.shadowOpacity ?: 0.2) * 255).toInt()
103
- maskFilter = BlurMaskFilter((4 * density).toFloat(), BlurMaskFilter.Blur.NORMAL)
104
- }
105
-
106
- val bubbleRect =
107
- RectF(
108
- (2 * density).toFloat(),
109
- 0f,
110
- (width - 2 * density).toFloat(),
111
- (height - arrowHeight).toFloat()
112
- )
113
- canvas.drawRoundRect(
114
- bubbleRect.apply { offset(0f, (2 * density).toFloat()) },
115
- cornerRadius.toFloat(),
116
- cornerRadius.toFloat(),
117
- shadowPaint
118
- )
119
-
120
- bubbleRect.offset(0f, (-2 * density).toFloat())
121
- canvas.drawRoundRect(bubbleRect, cornerRadius.toFloat(), cornerRadius.toFloat(), bgPaint)
122
-
123
- // Draw arrow
124
- val path =
125
- Path().apply {
126
- moveTo((width / 2 - arrowHeight).toFloat(), (height - arrowHeight).toFloat())
127
- lineTo((width / 2).toFloat(), height.toFloat())
128
- lineTo((width / 2 + arrowHeight).toFloat(), (height - arrowHeight).toFloat())
129
- close()
130
- }
131
- canvas.drawPath(path, bgPaint)
132
-
133
- // Draw text
134
- val textX = paddingH.toFloat()
135
- val textY = paddingV.toFloat() + textBounds.height()
136
- canvas.drawText(priceText, textX, textY, textPaint)
137
-
138
- // Cache the bitmap for reuse
139
- MarkerIconCache.putPriceMarker(cacheKey, bitmap)
140
-
141
- return BitmapDescriptorFactory.fromBitmap(bitmap)
142
- }
143
-
144
- private fun createImageMarkerIcon(
145
- context: Context,
146
- config: ImageMarkerConfig?
147
- ): BitmapDescriptor? {
148
- config ?: return null
149
-
150
- val imageSource = config.imageBase64 ?: config.imageUrl ?: ""
151
-
152
- // Generate cache key (Flyweight pattern)
153
- val cacheKey =
154
- MarkerIconCache.imageMarkerKey(
155
- imageSource,
156
- config.width,
157
- config.height,
158
- config.cornerRadius
159
- )
160
-
161
- // Check cache first
162
- MarkerIconCache.getImageMarker(cacheKey)?.let { cachedBitmap ->
163
- return BitmapDescriptorFactory.fromBitmap(cachedBitmap)
164
- }
165
-
166
- val density = context.resources.displayMetrics.density
167
- val width = (config.width * density).roundToInt()
168
- val height = (config.height * density).roundToInt()
169
- val cornerRadius = config.cornerRadius * density
170
- val borderWidth = config.borderWidth * density
171
-
172
- var sourceImage: Bitmap? = null
173
-
174
- // Try to load image from base64
175
- config.imageBase64?.let { base64 ->
176
- try {
177
- val bytes = Base64.decode(base64, Base64.DEFAULT)
178
- sourceImage = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
179
- } catch (e: Exception) {
180
- e.printStackTrace()
181
- }
182
- }
183
-
184
- // Try to load from URL (blocking - should be done on background thread in production)
185
- if (sourceImage == null) {
186
- config.imageUrl?.let { urlString ->
187
- try {
188
- val url = URL(urlString)
189
- val connection = url.openConnection()
190
- connection.connectTimeout = 5000
191
- connection.readTimeout = 5000
192
- val inputStream = connection.getInputStream()
193
- sourceImage = BitmapFactory.decodeStream(inputStream)
194
- inputStream.close()
195
- } catch (e: Exception) {
196
- e.printStackTrace()
197
- }
198
- }
199
- }
200
-
201
- val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
202
- val canvas = Canvas(bitmap)
203
-
204
- val clipPath =
205
- Path().apply {
206
- addRoundRect(
207
- RectF(0f, 0f, width.toFloat(), height.toFloat()),
208
- cornerRadius.toFloat(),
209
- cornerRadius.toFloat(),
210
- Path.Direction.CW
211
- )
212
- }
213
- canvas.clipPath(clipPath)
214
-
215
- // Draw image or placeholder
216
- sourceImage?.let { img ->
217
- val scaledBitmap = Bitmap.createScaledBitmap(img, width, height, true)
218
- canvas.drawBitmap(scaledBitmap, 0f, 0f, null)
219
- if (scaledBitmap != img) scaledBitmap.recycle()
220
- }
221
- ?: run {
222
- val placeholderPaint = Paint().apply { color = Color.LTGRAY }
223
- canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), placeholderPaint)
224
- }
225
-
226
- // Draw border
227
- if (borderWidth > 0) {
228
- val borderPaint =
229
- Paint(Paint.ANTI_ALIAS_FLAG).apply {
230
- color = ColorUtils.fromColorValue(config.borderColor)
231
- style = Paint.Style.STROKE
232
- strokeWidth = borderWidth.toFloat()
233
- }
234
- canvas.drawRoundRect(
235
- RectF(
236
- borderWidth.toFloat() / 2,
237
- borderWidth.toFloat() / 2,
238
- width - borderWidth.toFloat() / 2,
239
- height - borderWidth.toFloat() / 2
240
- ),
241
- cornerRadius.toFloat(),
242
- cornerRadius.toFloat(),
243
- borderPaint
244
- )
245
- }
246
-
247
- // Cache the bitmap for reuse
248
- MarkerIconCache.putImageMarker(cacheKey, bitmap)
249
-
250
- return BitmapDescriptorFactory.fromBitmap(bitmap)
251
- }
252
- }
@@ -1,252 +0,0 @@
1
- package com.margelo.nitro.nitromap.clustering
2
-
3
- import com.google.android.gms.maps.model.LatLng
4
- import com.google.android.gms.maps.model.LatLngBounds
5
- import com.margelo.nitro.nitromap.ClusterConfig
6
- import com.margelo.nitro.nitromap.MarkerData
7
- import kotlin.math.*
8
-
9
- /**
10
- * Marker point for clustering
11
- */
12
- data class MarkerPoint(
13
- val id: String,
14
- val latitude: Double,
15
- val longitude: Double
16
- )
17
-
18
- /**
19
- * Cluster result containing multiple markers
20
- */
21
- data class Cluster(
22
- val latitude: Double,
23
- val longitude: Double,
24
- val markerIds: List<String>,
25
- val count: Int
26
- )
27
-
28
- /**
29
- * Full clustering result
30
- */
31
- data class ClusterResult(
32
- val clusters: List<Cluster>,
33
- val singleMarkers: List<MarkerPoint>
34
- )
35
-
36
- /**
37
- * High-performance clustering engine using QuadTree spatial indexing
38
- * Kotlin port of C++ ClusterEngine
39
- */
40
- class NitroClusterEngine {
41
-
42
- // QuadTree for O(log n) spatial queries
43
- private var quadTree: QuadTree? = null
44
-
45
- // All markers stored by ID
46
- private val markers = mutableMapOf<String, MarkerPoint>()
47
-
48
- // Marker data cache for full marker info
49
- private val markerDataCache = mutableMapOf<String, MarkerData>()
50
-
51
- // Configuration
52
- private var clusterRadius = 60.0
53
- private var minClusterSize = 2
54
- private var maxZoom = 20.0
55
-
56
- // MARK: - Configuration
57
-
58
- fun setClusterRadius(radius: Double) {
59
- clusterRadius = radius
60
- }
61
-
62
- fun setMinClusterSize(size: Int) {
63
- minClusterSize = size
64
- }
65
-
66
- fun setMaxZoom(zoom: Double) {
67
- maxZoom = zoom
68
- }
69
-
70
- fun updateConfig(config: ClusterConfig?) {
71
- config?.let {
72
- minClusterSize = it.minimumClusterSize.toInt()
73
- maxZoom = it.maxZoom
74
- }
75
- }
76
-
77
- // MARK: - Marker Management
78
-
79
- fun addMarker(markerData: MarkerData) {
80
- val id = markerData.id
81
- val point = MarkerPoint(
82
- id = id,
83
- latitude = markerData.coordinate.latitude,
84
- longitude = markerData.coordinate.longitude
85
- )
86
- markers[id] = point
87
- markerDataCache[id] = markerData
88
- rebuildQuadTree()
89
- }
90
-
91
- fun addMarkers(markersList: List<MarkerData>) {
92
- for (marker in markersList) {
93
- val id = marker.id
94
- val point = MarkerPoint(
95
- id = id,
96
- latitude = marker.coordinate.latitude,
97
- longitude = marker.coordinate.longitude
98
- )
99
- markers[id] = point
100
- markerDataCache[id] = marker
101
- }
102
- rebuildQuadTree()
103
- }
104
-
105
- fun removeMarker(id: String) {
106
- markers.remove(id)
107
- markerDataCache.remove(id)
108
- rebuildQuadTree()
109
- }
110
-
111
- fun clearMarkers() {
112
- markers.clear()
113
- markerDataCache.clear()
114
- quadTree?.clear()
115
- quadTree = null
116
- }
117
-
118
- fun getMarkerCount(): Int = markers.size
119
-
120
- fun getMarkerData(id: String): MarkerData? = markerDataCache[id]
121
-
122
- // MARK: - Clustering
123
-
124
- fun cluster(
125
- bounds: LatLngBounds,
126
- zoom: Float,
127
- mapWidth: Int,
128
- mapHeight: Int
129
- ): ClusterResult {
130
- // At max zoom or beyond, don't cluster
131
- if (zoom >= maxZoom) {
132
- return ClusterResult(
133
- clusters = emptyList(),
134
- singleMarkers = markers.values.toList()
135
- )
136
- }
137
-
138
- val tree = quadTree ?: return ClusterResult(emptyList(), markers.values.toList())
139
-
140
- // Calculate cluster radius in coordinate space
141
- val latSpan = bounds.northeast.latitude - bounds.southwest.latitude
142
- val lngSpan = bounds.northeast.longitude - bounds.southwest.longitude
143
-
144
- // Pixels to degrees conversion
145
- val pixelsPerDegreeLat = if (latSpan > 0) mapHeight / latSpan else 1.0
146
- val pixelsPerDegreeLng = if (lngSpan > 0) mapWidth / lngSpan else 1.0
147
-
148
- // Cluster radius in degrees
149
- val radiusLat = clusterRadius / pixelsPerDegreeLat
150
- val radiusLng = clusterRadius / pixelsPerDegreeLng
151
- val avgRadius = (radiusLat + radiusLng) / 2.0
152
-
153
- // Grid-based clustering for O(n) performance
154
- val gridSizeLat = radiusLat * 2
155
- val gridSizeLng = radiusLng * 2
156
-
157
- // Get visible markers
158
- val visiblePoints = mutableListOf<QuadPoint>()
159
- val range = BoundingBox(
160
- bounds.southwest.longitude,
161
- bounds.southwest.latitude,
162
- bounds.northeast.longitude,
163
- bounds.northeast.latitude
164
- )
165
- tree.query(range, visiblePoints)
166
-
167
- // Track processed markers
168
- val processed = mutableSetOf<String>()
169
- val clusters = mutableListOf<Cluster>()
170
- val singleMarkers = mutableListOf<MarkerPoint>()
171
-
172
- for (point in visiblePoints) {
173
- if (processed.contains(point.id)) continue
174
-
175
- // Find nearby markers
176
- val nearbyPoints = mutableListOf<QuadPoint>()
177
- tree.queryRadius(point.x, point.y, avgRadius, nearbyPoints)
178
-
179
- // Filter to unprocessed points
180
- val unprocessedNearby = nearbyPoints.filter { !processed.contains(it.id) }
181
-
182
- if (unprocessedNearby.size >= minClusterSize) {
183
- // Create cluster
184
- var sumLat = 0.0
185
- var sumLng = 0.0
186
- val markerIds = mutableListOf<String>()
187
-
188
- for (nearby in unprocessedNearby) {
189
- sumLng += nearby.x
190
- sumLat += nearby.y
191
- markerIds.add(nearby.id)
192
- processed.add(nearby.id)
193
- }
194
-
195
- val count = unprocessedNearby.size
196
- clusters.add(
197
- Cluster(
198
- latitude = sumLat / count,
199
- longitude = sumLng / count,
200
- markerIds = markerIds,
201
- count = count
202
- )
203
- )
204
- } else {
205
- // Single marker
206
- processed.add(point.id)
207
- markers[point.id]?.let { singleMarkers.add(it) }
208
- }
209
- }
210
-
211
- return ClusterResult(clusters, singleMarkers)
212
- }
213
-
214
- private fun rebuildQuadTree() {
215
- if (markers.isEmpty()) {
216
- quadTree = null
217
- return
218
- }
219
-
220
- // Find bounds
221
- var minLat = Double.MAX_VALUE
222
- var maxLat = -Double.MAX_VALUE
223
- var minLng = Double.MAX_VALUE
224
- var maxLng = -Double.MAX_VALUE
225
-
226
- for (marker in markers.values) {
227
- minLat = min(minLat, marker.latitude)
228
- maxLat = max(maxLat, marker.latitude)
229
- minLng = min(minLng, marker.longitude)
230
- maxLng = max(maxLng, marker.longitude)
231
- }
232
-
233
- // Add padding
234
- val latPadding = (maxLat - minLat) * 0.1 + 0.001
235
- val lngPadding = (maxLng - minLng) * 0.1 + 0.001
236
-
237
- val bounds = BoundingBox(
238
- minLng - lngPadding,
239
- minLat - latPadding,
240
- maxLng + lngPadding,
241
- maxLat + latPadding
242
- )
243
-
244
- quadTree = QuadTree(bounds)
245
-
246
- for (marker in markers.values) {
247
- quadTree?.insert(
248
- QuadPoint(marker.longitude, marker.latitude, marker.id)
249
- )
250
- }
251
- }
252
- }
@@ -1,195 +0,0 @@
1
- package com.margelo.nitro.nitromap.clustering
2
-
3
- import kotlin.math.sqrt
4
-
5
- /**
6
- * Point in 2D space with optional ID
7
- */
8
- data class QuadPoint(
9
- val x: Double,
10
- val y: Double,
11
- val id: String = ""
12
- )
13
-
14
- /**
15
- * Bounding box for spatial queries
16
- */
17
- data class BoundingBox(
18
- val minX: Double,
19
- val minY: Double,
20
- val maxX: Double,
21
- val maxY: Double
22
- ) {
23
- fun contains(point: QuadPoint): Boolean {
24
- return point.x >= minX && point.x <= maxX &&
25
- point.y >= minY && point.y <= maxY
26
- }
27
-
28
- fun intersects(other: BoundingBox): Boolean {
29
- return !(other.minX > maxX || other.maxX < minX ||
30
- other.minY > maxY || other.maxY < minY)
31
- }
32
- }
33
-
34
- /**
35
- * High-performance QuadTree for spatial indexing
36
- * O(log n) point insertion and range queries
37
- */
38
- class QuadTree(
39
- private val boundary: BoundingBox,
40
- private val capacity: Int = MAX_POINTS,
41
- private val depth: Int = 0
42
- ) {
43
- companion object {
44
- const val MAX_POINTS = 4
45
- const val MAX_DEPTH = 12
46
- }
47
-
48
- private val points = mutableListOf<QuadPoint>()
49
- private var divided = false
50
- private var northwest: QuadTree? = null
51
- private var northeast: QuadTree? = null
52
- private var southwest: QuadTree? = null
53
- private var southeast: QuadTree? = null
54
-
55
- /**
56
- * Insert a point into the tree
57
- */
58
- fun insert(point: QuadPoint): Boolean {
59
- if (!boundary.contains(point)) {
60
- return false
61
- }
62
-
63
- if (points.size < capacity || depth >= MAX_DEPTH) {
64
- points.add(point)
65
- return true
66
- }
67
-
68
- if (!divided) {
69
- subdivide()
70
- }
71
-
72
- return northwest?.insert(point) == true ||
73
- northeast?.insert(point) == true ||
74
- southwest?.insert(point) == true ||
75
- southeast?.insert(point) == true
76
- }
77
-
78
- /**
79
- * Query points within a bounding box
80
- */
81
- fun query(range: BoundingBox, found: MutableList<QuadPoint>) {
82
- if (!boundary.intersects(range)) {
83
- return
84
- }
85
-
86
- for (point in points) {
87
- if (range.contains(point)) {
88
- found.add(point)
89
- }
90
- }
91
-
92
- if (divided) {
93
- northwest?.query(range, found)
94
- northeast?.query(range, found)
95
- southwest?.query(range, found)
96
- southeast?.query(range, found)
97
- }
98
- }
99
-
100
- /**
101
- * Query points within a circular radius
102
- */
103
- fun queryRadius(
104
- centerX: Double,
105
- centerY: Double,
106
- radius: Double,
107
- found: MutableList<QuadPoint>
108
- ) {
109
- val range = BoundingBox(
110
- centerX - radius,
111
- centerY - radius,
112
- centerX + radius,
113
- centerY + radius
114
- )
115
-
116
- if (!boundary.intersects(range)) {
117
- return
118
- }
119
-
120
- val radiusSq = radius * radius
121
- for (point in points) {
122
- val dx = point.x - centerX
123
- val dy = point.y - centerY
124
- if (dx * dx + dy * dy <= radiusSq) {
125
- found.add(point)
126
- }
127
- }
128
-
129
- if (divided) {
130
- northwest?.queryRadius(centerX, centerY, radius, found)
131
- northeast?.queryRadius(centerX, centerY, radius, found)
132
- southwest?.queryRadius(centerX, centerY, radius, found)
133
- southeast?.queryRadius(centerX, centerY, radius, found)
134
- }
135
- }
136
-
137
- /**
138
- * Get all points in the tree
139
- */
140
- fun getAllPoints(allPoints: MutableList<QuadPoint>) {
141
- allPoints.addAll(points)
142
- if (divided) {
143
- northwest?.getAllPoints(allPoints)
144
- northeast?.getAllPoints(allPoints)
145
- southwest?.getAllPoints(allPoints)
146
- southeast?.getAllPoints(allPoints)
147
- }
148
- }
149
-
150
- /**
151
- * Clear all points from the tree
152
- */
153
- fun clear() {
154
- points.clear()
155
- if (divided) {
156
- northwest?.clear()
157
- northeast?.clear()
158
- southwest?.clear()
159
- southeast?.clear()
160
- northwest = null
161
- northeast = null
162
- southwest = null
163
- southeast = null
164
- divided = false
165
- }
166
- }
167
-
168
- private fun subdivide() {
169
- val midX = (boundary.minX + boundary.maxX) / 2
170
- val midY = (boundary.minY + boundary.maxY) / 2
171
-
172
- northwest = QuadTree(
173
- BoundingBox(boundary.minX, midY, midX, boundary.maxY),
174
- capacity,
175
- depth + 1
176
- )
177
- northeast = QuadTree(
178
- BoundingBox(midX, midY, boundary.maxX, boundary.maxY),
179
- capacity,
180
- depth + 1
181
- )
182
- southwest = QuadTree(
183
- BoundingBox(boundary.minX, boundary.minY, midX, midY),
184
- capacity,
185
- depth + 1
186
- )
187
- southeast = QuadTree(
188
- BoundingBox(midX, boundary.minY, boundary.maxX, midY),
189
- capacity,
190
- depth + 1
191
- )
192
-
193
- divided = true
194
- }
195
- }