expo-gaode-map-navigation 1.1.5 → 1.1.7

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 (146) hide show
  1. package/README.md +213 -73
  2. package/android/build.gradle +10 -0
  3. package/android/src/main/cpp/CMakeLists.txt +24 -0
  4. package/android/src/main/cpp/cluster_jni.cpp +848 -0
  5. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
  6. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
  7. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
  8. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
  9. package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
  10. package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
  11. package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
  12. package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
  13. package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
  14. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
  15. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
  16. package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
  17. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
  18. package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
  19. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
  20. package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
  21. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
  22. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
  23. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
  24. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
  25. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
  26. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
  27. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
  28. package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
  29. package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
  30. package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
  31. package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
  32. package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
  33. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
  34. package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
  35. package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
  36. package/build/ExpoGaodeMapNaviView.d.ts +7 -7
  37. package/build/ExpoGaodeMapNaviView.js +10 -11
  38. package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
  39. package/build/index.d.ts +35 -33
  40. package/build/index.js +70 -106
  41. package/build/map/ExpoGaodeMapModule.d.ts +2 -201
  42. package/build/map/ExpoGaodeMapModule.js +586 -18
  43. package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
  44. package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
  45. package/build/map/ExpoGaodeMapView.js +66 -58
  46. package/build/map/components/FoldableMapView.d.ts +38 -0
  47. package/build/map/components/FoldableMapView.js +209 -0
  48. package/build/map/components/MapContext.d.ts +12 -0
  49. package/build/map/components/MapContext.js +54 -0
  50. package/build/map/components/MapUI.d.ts +18 -0
  51. package/build/map/components/MapUI.js +29 -0
  52. package/build/map/components/overlays/Circle.js +34 -3
  53. package/build/map/components/overlays/Cluster.d.ts +3 -1
  54. package/build/map/components/overlays/Cluster.js +31 -2
  55. package/build/map/components/overlays/HeatMap.d.ts +3 -1
  56. package/build/map/components/overlays/HeatMap.js +33 -3
  57. package/build/map/components/overlays/Marker.d.ts +1 -1
  58. package/build/map/components/overlays/Marker.js +37 -32
  59. package/build/map/components/overlays/MultiPoint.js +1 -1
  60. package/build/map/components/overlays/Polygon.js +30 -3
  61. package/build/map/components/overlays/Polyline.js +36 -3
  62. package/build/map/index.d.ts +25 -5
  63. package/build/map/index.js +59 -18
  64. package/build/map/types/common.types.d.ts +40 -0
  65. package/build/map/types/common.types.js +0 -4
  66. package/build/map/types/index.d.ts +3 -2
  67. package/build/map/types/map-view.types.d.ts +108 -3
  68. package/build/map/types/native-module.types.d.ts +363 -0
  69. package/build/map/types/native-module.types.js +5 -0
  70. package/build/map/types/offline.types.d.ts +132 -0
  71. package/build/map/types/offline.types.js +5 -0
  72. package/build/map/types/overlays.types.d.ts +137 -24
  73. package/build/map/utils/ErrorHandler.d.ts +110 -0
  74. package/build/map/utils/ErrorHandler.js +421 -0
  75. package/build/map/utils/GeoUtils.d.ts +20 -0
  76. package/build/map/utils/GeoUtils.js +76 -0
  77. package/build/map/utils/OfflineMapManager.d.ts +148 -0
  78. package/build/map/utils/OfflineMapManager.js +217 -0
  79. package/build/map/utils/PermissionUtils.d.ts +91 -0
  80. package/build/map/utils/PermissionUtils.js +255 -0
  81. package/build/map/utils/PlatformDetector.d.ts +102 -0
  82. package/build/map/utils/PlatformDetector.js +186 -0
  83. package/build/types/index.d.ts +1 -0
  84. package/build/types/index.js +1 -0
  85. package/build/types/native-module.types.d.ts +69 -0
  86. package/build/types/native-module.types.js +2 -0
  87. package/build/types/naviview.types.d.ts +1 -1
  88. package/expo-module.config.json +12 -10
  89. package/ios/ExpoGaodeMapNavigation.podspec +9 -0
  90. package/ios/map/ExpoGaodeMapModule.swift +485 -75
  91. package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
  92. package/ios/map/ExpoGaodeMapView.swift +611 -62
  93. package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
  94. package/ios/map/MapPreloadManager.swift +348 -0
  95. package/ios/map/cpp/ClusterEngine.cpp +110 -0
  96. package/ios/map/cpp/ClusterEngine.hpp +20 -0
  97. package/ios/map/cpp/ColorParser.cpp +135 -0
  98. package/ios/map/cpp/ColorParser.hpp +14 -0
  99. package/ios/map/cpp/GeometryEngine.cpp +574 -0
  100. package/ios/map/cpp/GeometryEngine.hpp +159 -0
  101. package/ios/map/cpp/QuadTree.cpp +92 -0
  102. package/ios/map/cpp/QuadTree.hpp +42 -0
  103. package/ios/map/cpp/README.md +55 -0
  104. package/ios/map/managers/UIManager.swift +72 -1
  105. package/ios/map/modules/LocationManager.swift +123 -166
  106. package/ios/map/overlays/CircleView.swift +16 -32
  107. package/ios/map/overlays/CircleViewModule.swift +12 -12
  108. package/ios/map/overlays/ClusterAnnotation.swift +32 -0
  109. package/ios/map/overlays/ClusterView.swift +331 -45
  110. package/ios/map/overlays/ClusterViewModule.swift +20 -6
  111. package/ios/map/overlays/HeatMapView.swift +135 -32
  112. package/ios/map/overlays/HeatMapViewModule.swift +20 -8
  113. package/ios/map/overlays/MarkerView.swift +613 -130
  114. package/ios/map/overlays/MarkerViewModule.swift +38 -18
  115. package/ios/map/overlays/MultiPointView.swift +168 -10
  116. package/ios/map/overlays/MultiPointViewModule.swift +27 -5
  117. package/ios/map/overlays/PolygonView.swift +62 -23
  118. package/ios/map/overlays/PolygonViewModule.swift +18 -12
  119. package/ios/map/overlays/PolylineView.swift +21 -13
  120. package/ios/map/overlays/PolylineViewModule.swift +18 -12
  121. package/ios/map/utils/ClusterNative.h +96 -0
  122. package/ios/map/utils/ClusterNative.mm +377 -0
  123. package/ios/map/utils/ColorParser.swift +12 -1
  124. package/ios/map/utils/CppBridging.mm +13 -0
  125. package/ios/map/utils/GeometryUtils.swift +34 -0
  126. package/ios/map/utils/LatLngParser.swift +87 -0
  127. package/ios/map/utils/PermissionManager.swift +135 -6
  128. package/package.json +3 -2
  129. package/shared/cpp/ClusterEngine.cpp +110 -0
  130. package/shared/cpp/ClusterEngine.hpp +20 -0
  131. package/shared/cpp/ColorParser.cpp +135 -0
  132. package/shared/cpp/ColorParser.hpp +14 -0
  133. package/shared/cpp/GeometryEngine.cpp +574 -0
  134. package/shared/cpp/GeometryEngine.hpp +159 -0
  135. package/shared/cpp/QuadTree.cpp +92 -0
  136. package/shared/cpp/QuadTree.hpp +42 -0
  137. package/shared/cpp/README.md +55 -0
  138. package/shared/cpp/tests/benchmark_js.js +41 -0
  139. package/shared/cpp/tests/run.sh +17 -0
  140. package/shared/cpp/tests/test_main.cpp +276 -0
  141. package/build/map/ExpoGaodeMap.types.d.ts +0 -41
  142. package/build/map/ExpoGaodeMap.types.js +0 -24
  143. package/build/map/utils/EventManager.d.ts +0 -10
  144. package/build/map/utils/EventManager.js +0 -26
  145. package/build/map/utils/ModuleLoader.d.ts +0 -73
  146. package/build/map/utils/ModuleLoader.js +0 -112
@@ -0,0 +1,515 @@
1
+ package expo.modules.gaodemap.map.utils
2
+
3
+ import com.amap.api.maps.AMapUtils
4
+ import com.amap.api.maps.model.LatLng
5
+ import kotlin.math.*
6
+
7
+ /**
8
+ * 几何计算工具类
9
+ *
10
+ * 提供高德官方 SDK AMapUtils 中不包含的几何计算方法
11
+ */
12
+ object GeometryUtils {
13
+
14
+ init {
15
+ System.loadLibrary("gaodecluster_nav")
16
+ }
17
+
18
+ private external fun nativeIsPointInCircle(
19
+ pointLat: Double,
20
+ pointLon: Double,
21
+ centerLat: Double,
22
+ centerLon: Double,
23
+ radiusMeters: Double
24
+ ): Boolean
25
+
26
+ private external fun nativeIsPointInPolygon(
27
+ pointLat: Double,
28
+ pointLon: Double,
29
+ latitudes: DoubleArray,
30
+ longitudes: DoubleArray
31
+ ): Boolean
32
+
33
+ private external fun nativeCalculatePolygonArea(
34
+ latitudes: DoubleArray,
35
+ longitudes: DoubleArray
36
+ ): Double
37
+
38
+ private external fun nativeCalculateRectangleArea(
39
+ swLat: Double,
40
+ swLon: Double,
41
+ neLat: Double,
42
+ neLon: Double
43
+ ): Double
44
+
45
+ private external fun nativeCalculateDistance(
46
+ lat1: Double,
47
+ lon1: Double,
48
+ lat2: Double,
49
+ lon2: Double
50
+ ): Double
51
+
52
+ private external fun nativeSimplifyPolyline(
53
+ latitudes: DoubleArray,
54
+ longitudes: DoubleArray,
55
+ tolerance: Double
56
+ ): DoubleArray
57
+
58
+ private external fun nativeCalculatePathLength(
59
+ latitudes: DoubleArray,
60
+ longitudes: DoubleArray
61
+ ): Double
62
+
63
+ private external fun nativeGetPointAtDistance(
64
+ latitudes: DoubleArray,
65
+ longitudes: DoubleArray,
66
+ distanceMeters: Double
67
+ ): DoubleArray
68
+
69
+ private external fun nativeGetNearestPointOnPath(
70
+ latitudes: DoubleArray,
71
+ longitudes: DoubleArray,
72
+ targetLat: Double,
73
+ targetLon: Double
74
+ ): DoubleArray
75
+
76
+ private external fun nativeCalculateCentroid(
77
+ latitudes: DoubleArray,
78
+ longitudes: DoubleArray
79
+ ): DoubleArray
80
+
81
+ private external fun nativeCalculatePathBounds(
82
+ latitudes: DoubleArray,
83
+ longitudes: DoubleArray
84
+ ): DoubleArray?
85
+
86
+ private external fun nativeEncodeGeoHash(
87
+ lat: Double,
88
+ lon: Double,
89
+ precision: Int
90
+ ): String
91
+
92
+ private external fun nativeLatLngToTile(
93
+ lat: Double,
94
+ lon: Double,
95
+ zoom: Int
96
+ ): IntArray
97
+
98
+ private external fun nativeTileToLatLng(
99
+ x: Int,
100
+ y: Int,
101
+ zoom: Int
102
+ ): DoubleArray
103
+
104
+ private external fun nativeLatLngToPixel(
105
+ lat: Double,
106
+ lon: Double,
107
+ zoom: Int
108
+ ): DoubleArray
109
+
110
+ private external fun nativePixelToLatLng(
111
+ x: Double,
112
+ y: Double,
113
+ zoom: Int
114
+ ): DoubleArray
115
+
116
+ private external fun nativeFindPointInPolygons(
117
+ pointLat: Double,
118
+ pointLon: Double,
119
+ polygons: Array<DoubleArray>,
120
+ polygonsLon: Array<DoubleArray>
121
+ ): Int
122
+
123
+ private external fun nativeGenerateHeatmapGrid(
124
+ latitudes: DoubleArray,
125
+ longitudes: DoubleArray,
126
+ weights: DoubleArray,
127
+ gridSizeMeters: Double
128
+ ): DoubleArray
129
+
130
+ /**
131
+ * 判断点是否在圆内
132
+ * @param point 要判断的点
133
+ * @param center 圆心坐标
134
+ * @param radius 圆半径(单位:米)
135
+ * @return 是否在圆内
136
+ */
137
+ fun isPointInCircle(point: LatLng, center: LatLng, radius: Double): Boolean {
138
+ return try {
139
+ nativeIsPointInCircle(
140
+ point.latitude,
141
+ point.longitude,
142
+ center.latitude,
143
+ center.longitude,
144
+ radius
145
+ )
146
+ } catch (_: Throwable) {
147
+ val distance = calculateDistance(point, center)
148
+ distance <= radius
149
+ }
150
+ }
151
+
152
+ /**
153
+ * 判断点是否在多边形内(射线法)
154
+ * @param point 要判断的点
155
+ * @param polygon 多边形的顶点坐标数组
156
+ * @return 是否在多边形内
157
+ */
158
+ fun isPointInPolygon(point: LatLng, polygon: List<LatLng>): Boolean {
159
+ return try {
160
+ val latitudes = DoubleArray(polygon.size)
161
+ val longitudes = DoubleArray(polygon.size)
162
+ for (i in polygon.indices) {
163
+ latitudes[i] = polygon[i].latitude
164
+ longitudes[i] = polygon[i].longitude
165
+ }
166
+ nativeIsPointInPolygon(point.latitude, point.longitude, latitudes, longitudes)
167
+ } catch (_: Throwable) {
168
+ var inside = false
169
+ val n = polygon.size
170
+ var j = n - 1
171
+
172
+ for (i in 0 until n) {
173
+ val xi = polygon[i].latitude
174
+ val yi = polygon[i].longitude
175
+ val xj = polygon[j].latitude
176
+ val yj = polygon[j].longitude
177
+
178
+ if ((yi > point.longitude) != (yj > point.longitude) &&
179
+ (point.latitude < (xj - xi) * (point.longitude - yi) / (yj - yi) + xi)) {
180
+ inside = !inside
181
+ }
182
+ j = i
183
+ }
184
+
185
+ inside
186
+ }
187
+ }
188
+
189
+ fun calculatePolygonArea(polygon: List<LatLng>): Double {
190
+ if (polygon.size < 3) {
191
+ return 0.0
192
+ }
193
+ return try {
194
+ val latitudes = DoubleArray(polygon.size)
195
+ val longitudes = DoubleArray(polygon.size)
196
+ for (i in polygon.indices) {
197
+ latitudes[i] = polygon[i].latitude
198
+ longitudes[i] = polygon[i].longitude
199
+ }
200
+ nativeCalculatePolygonArea(latitudes, longitudes)
201
+ } catch (_: Throwable) {
202
+ AMapUtils.calculateArea(polygon).toDouble()
203
+ }
204
+ }
205
+
206
+ fun calculateRectangleArea(southWest: LatLng, northEast: LatLng): Double {
207
+ return try {
208
+ nativeCalculateRectangleArea(
209
+ southWest.latitude,
210
+ southWest.longitude,
211
+ northEast.latitude,
212
+ northEast.longitude
213
+ )
214
+ } catch (_: Throwable) {
215
+ val rectangle = listOf(
216
+ LatLng(southWest.latitude, southWest.longitude),
217
+ LatLng(southWest.latitude, northEast.longitude),
218
+ LatLng(northEast.latitude, northEast.longitude),
219
+ LatLng(northEast.latitude, southWest.longitude)
220
+ )
221
+ AMapUtils.calculateArea(rectangle).toDouble()
222
+ }
223
+ }
224
+
225
+ /**
226
+ * 计算两点之间的距离
227
+ * @param p1 第一个点
228
+ * @param p2 第二个点
229
+ * @return 两点之间的距离(单位:米)
230
+ */
231
+ fun calculateDistance(p1: LatLng, p2: LatLng): Double {
232
+ return try {
233
+ nativeCalculateDistance(p1.latitude, p1.longitude, p2.latitude, p2.longitude)
234
+ } catch (_: Throwable) {
235
+ val lat1 = Math.toRadians(p1.latitude)
236
+ val lat2 = Math.toRadians(p2.latitude)
237
+ val dLat = lat2 - lat1
238
+ val dLon = Math.toRadians(p2.longitude - p1.longitude)
239
+
240
+ val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLon / 2).pow(2)
241
+ val c = 2 * asin(sqrt(a))
242
+
243
+ 6371000.0 * c
244
+ }
245
+ }
246
+
247
+ /**
248
+ * 轨迹抽稀(RDP 算法)
249
+ * @param points 原始轨迹点
250
+ * @param tolerance 允许误差(米)
251
+ * @return 简化后的轨迹点
252
+ */
253
+ fun simplifyPolyline(points: List<LatLng>, tolerance: Double): List<LatLng> {
254
+ if (points.size < 3) return points
255
+ return try {
256
+ val latitudes = DoubleArray(points.size)
257
+ val longitudes = DoubleArray(points.size)
258
+ for (i in points.indices) {
259
+ latitudes[i] = points[i].latitude
260
+ longitudes[i] = points[i].longitude
261
+ }
262
+ val result = nativeSimplifyPolyline(latitudes, longitudes, tolerance)
263
+ val simplified = ArrayList<LatLng>(result.size / 2)
264
+ for (i in result.indices step 2) {
265
+ simplified.add(LatLng(result[i], result[i+1]))
266
+ }
267
+ simplified
268
+ } catch (_: Throwable) {
269
+ points
270
+ }
271
+ }
272
+
273
+ /**
274
+ * 计算路径总长度
275
+ */
276
+ fun calculatePathLength(points: List<LatLng>): Double {
277
+ if (points.size < 2) return 0.0
278
+ return try {
279
+ val latitudes = DoubleArray(points.size)
280
+ val longitudes = DoubleArray(points.size)
281
+ for (i in points.indices) {
282
+ latitudes[i] = points[i].latitude
283
+ longitudes[i] = points[i].longitude
284
+ }
285
+ nativeCalculatePathLength(latitudes, longitudes)
286
+ } catch (_: Throwable) {
287
+ var total = 0.0
288
+ for (i in 0 until points.size - 1) {
289
+ total += calculateDistance(points[i], points[i+1])
290
+ }
291
+ total
292
+ }
293
+ }
294
+
295
+ data class PointAtDistance(val point: LatLng, val angle: Double)
296
+
297
+ /**
298
+ * 获取路径上指定距离的点和角度
299
+ */
300
+ fun getPointAtDistance(points: List<LatLng>, distanceMeters: Double): PointAtDistance? {
301
+ if (points.size < 2) return null
302
+ return try {
303
+ val latitudes = DoubleArray(points.size)
304
+ val longitudes = DoubleArray(points.size)
305
+ for (i in points.indices) {
306
+ latitudes[i] = points[i].latitude
307
+ longitudes[i] = points[i].longitude
308
+ }
309
+ val result = nativeGetPointAtDistance(latitudes, longitudes, distanceMeters)
310
+ if (result != null && result.size >= 3) {
311
+ PointAtDistance(LatLng(result[0], result[1]), result[2])
312
+ } else {
313
+ null
314
+ }
315
+ } catch (_: Throwable) {
316
+ null
317
+ }
318
+ }
319
+
320
+ data class NearestPointResult(val point: LatLng, val index: Int, val distanceMeters: Double)
321
+
322
+ /**
323
+ * 获取路径上距离目标点最近的点
324
+ * @param points 路径点集合
325
+ * @param target 目标点
326
+ * @return 最近点结果,包含点坐标、最近点在路径中的索引(作为起始点的线段)、以及到路径的距离
327
+ */
328
+ fun getNearestPointOnPath(points: List<LatLng>, target: LatLng): NearestPointResult? {
329
+ if (points.size < 2) return null
330
+ return try {
331
+ val latitudes = DoubleArray(points.size)
332
+ val longitudes = DoubleArray(points.size)
333
+ for (i in points.indices) {
334
+ latitudes[i] = points[i].latitude
335
+ longitudes[i] = points[i].longitude
336
+ }
337
+ val result = nativeGetNearestPointOnPath(latitudes, longitudes, target.latitude, target.longitude)
338
+ if (result != null && result.size >= 4) {
339
+ NearestPointResult(
340
+ LatLng(result[0], result[1]),
341
+ result[2].toInt(),
342
+ result[3]
343
+ )
344
+ } else {
345
+ null
346
+ }
347
+ } catch (_: Throwable) {
348
+ null
349
+ }
350
+ }
351
+
352
+ fun calculateCentroid(points: List<LatLng>): LatLng? {
353
+ if (points.size < 3) return null
354
+ return try {
355
+ val latitudes = DoubleArray(points.size)
356
+ val longitudes = DoubleArray(points.size)
357
+ for (i in points.indices) {
358
+ latitudes[i] = points[i].latitude
359
+ longitudes[i] = points[i].longitude
360
+ }
361
+ val result = nativeCalculateCentroid(latitudes, longitudes)
362
+ if (result != null && result.size >= 2) {
363
+ LatLng(result[0], result[1])
364
+ } else {
365
+ null
366
+ }
367
+ } catch (_: Throwable) {
368
+ null
369
+ }
370
+ }
371
+
372
+ data class PathBounds(
373
+ val north: Double,
374
+ val south: Double,
375
+ val east: Double,
376
+ val west: Double,
377
+ val centerLat: Double,
378
+ val centerLon: Double
379
+ )
380
+
381
+ /**
382
+ * 计算路径的边界和中心点
383
+ */
384
+ fun calculatePathBounds(points: List<LatLng>): PathBounds? {
385
+ if (points.isEmpty()) return null
386
+ return try {
387
+ val latitudes = DoubleArray(points.size)
388
+ val longitudes = DoubleArray(points.size)
389
+ for (i in points.indices) {
390
+ latitudes[i] = points[i].latitude
391
+ longitudes[i] = points[i].longitude
392
+ }
393
+ val result = nativeCalculatePathBounds(latitudes, longitudes)
394
+ if (result != null && result.size >= 6) {
395
+ PathBounds(result[0], result[1], result[2], result[3], result[4], result[5])
396
+ } else {
397
+ null
398
+ }
399
+ } catch (_: Throwable) {
400
+ null
401
+ }
402
+ }
403
+
404
+ fun encodeGeoHash(point: LatLng, precision: Int): String {
405
+ return try {
406
+ nativeEncodeGeoHash(point.latitude, point.longitude, precision)
407
+ } catch (_: Throwable) {
408
+ ""
409
+ }
410
+ }
411
+
412
+ /**
413
+ * 解析高德地图 API 返回的 Polyline 字符串
414
+ * 格式: "lng,lat;lng,lat;..."
415
+ */
416
+ fun parsePolyline(polylineStr: String?): List<LatLng> {
417
+ if (polylineStr.isNullOrEmpty()) return emptyList()
418
+ return try {
419
+ val result = nativeParsePolyline(polylineStr)
420
+ if (result != null && result.isNotEmpty()) {
421
+ val points = mutableListOf<LatLng>()
422
+ for (i in 0 until result.size step 2) {
423
+ points.add(LatLng(result[i], result[i+1]))
424
+ }
425
+ points
426
+ } else {
427
+ emptyList()
428
+ }
429
+ } catch (_: Throwable) {
430
+ emptyList()
431
+ }
432
+ }
433
+
434
+ private external fun nativeParsePolyline(
435
+ polylineStr: String
436
+ ): DoubleArray?
437
+
438
+ fun latLngToTile(latLng: LatLng, zoom: Int): IntArray? {
439
+ return try {
440
+ nativeLatLngToTile(latLng.latitude, latLng.longitude, zoom)
441
+ } catch (_: Throwable) {
442
+ null
443
+ }
444
+ }
445
+
446
+ fun tileToLatLng(x: Int, y: Int, zoom: Int): LatLng? {
447
+ return try {
448
+ val result = nativeTileToLatLng(x, y, zoom)
449
+ if (result != null && result.size >= 2) {
450
+ LatLng(result[0], result[1])
451
+ } else {
452
+ null
453
+ }
454
+ } catch (_: Throwable) {
455
+ null
456
+ }
457
+ }
458
+
459
+ fun latLngToPixel(latLng: LatLng, zoom: Int): DoubleArray? {
460
+ return try {
461
+ nativeLatLngToPixel(latLng.latitude, latLng.longitude, zoom)
462
+ } catch (_: Throwable) {
463
+ null
464
+ }
465
+ }
466
+
467
+ fun pixelToLatLng(x: Double, y: Double, zoom: Int): LatLng? {
468
+ return try {
469
+ val result = nativePixelToLatLng(x, y, zoom)
470
+ if (result != null && result.size >= 2) {
471
+ LatLng(result[0], result[1])
472
+ } else {
473
+ null
474
+ }
475
+ } catch (_: Throwable) {
476
+ null
477
+ }
478
+ }
479
+
480
+ fun findPointInPolygons(point: LatLng, polygons: List<List<LatLng>>): Int {
481
+ if (polygons.isEmpty()) return -1
482
+ return try {
483
+ val polygonsLat = Array(polygons.size) { i ->
484
+ DoubleArray(polygons[i].size) { j -> polygons[i][j].latitude }
485
+ }
486
+ val polygonsLon = Array(polygons.size) { i ->
487
+ DoubleArray(polygons[i].size) { j -> polygons[i][j].longitude }
488
+ }
489
+ nativeFindPointInPolygons(point.latitude, point.longitude, polygonsLat, polygonsLon)
490
+ } catch (_: Throwable) {
491
+ -1
492
+ }
493
+ }
494
+
495
+ data class HeatmapGridCell(val latitude: Double, val longitude: Double, val intensity: Double)
496
+
497
+ fun generateHeatmapGrid(
498
+ latitudes: DoubleArray,
499
+ longitudes: DoubleArray,
500
+ weights: DoubleArray,
501
+ gridSizeMeters: Double
502
+ ): List<HeatmapGridCell> {
503
+ if (latitudes.isEmpty() || latitudes.size != longitudes.size || latitudes.size != weights.size) return emptyList()
504
+ return try {
505
+ val result = nativeGenerateHeatmapGrid(latitudes, longitudes, weights, gridSizeMeters)
506
+ val cells = mutableListOf<HeatmapGridCell>()
507
+ for (i in result.indices step 3) {
508
+ cells.add(HeatmapGridCell(result[i], result[i+1], result[i+2]))
509
+ }
510
+ cells
511
+ } catch (_: Throwable) {
512
+ emptyList()
513
+ }
514
+ }
515
+ }
@@ -0,0 +1,91 @@
1
+ package expo.modules.gaodemap.map.utils
2
+
3
+ import com.amap.api.maps.model.LatLng
4
+
5
+ /**
6
+ * 坐标解析工具类
7
+ * 支持解析多种格式的坐标:
8
+ * 1. Map: { "latitude": 39.9, "longitude": 116.4 }
9
+ * 2. List: [116.4, 39.9] (longitude, latitude)
10
+ */
11
+ object LatLngParser {
12
+
13
+ /**
14
+ * 解析单个坐标点
15
+ */
16
+ fun parseLatLng(data: Any?): LatLng? {
17
+ if (data == null) return null
18
+
19
+ val latLng = when (data) {
20
+ is Map<*, *> -> {
21
+ val lat = (data["latitude"] as? Number)?.toDouble()
22
+ val lng = (data["longitude"] as? Number)?.toDouble()
23
+ if (lat != null && lng != null) LatLng(lat, lng) else null
24
+ }
25
+ is List<*> -> {
26
+ if (data.size >= 2) {
27
+ val lng = (data[0] as? Number)?.toDouble()
28
+ val lat = (data[1] as? Number)?.toDouble()
29
+ if (lat != null && lng != null) LatLng(lat, lng) else null
30
+ } else null
31
+ }
32
+ else -> null
33
+ }
34
+
35
+ // 验证坐标范围
36
+ return if (latLng != null &&
37
+ latLng.latitude >= -90.0 && latLng.latitude <= 90.0 &&
38
+ latLng.longitude >= -180.0 && latLng.longitude <= 180.0) {
39
+ latLng
40
+ } else {
41
+ null
42
+ }
43
+ }
44
+
45
+ /**
46
+ * 解析坐标列表
47
+ * 支持扁平列表 [p1, p2, ...]
48
+ * 也支持嵌套列表 [[p1, p2], [p3, p4]] 并自动展平
49
+ */
50
+ fun parseLatLngList(data: Any?): List<LatLng> {
51
+ if (data == null) return emptyList()
52
+ if (data !is List<*>) return emptyList()
53
+
54
+ val result = mutableListOf<LatLng>()
55
+ for (item in data) {
56
+ // 尝试直接解析为点
57
+ val point = parseLatLng(item)
58
+ if (point != null) {
59
+ result.add(point)
60
+ } else if (item is List<*>) {
61
+ // 如果不是点且是列表,递归解析并展平
62
+ result.addAll(parseLatLngList(item))
63
+ }
64
+ }
65
+ return result
66
+ }
67
+
68
+ /**
69
+ * 解析嵌套坐标列表 (例如用于多边形孔洞)
70
+ * 返回 List<List<LatLng>>
71
+ */
72
+ fun parseLatLngListList(data: Any?): List<List<LatLng>> {
73
+ if (data == null || data !is List<*>) return emptyList()
74
+ if (data.isEmpty()) return emptyList()
75
+
76
+ // 检查第一项。如果第一项能直接解析为点,说明这是一个平铺的列表 [p1, p2, ...]
77
+ if (parseLatLng(data[0]) != null) {
78
+ return listOf(parseLatLngList(data))
79
+ }
80
+
81
+ // 否则,它可能是一个嵌套列表 [[p1, p2], [p3, p4]]
82
+ val result = mutableListOf<List<LatLng>>()
83
+ for (item in data) {
84
+ val ring = parseLatLngList(item)
85
+ if (ring.isNotEmpty()) {
86
+ result.add(ring)
87
+ }
88
+ }
89
+ return result
90
+ }
91
+ }