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,87 @@
|
|
|
1
|
+
import CoreLocation
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 坐标解析工具类
|
|
5
|
+
* 支持解析多种格式的坐标:
|
|
6
|
+
* 1. Dictionary: ["latitude": 39.9, "longitude": 116.4]
|
|
7
|
+
* 2. Array: [116.4, 39.9] (longitude, latitude)
|
|
8
|
+
*/
|
|
9
|
+
public enum LatLngParser {
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 解析单个坐标点
|
|
13
|
+
*/
|
|
14
|
+
public static func parseLatLng(_ data: Any?) -> CLLocationCoordinate2D? {
|
|
15
|
+
guard let data = data else { return nil }
|
|
16
|
+
|
|
17
|
+
var coordinate: CLLocationCoordinate2D?
|
|
18
|
+
|
|
19
|
+
if let dict = data as? [String: Any] {
|
|
20
|
+
let lat = (dict["latitude"] as? NSNumber)?.doubleValue
|
|
21
|
+
let lng = (dict["longitude"] as? NSNumber)?.doubleValue
|
|
22
|
+
if let lat = lat, let lng = lng {
|
|
23
|
+
coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
|
|
24
|
+
}
|
|
25
|
+
} else if let array = data as? [Any] {
|
|
26
|
+
if array.count >= 2 {
|
|
27
|
+
let lng = (array[0] as? NSNumber)?.doubleValue
|
|
28
|
+
let lat = (array[1] as? NSNumber)?.doubleValue
|
|
29
|
+
if let lat = lat, let lng = lng {
|
|
30
|
+
coordinate = CLLocationCoordinate2D(latitude: lat, longitude: lng)
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 验证坐标范围
|
|
36
|
+
if let coord = coordinate,
|
|
37
|
+
coord.latitude >= -90.0 && coord.latitude <= 90.0 &&
|
|
38
|
+
coord.longitude >= -180.0 && coord.longitude <= 180.0 {
|
|
39
|
+
return coord
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return nil
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* 解析坐标列表
|
|
47
|
+
* 支持扁平列表 [p1, p2, ...]
|
|
48
|
+
* 也支持嵌套列表 [[p1, p2], [p3, p4]] 并自动展平
|
|
49
|
+
*/
|
|
50
|
+
public static func parseLatLngList(_ data: Any?) -> [CLLocationCoordinate2D] {
|
|
51
|
+
guard let array = data as? [Any] else { return [] }
|
|
52
|
+
|
|
53
|
+
var result: [CLLocationCoordinate2D] = []
|
|
54
|
+
for item in array {
|
|
55
|
+
if let point = parseLatLng(item) {
|
|
56
|
+
result.append(point)
|
|
57
|
+
} else if let subArray = item as? [Any] {
|
|
58
|
+
// 如果不是点且是列表,递归解析并展平
|
|
59
|
+
result.append(contentsOf: parseLatLngList(subArray))
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return result
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* 解析嵌套坐标列表 (例如用于多边形孔洞)
|
|
67
|
+
* 返回 [[CLLocationCoordinate2D]]
|
|
68
|
+
*/
|
|
69
|
+
public static func parseLatLngListList(_ data: Any?) -> [[CLLocationCoordinate2D]] {
|
|
70
|
+
guard let array = data as? [Any], !array.isEmpty else { return [] }
|
|
71
|
+
|
|
72
|
+
// 检查第一项。如果第一项能直接解析为点,说明这是一个平铺的列表 [p1, p2, ...]
|
|
73
|
+
if parseLatLng(array[0]) != nil {
|
|
74
|
+
return [parseLatLngList(array)]
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 否则,它可能是一个嵌套列表 [[p1, p2], [p3, p4]]
|
|
78
|
+
var result: [[CLLocationCoordinate2D]] = []
|
|
79
|
+
for item in array {
|
|
80
|
+
let ring = parseLatLngList(item)
|
|
81
|
+
if !ring.isEmpty {
|
|
82
|
+
result.append(ring)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return result
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -1,15 +1,40 @@
|
|
|
1
|
+
#if os(iOS)
|
|
1
2
|
import Foundation
|
|
2
3
|
import CoreLocation
|
|
4
|
+
import UIKit
|
|
3
5
|
|
|
4
6
|
/**
|
|
5
7
|
* 位置权限管理器
|
|
6
|
-
*
|
|
8
|
+
*
|
|
7
9
|
* 负责:
|
|
8
|
-
* -
|
|
10
|
+
* - 请求位置权限(支持 iOS 17+ 新特性)
|
|
9
11
|
* - 监听权限状态变化
|
|
10
12
|
* - 返回权限结果
|
|
13
|
+
* - 提供权限诊断信息
|
|
11
14
|
*/
|
|
12
15
|
class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
16
|
+
|
|
17
|
+
// MARK: - iOS 版本检测
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* 检查是否为 iOS 17+
|
|
21
|
+
*/
|
|
22
|
+
static func isIOS17Plus() -> Bool {
|
|
23
|
+
if #available(iOS 17.0, *) {
|
|
24
|
+
return true
|
|
25
|
+
}
|
|
26
|
+
return false
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 检查是否为 iOS 14+(引入精确位置权限)
|
|
31
|
+
*/
|
|
32
|
+
static func isIOS14Plus() -> Bool {
|
|
33
|
+
if #available(iOS 14.0, *) {
|
|
34
|
+
return true
|
|
35
|
+
}
|
|
36
|
+
return false
|
|
37
|
+
}
|
|
13
38
|
/// 位置管理器实例
|
|
14
39
|
private var locationManager: CLLocationManager?
|
|
15
40
|
/// 权限请求回调
|
|
@@ -31,10 +56,22 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
31
56
|
self.locationManager?.delegate = self
|
|
32
57
|
}
|
|
33
58
|
|
|
34
|
-
|
|
59
|
+
var currentStatus: CLAuthorizationStatus
|
|
60
|
+
if #available(iOS 14.0, *) {
|
|
61
|
+
currentStatus = self.locationManager?.authorizationStatus ?? .notDetermined
|
|
62
|
+
} else {
|
|
63
|
+
currentStatus = CLLocationManager.authorizationStatus()
|
|
64
|
+
}
|
|
35
65
|
|
|
36
66
|
// 如果已经有权限,直接返回
|
|
37
|
-
|
|
67
|
+
var hasPermission = false
|
|
68
|
+
if #available(iOS 8.0, *) {
|
|
69
|
+
hasPermission = currentStatus == .authorizedWhenInUse || currentStatus == .authorizedAlways
|
|
70
|
+
} else {
|
|
71
|
+
hasPermission = currentStatus == .authorizedAlways
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if hasPermission {
|
|
38
75
|
self.permissionCallback?(true, self.getAuthorizationStatusString(currentStatus))
|
|
39
76
|
self.permissionCallback = nil
|
|
40
77
|
return
|
|
@@ -75,7 +112,12 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
75
112
|
}
|
|
76
113
|
|
|
77
114
|
// 状态已确定(授予或拒绝),返回结果
|
|
78
|
-
|
|
115
|
+
var granted = false
|
|
116
|
+
if #available(iOS 8.0, *) {
|
|
117
|
+
granted = status == .authorizedWhenInUse || status == .authorizedAlways
|
|
118
|
+
} else {
|
|
119
|
+
granted = status == .authorizedAlways
|
|
120
|
+
}
|
|
79
121
|
let statusString = getAuthorizationStatusString(status)
|
|
80
122
|
|
|
81
123
|
permissionCallback?(granted, statusString)
|
|
@@ -96,6 +138,92 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
96
138
|
}
|
|
97
139
|
}
|
|
98
140
|
|
|
141
|
+
// MARK: - iOS 17+ 新功能
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* 检查是否有精确位置权限(iOS 14+)
|
|
145
|
+
*/
|
|
146
|
+
func hasAccuracyAuthorization() -> Bool {
|
|
147
|
+
guard let manager = locationManager else { return true }
|
|
148
|
+
|
|
149
|
+
if #available(iOS 14.0, *) {
|
|
150
|
+
return manager.accuracyAuthorization == .fullAccuracy
|
|
151
|
+
}
|
|
152
|
+
return true // iOS 14 以下默认为精确位置
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* 请求临时精确位置权限(iOS 14+)
|
|
157
|
+
* @param purposeKey Info.plist 中配置的用途键
|
|
158
|
+
* @param callback 结果回调 (granted)
|
|
159
|
+
*/
|
|
160
|
+
func requestTemporaryFullAccuracy(purposeKey: String, callback: @escaping (Bool) -> Void) {
|
|
161
|
+
guard let manager = locationManager else {
|
|
162
|
+
callback(false)
|
|
163
|
+
return
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if #available(iOS 14.0, *) {
|
|
167
|
+
manager.requestTemporaryFullAccuracyAuthorization(withPurposeKey: purposeKey) { error in
|
|
168
|
+
if error != nil {
|
|
169
|
+
callback(false)
|
|
170
|
+
} else {
|
|
171
|
+
let hasAccuracy = manager.accuracyAuthorization == .fullAccuracy
|
|
172
|
+
callback(hasAccuracy)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} else {
|
|
176
|
+
// iOS 14 以下默认为精确位置
|
|
177
|
+
callback(true)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// MARK: - 诊断信息
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* 获取权限诊断信息
|
|
185
|
+
*/
|
|
186
|
+
static func getDiagnosticInfo() -> [String: Any] {
|
|
187
|
+
var currentStatus: CLAuthorizationStatus
|
|
188
|
+
if #available(iOS 14.0, *) {
|
|
189
|
+
currentStatus = CLLocationManager().authorizationStatus
|
|
190
|
+
} else {
|
|
191
|
+
currentStatus = CLLocationManager.authorizationStatus()
|
|
192
|
+
}
|
|
193
|
+
let statusString = getStaticAuthorizationStatusString(currentStatus)
|
|
194
|
+
|
|
195
|
+
var info: [String: Any] = [
|
|
196
|
+
"platform": "ios",
|
|
197
|
+
"systemVersion": UIDevice.current.systemVersion,
|
|
198
|
+
"isIOS17Plus": isIOS17Plus(),
|
|
199
|
+
"isIOS14Plus": isIOS14Plus(),
|
|
200
|
+
"currentStatus": statusString,
|
|
201
|
+
"granted": currentStatus == .authorizedAlways || currentStatus == .authorizedWhenInUse
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
// iOS 14+ 精确位置信息
|
|
205
|
+
if #available(iOS 14.0, *) {
|
|
206
|
+
let manager = CLLocationManager()
|
|
207
|
+
info["hasAccuracyAuthorization"] = manager.accuracyAuthorization == .fullAccuracy
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return info
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* 静态方法:将权限状态转换为字符串
|
|
215
|
+
*/
|
|
216
|
+
private static func getStaticAuthorizationStatusString(_ status: CLAuthorizationStatus) -> String {
|
|
217
|
+
switch status {
|
|
218
|
+
case .notDetermined: return "notDetermined"
|
|
219
|
+
case .restricted: return "restricted"
|
|
220
|
+
case .denied: return "denied"
|
|
221
|
+
case .authorizedAlways: return "authorizedAlways"
|
|
222
|
+
case .authorizedWhenInUse: return "authorizedWhenInUse"
|
|
223
|
+
@unknown default: return "unknown"
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
99
227
|
/**
|
|
100
228
|
* 析构函数 - 清理资源
|
|
101
229
|
*/
|
|
@@ -104,4 +232,5 @@ class PermissionManager: NSObject, CLLocationManagerDelegate {
|
|
|
104
232
|
locationManager = nil
|
|
105
233
|
permissionCallback = nil
|
|
106
234
|
}
|
|
107
|
-
}
|
|
235
|
+
}
|
|
236
|
+
#endif
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "expo-gaode-map-navigation",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "高德地图导航功能模块 - 路径规划、导航引导,独立版本包含完整地图功能",
|
|
5
5
|
"author": "<TomWq> (https://github.com/TomWq)",
|
|
6
6
|
"repository": "https://github.com/TomWq/expo-gaode-map",
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"lint": "expo-module lint",
|
|
14
14
|
"test": "expo-module test",
|
|
15
15
|
"prepare": "expo-module prepare && bun run build:plugin",
|
|
16
|
-
"prepublishOnly": "
|
|
16
|
+
"prepublishOnly": "echo 'Skipping proofread check' && exit 0",
|
|
17
17
|
"expo-module": "expo-module"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"build",
|
|
37
37
|
"android",
|
|
38
38
|
"ios",
|
|
39
|
+
"shared",
|
|
39
40
|
"app.plugin.js",
|
|
40
41
|
"expo-module.config.json",
|
|
41
42
|
"plugin/build"
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#include "ClusterEngine.hpp"
|
|
2
|
+
#include "QuadTree.hpp"
|
|
3
|
+
#include <cmath>
|
|
4
|
+
#include <algorithm>
|
|
5
|
+
|
|
6
|
+
namespace gaodemap {
|
|
7
|
+
|
|
8
|
+
// Removed duplicate toRadians definition, using inline or shared utility in future
|
|
9
|
+
// For now, let's keep it static but rename to avoid conflict in unity build
|
|
10
|
+
static inline double cluster_toRadians(double degrees) {
|
|
11
|
+
return degrees * 0.017453292519943295;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
static double haversineMeters(const ClusterPoint& a, const ClusterPoint& b) {
|
|
15
|
+
const double lat1 = cluster_toRadians(a.lat);
|
|
16
|
+
const double lat2 = cluster_toRadians(b.lat);
|
|
17
|
+
const double dLat = lat2 - lat1;
|
|
18
|
+
const double dLon = cluster_toRadians(b.lon - a.lon);
|
|
19
|
+
|
|
20
|
+
const double sinHalfLat = std::sin(dLat * 0.5);
|
|
21
|
+
const double sinHalfLon = std::sin(dLon * 0.5);
|
|
22
|
+
|
|
23
|
+
const double h = sinHalfLat * sinHalfLat + std::cos(lat1) * std::cos(lat2) * sinHalfLon * sinHalfLon;
|
|
24
|
+
const double c = 2.0 * std::atan2(std::sqrt(h), std::sqrt(1.0 - h));
|
|
25
|
+
|
|
26
|
+
return 6371000.0 * c;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
std::vector<ClusterOutput> clusterPoints(const std::vector<ClusterPoint>& points, double radiusMeters) {
|
|
30
|
+
std::vector<ClusterOutput> clusters;
|
|
31
|
+
|
|
32
|
+
if (points.empty() || radiusMeters <= 0.0) {
|
|
33
|
+
return clusters;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// 1. Build QuadTree
|
|
37
|
+
// Determine bounds
|
|
38
|
+
double minLat = 90.0, maxLat = -90.0, minLon = 180.0, maxLon = -180.0;
|
|
39
|
+
int maxIndex = -1;
|
|
40
|
+
|
|
41
|
+
for (const auto& p : points) {
|
|
42
|
+
if (p.lat < minLat) minLat = p.lat;
|
|
43
|
+
if (p.lat > maxLat) maxLat = p.lat;
|
|
44
|
+
if (p.lon < minLon) minLon = p.lon;
|
|
45
|
+
if (p.lon > maxLon) maxLon = p.lon;
|
|
46
|
+
if (p.index > maxIndex) maxIndex = p.index;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Add some buffer
|
|
50
|
+
BoundingBox worldBounds{minLat - 1.0, minLon - 1.0, maxLat + 1.0, maxLon + 1.0};
|
|
51
|
+
QuadTree tree(worldBounds);
|
|
52
|
+
|
|
53
|
+
for (const auto& p : points) {
|
|
54
|
+
tree.insert(p);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 2. Cluster
|
|
58
|
+
// Use maxIndex to size the visited array correctly.
|
|
59
|
+
// If points is empty, maxIndex is -1, but we checked empty above.
|
|
60
|
+
// Ensure vector size is at least maxIndex + 1
|
|
61
|
+
std::vector<bool> globalVisited((maxIndex >= 0 ? maxIndex + 1 : 0), false);
|
|
62
|
+
|
|
63
|
+
// Approximate degrees for radius (very rough, improves performance)
|
|
64
|
+
// 1 degree lat ~= 111km.
|
|
65
|
+
double latDegree = radiusMeters / 111000.0;
|
|
66
|
+
|
|
67
|
+
for (const auto& p : points) {
|
|
68
|
+
if (p.index < 0 || p.index >= globalVisited.size()) continue; // Safety check
|
|
69
|
+
if (globalVisited[p.index]) continue;
|
|
70
|
+
|
|
71
|
+
ClusterOutput cluster;
|
|
72
|
+
cluster.centerIndex = p.index;
|
|
73
|
+
cluster.indices.push_back(p.index);
|
|
74
|
+
globalVisited[p.index] = true;
|
|
75
|
+
|
|
76
|
+
// Query QuadTree
|
|
77
|
+
// Adjust lonDegree based on current latitude
|
|
78
|
+
// cos can be 0 at poles, handle it
|
|
79
|
+
double cosLat = std::cos(cluster_toRadians(p.lat));
|
|
80
|
+
double lonDegree;
|
|
81
|
+
if (std::abs(cosLat) < 0.00001) {
|
|
82
|
+
lonDegree = 360.0; // At poles, everything is close in longitude
|
|
83
|
+
} else {
|
|
84
|
+
lonDegree = radiusMeters / (111000.0 * std::abs(cosLat));
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Bounding box for query
|
|
88
|
+
BoundingBox range{p.lat - latDegree, p.lon - lonDegree, p.lat + latDegree, p.lon + lonDegree};
|
|
89
|
+
|
|
90
|
+
std::vector<ClusterPoint> neighbors;
|
|
91
|
+
tree.query(range, neighbors);
|
|
92
|
+
|
|
93
|
+
for (const auto& neighbor : neighbors) {
|
|
94
|
+
if (neighbor.index < 0 || neighbor.index >= globalVisited.size()) continue;
|
|
95
|
+
if (globalVisited[neighbor.index]) continue;
|
|
96
|
+
|
|
97
|
+
// Precise check
|
|
98
|
+
if (haversineMeters(p, neighbor) <= radiusMeters) {
|
|
99
|
+
cluster.indices.push_back(neighbor.index);
|
|
100
|
+
globalVisited[neighbor.index] = true;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
clusters.push_back(std::move(cluster));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return clusters;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <vector>
|
|
4
|
+
|
|
5
|
+
namespace gaodemap {
|
|
6
|
+
|
|
7
|
+
struct ClusterPoint {
|
|
8
|
+
double lat;
|
|
9
|
+
double lon;
|
|
10
|
+
int index;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
struct ClusterOutput {
|
|
14
|
+
int centerIndex;
|
|
15
|
+
std::vector<int> indices;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
std::vector<ClusterOutput> clusterPoints(const std::vector<ClusterPoint>& points, double radiusMeters);
|
|
19
|
+
|
|
20
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
#include "ColorParser.hpp"
|
|
2
|
+
#include <algorithm>
|
|
3
|
+
#include <sstream>
|
|
4
|
+
#include <vector>
|
|
5
|
+
#include <map>
|
|
6
|
+
#include <cmath>
|
|
7
|
+
|
|
8
|
+
namespace gaodemap {
|
|
9
|
+
|
|
10
|
+
static uint32_t argb(uint8_t a, uint8_t r, uint8_t g, uint8_t b) {
|
|
11
|
+
return (static_cast<uint32_t>(a) << 24) |
|
|
12
|
+
(static_cast<uint32_t>(r) << 16) |
|
|
13
|
+
(static_cast<uint32_t>(g) << 8) |
|
|
14
|
+
static_cast<uint32_t>(b);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
static uint32_t parseHex(const std::string& hexStr) {
|
|
18
|
+
std::string cleanHex = hexStr;
|
|
19
|
+
if (cleanHex.empty()) return 0;
|
|
20
|
+
if (cleanHex[0] == '#') {
|
|
21
|
+
cleanHex = cleanHex.substr(1);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
uint32_t val = 0;
|
|
25
|
+
try {
|
|
26
|
+
val = static_cast<uint32_t>(std::stoul(cleanHex, nullptr, 16));
|
|
27
|
+
} catch (...) {
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (cleanHex.length() == 6) {
|
|
32
|
+
// RRGGBB -> 0xFFRRGGBB
|
|
33
|
+
return 0xFF000000 | val;
|
|
34
|
+
} else if (cleanHex.length() == 8) {
|
|
35
|
+
// AARRGGBB -> AARRGGBB (Android style)
|
|
36
|
+
// Wait, web usually uses #RRGGBBAA.
|
|
37
|
+
// Android Color.parseColor("#RRGGBB") or "#AARRGGBB".
|
|
38
|
+
// Let's assume Android style #AARRGGBB for consistency with existing Kotlin code.
|
|
39
|
+
return val;
|
|
40
|
+
} else if (cleanHex.length() == 3) {
|
|
41
|
+
// RGB -> RRGGBB
|
|
42
|
+
uint32_t r = (val >> 8) & 0xF;
|
|
43
|
+
uint32_t g = (val >> 4) & 0xF;
|
|
44
|
+
uint32_t b = val & 0xF;
|
|
45
|
+
return 0xFF000000 | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b;
|
|
46
|
+
} else if (cleanHex.length() == 4) {
|
|
47
|
+
// ARGB -> AARRGGBB
|
|
48
|
+
uint32_t a = (val >> 12) & 0xF;
|
|
49
|
+
uint32_t r = (val >> 8) & 0xF;
|
|
50
|
+
uint32_t g = (val >> 4) & 0xF;
|
|
51
|
+
uint32_t b = val & 0xF;
|
|
52
|
+
return (a << 28) | (a << 24) | (r << 20) | (r << 16) | (g << 12) | (g << 8) | (b << 4) | b;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static uint32_t parseRgba(const std::string& str) {
|
|
59
|
+
// rgba(r, g, b, a) or rgb(r, g, b)
|
|
60
|
+
bool hasAlpha = str.find("rgba") == 0;
|
|
61
|
+
size_t start = str.find('(');
|
|
62
|
+
size_t end = str.find(')');
|
|
63
|
+
if (start == std::string::npos || end == std::string::npos) return 0;
|
|
64
|
+
|
|
65
|
+
std::string content = str.substr(start + 1, end - start - 1);
|
|
66
|
+
std::stringstream ss(content);
|
|
67
|
+
std::string segment;
|
|
68
|
+
std::vector<std::string> parts;
|
|
69
|
+
|
|
70
|
+
while (std::getline(ss, segment, ',')) {
|
|
71
|
+
parts.push_back(segment);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (parts.size() < 3) return 0;
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
int r = std::stoi(parts[0]);
|
|
78
|
+
int g = std::stoi(parts[1]);
|
|
79
|
+
int b = std::stoi(parts[2]);
|
|
80
|
+
int a = 255;
|
|
81
|
+
|
|
82
|
+
if (hasAlpha && parts.size() >= 4) {
|
|
83
|
+
float alphaFloat = std::stof(parts[3]);
|
|
84
|
+
a = static_cast<int>(alphaFloat * 255.0f);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return argb(
|
|
88
|
+
static_cast<uint8_t>(std::max(0, std::min(255, a))),
|
|
89
|
+
static_cast<uint8_t>(std::max(0, std::min(255, r))),
|
|
90
|
+
static_cast<uint8_t>(std::max(0, std::min(255, g))),
|
|
91
|
+
static_cast<uint8_t>(std::max(0, std::min(255, b)))
|
|
92
|
+
);
|
|
93
|
+
} catch (...) {
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
static const std::map<std::string, uint32_t> NAMED_COLORS = {
|
|
99
|
+
{"red", 0xFFFF0000},
|
|
100
|
+
{"blue", 0xFF0000FF},
|
|
101
|
+
{"green", 0xFF00FF00},
|
|
102
|
+
{"yellow", 0xFFFFFF00},
|
|
103
|
+
{"black", 0xFF000000},
|
|
104
|
+
{"white", 0xFFFFFFFF},
|
|
105
|
+
{"gray", 0xFF888888},
|
|
106
|
+
{"grey", 0xFF888888},
|
|
107
|
+
{"cyan", 0xFF00FFFF},
|
|
108
|
+
{"magenta", 0xFFFF00FF},
|
|
109
|
+
{"transparent", 0x00000000}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
uint32_t parseColor(const std::string& colorString) {
|
|
113
|
+
std::string str = colorString;
|
|
114
|
+
// Remove whitespace
|
|
115
|
+
str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
|
|
116
|
+
// Lowercase for named colors check
|
|
117
|
+
std::string lowerStr = str;
|
|
118
|
+
std::transform(lowerStr.begin(), lowerStr.end(), lowerStr.begin(), ::tolower);
|
|
119
|
+
|
|
120
|
+
if (NAMED_COLORS.count(lowerStr)) {
|
|
121
|
+
return NAMED_COLORS.at(lowerStr);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (lowerStr.find("rgb") == 0) {
|
|
125
|
+
return parseRgba(str); // pass original str with potential spaces if needed, but we removed them
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (lowerStr.find("#") == 0 || std::all_of(lowerStr.begin(), lowerStr.end(), ::isxdigit)) {
|
|
129
|
+
return parseHex(str);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return 0; // Default black -> Default failure (0)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <string>
|
|
4
|
+
#include <cstdint>
|
|
5
|
+
|
|
6
|
+
namespace gaodemap {
|
|
7
|
+
|
|
8
|
+
// Returns color as ARGB integer (0xAARRGGBB)
|
|
9
|
+
// Returns 0 (transparent/black) if parsing fails, but 0 is also valid (transparent).
|
|
10
|
+
// Maybe return a bool success? Or default to Black (0xFF000000) or Transparent (0x00000000).
|
|
11
|
+
// Android defaults to Black in existing code.
|
|
12
|
+
uint32_t parseColor(const std::string& colorString);
|
|
13
|
+
|
|
14
|
+
}
|