expo-gaode-map-navigation 1.1.5 → 1.1.6
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.
- package/README.md +213 -73
- package/android/build.gradle +10 -0
- package/android/src/main/cpp/CMakeLists.txt +24 -0
- package/android/src/main/cpp/cluster_jni.cpp +848 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +616 -92
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +493 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +230 -14
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapViewModule.kt +37 -27
- package/android/src/main/java/expo/modules/gaodemap/map/MapPreloadManager.kt +494 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/BitmapDescriptorCache.kt +30 -0
- package/android/src/main/java/expo/modules/gaodemap/map/companion/IconBitmapCache.kt +37 -0
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +76 -0
- package/android/src/main/java/expo/modules/gaodemap/map/modules/LocationManager.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +4 -59
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleView.kt +9 -12
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/CircleViewModule.kt +5 -6
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +539 -66
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterViewModule.kt +17 -1
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +165 -33
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +15 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +1249 -672
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +40 -17
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointView.kt +177 -22
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MultiPointViewModule.kt +11 -3
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonView.kt +57 -14
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolygonViewModule.kt +9 -5
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineView.kt +90 -63
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/PolylineViewModule.kt +7 -3
- package/android/src/main/java/expo/modules/gaodemap/map/services/LocationForegroundService.kt +3 -2
- package/android/src/main/java/expo/modules/gaodemap/map/utils/BitmapDescriptorCache.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ClusterNative.kt +13 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/ColorParser.kt +20 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +515 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/LatLngParser.kt +91 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/PermissionHelper.kt +248 -0
- package/build/ExpoGaodeMapNaviView.d.ts +7 -7
- package/build/ExpoGaodeMapNaviView.js +10 -11
- package/build/ExpoGaodeMapNavigationModule.d.ts +2 -1
- package/build/index.d.ts +35 -33
- package/build/index.js +70 -106
- package/build/map/ExpoGaodeMapModule.d.ts +2 -201
- package/build/map/ExpoGaodeMapModule.js +586 -18
- package/build/map/ExpoGaodeMapOfflineModule.d.ts +139 -0
- package/build/map/ExpoGaodeMapOfflineModule.js +8 -0
- package/build/map/ExpoGaodeMapView.js +66 -58
- package/build/map/components/FoldableMapView.d.ts +38 -0
- package/build/map/components/FoldableMapView.js +209 -0
- package/build/map/components/MapContext.d.ts +12 -0
- package/build/map/components/MapContext.js +54 -0
- package/build/map/components/MapUI.d.ts +18 -0
- package/build/map/components/MapUI.js +29 -0
- package/build/map/components/overlays/Circle.js +34 -3
- package/build/map/components/overlays/Cluster.d.ts +3 -1
- package/build/map/components/overlays/Cluster.js +31 -2
- package/build/map/components/overlays/HeatMap.d.ts +3 -1
- package/build/map/components/overlays/HeatMap.js +33 -3
- package/build/map/components/overlays/Marker.d.ts +1 -1
- package/build/map/components/overlays/Marker.js +37 -32
- package/build/map/components/overlays/MultiPoint.js +1 -1
- package/build/map/components/overlays/Polygon.js +30 -3
- package/build/map/components/overlays/Polyline.js +36 -3
- package/build/map/index.d.ts +25 -5
- package/build/map/index.js +59 -18
- package/build/map/types/common.types.d.ts +40 -0
- package/build/map/types/common.types.js +0 -4
- package/build/map/types/index.d.ts +3 -2
- package/build/map/types/map-view.types.d.ts +108 -3
- package/build/map/types/native-module.types.d.ts +363 -0
- package/build/map/types/native-module.types.js +5 -0
- package/build/map/types/offline.types.d.ts +132 -0
- package/build/map/types/offline.types.js +5 -0
- package/build/map/types/overlays.types.d.ts +137 -24
- package/build/map/utils/ErrorHandler.d.ts +110 -0
- package/build/map/utils/ErrorHandler.js +421 -0
- package/build/map/utils/GeoUtils.d.ts +20 -0
- package/build/map/utils/GeoUtils.js +76 -0
- package/build/map/utils/OfflineMapManager.d.ts +148 -0
- package/build/map/utils/OfflineMapManager.js +217 -0
- package/build/map/utils/PermissionUtils.d.ts +91 -0
- package/build/map/utils/PermissionUtils.js +255 -0
- package/build/map/utils/PlatformDetector.d.ts +102 -0
- package/build/map/utils/PlatformDetector.js +186 -0
- package/build/types/index.d.ts +1 -0
- package/build/types/index.js +1 -0
- package/build/types/native-module.types.d.ts +69 -0
- package/build/types/native-module.types.js +2 -0
- package/build/types/naviview.types.d.ts +1 -1
- package/expo-module.config.json +12 -10
- package/ios/ExpoGaodeMapNavigation.podspec +9 -0
- package/ios/map/ExpoGaodeMapModule.swift +485 -75
- package/ios/map/ExpoGaodeMapOfflineModule.swift +479 -0
- package/ios/map/ExpoGaodeMapView.swift +611 -62
- package/ios/map/ExpoGaodeMapViewModule.swift +48 -26
- package/ios/map/MapPreloadManager.swift +348 -0
- package/ios/map/cpp/ClusterEngine.cpp +110 -0
- package/ios/map/cpp/ClusterEngine.hpp +20 -0
- package/ios/map/cpp/ColorParser.cpp +135 -0
- package/ios/map/cpp/ColorParser.hpp +14 -0
- package/ios/map/cpp/GeometryEngine.cpp +574 -0
- package/ios/map/cpp/GeometryEngine.hpp +159 -0
- package/ios/map/cpp/QuadTree.cpp +92 -0
- package/ios/map/cpp/QuadTree.hpp +42 -0
- package/ios/map/cpp/README.md +55 -0
- package/ios/map/managers/UIManager.swift +72 -1
- package/ios/map/modules/LocationManager.swift +123 -166
- package/ios/map/overlays/CircleView.swift +16 -32
- package/ios/map/overlays/CircleViewModule.swift +12 -12
- package/ios/map/overlays/ClusterAnnotation.swift +32 -0
- package/ios/map/overlays/ClusterView.swift +331 -45
- package/ios/map/overlays/ClusterViewModule.swift +20 -6
- package/ios/map/overlays/HeatMapView.swift +135 -32
- package/ios/map/overlays/HeatMapViewModule.swift +20 -8
- package/ios/map/overlays/MarkerView.swift +613 -130
- package/ios/map/overlays/MarkerViewModule.swift +38 -18
- package/ios/map/overlays/MultiPointView.swift +168 -10
- package/ios/map/overlays/MultiPointViewModule.swift +27 -5
- package/ios/map/overlays/PolygonView.swift +62 -23
- package/ios/map/overlays/PolygonViewModule.swift +18 -12
- package/ios/map/overlays/PolylineView.swift +21 -13
- package/ios/map/overlays/PolylineViewModule.swift +18 -12
- package/ios/map/utils/ClusterNative.h +96 -0
- package/ios/map/utils/ClusterNative.mm +377 -0
- package/ios/map/utils/ColorParser.swift +12 -1
- package/ios/map/utils/CppBridging.mm +13 -0
- package/ios/map/utils/GeometryUtils.swift +34 -0
- package/ios/map/utils/LatLngParser.swift +87 -0
- package/ios/map/utils/PermissionManager.swift +135 -6
- package/package.json +3 -2
- package/shared/cpp/ClusterEngine.cpp +110 -0
- package/shared/cpp/ClusterEngine.hpp +20 -0
- package/shared/cpp/ColorParser.cpp +135 -0
- package/shared/cpp/ColorParser.hpp +14 -0
- package/shared/cpp/GeometryEngine.cpp +574 -0
- package/shared/cpp/GeometryEngine.hpp +159 -0
- package/shared/cpp/QuadTree.cpp +92 -0
- package/shared/cpp/QuadTree.hpp +42 -0
- package/shared/cpp/README.md +55 -0
- package/shared/cpp/tests/benchmark_js.js +41 -0
- package/shared/cpp/tests/run.sh +17 -0
- package/shared/cpp/tests/test_main.cpp +276 -0
- package/build/map/ExpoGaodeMap.types.d.ts +0 -41
- package/build/map/ExpoGaodeMap.types.js +0 -24
- package/build/map/utils/EventManager.d.ts +0 -10
- package/build/map/utils/EventManager.js +0 -26
- package/build/map/utils/ModuleLoader.d.ts +0 -73
- 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
|
+
}
|