expo-gaode-map 0.1.6 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PUBLISHING.md +1 -1
- package/README.md +68 -27
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +117 -26
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +97 -36
- package/android/src/main/java/expo/modules/gaodemap/managers/CameraManager.kt +27 -21
- package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +4 -23
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +30 -27
- package/android/src/main/java/expo/modules/gaodemap/modules/LocationManager.kt +49 -4
- package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +13 -5
- package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +0 -6
- package/build/ExpoGaodeMapView.js +2 -2
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +3 -1
- package/build/index.js.map +1 -1
- package/build/modules/AMapPermissions.d.ts +27 -0
- package/build/modules/AMapPermissions.d.ts.map +1 -0
- package/build/modules/AMapPermissions.js +31 -0
- package/build/modules/AMapPermissions.js.map +1 -0
- package/build/modules/AMapView.d.ts +7 -2
- package/build/modules/AMapView.d.ts.map +1 -1
- package/build/modules/AMapView.js +15 -3
- package/build/modules/AMapView.js.map +1 -1
- package/build/types/common.types.d.ts +1 -0
- package/build/types/common.types.d.ts.map +1 -1
- package/build/types/common.types.js +1 -0
- package/build/types/common.types.js.map +1 -1
- package/docs/API.md +5 -1
- package/docs/ARCHITECTURE.md +421 -0
- package/docs/EXAMPLES.md +166 -24
- package/docs/INITIALIZATION.md +335 -0
- package/ios/ExpoGaodeMapModule.swift +95 -9
- package/ios/ExpoGaodeMapView.swift +88 -6
- package/ios/managers/CameraManager.swift +58 -0
- package/ios/managers/OverlayManager.swift +105 -29
- package/ios/managers/UIManager.swift +73 -1
- package/ios/modules/LocationManager.swift +109 -3
- package/ios/overlays/CircleView.swift +53 -0
- package/ios/overlays/HeatMapView.swift +27 -0
- package/ios/overlays/MarkerView.swift +29 -1
- package/ios/overlays/PolygonView.swift +51 -0
- package/ios/overlays/PolylineView.swift +61 -14
- package/ios/utils/PermissionManager.swift +58 -0
- package/package.json +1 -1
- package/src/ExpoGaodeMapView.tsx +2 -2
- package/src/index.ts +9 -1
- package/src/modules/AMapPermissions.ts +48 -0
- package/src/modules/AMapView.ts +15 -3
- package/src/types/common.types.ts +1 -0
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
2
|
import MAMapKit
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 高德地图视图组件
|
|
6
|
+
*
|
|
7
|
+
* 负责:
|
|
8
|
+
* - 地图视图的创建和管理
|
|
9
|
+
* - 相机控制和手势交互
|
|
10
|
+
* - 覆盖物的添加和管理
|
|
11
|
+
* - 地图事件的派发
|
|
12
|
+
*/
|
|
4
13
|
class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
14
|
+
// MARK: - 属性
|
|
15
|
+
|
|
16
|
+
/// 地图类型 (0:标准 1:卫星 2:夜间 3:导航)
|
|
5
17
|
var mapType: Int = 0
|
|
18
|
+
/// 初始相机位置
|
|
6
19
|
var initialCameraPosition: [String: Any]?
|
|
20
|
+
/// 是否显示缩放控件
|
|
7
21
|
var showsZoomControls: Bool = true
|
|
22
|
+
/// 是否显示指南针
|
|
8
23
|
var showsCompass: Bool = true
|
|
24
|
+
/// 是否显示比例尺
|
|
9
25
|
var showsScale: Bool = true
|
|
26
|
+
/// 是否启用缩放手势
|
|
10
27
|
var isZoomEnabled: Bool = true
|
|
28
|
+
/// 是否启用滚动手势
|
|
11
29
|
var isScrollEnabled: Bool = true
|
|
30
|
+
/// 是否启用旋转手势
|
|
12
31
|
var isRotateEnabled: Bool = true
|
|
32
|
+
/// 是否启用倾斜手势
|
|
13
33
|
var isTiltEnabled: Bool = true
|
|
34
|
+
/// 是否显示用户位置
|
|
14
35
|
var showsUserLocation: Bool = false
|
|
36
|
+
/// 是否跟随用户位置
|
|
15
37
|
var followUserLocation: Bool = false {
|
|
16
38
|
didSet {
|
|
17
39
|
if showsUserLocation {
|
|
@@ -19,23 +41,40 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
19
41
|
}
|
|
20
42
|
}
|
|
21
43
|
}
|
|
44
|
+
/// 用户位置样式配置
|
|
22
45
|
var userLocationRepresentation: [String: Any]?
|
|
46
|
+
/// 是否显示交通路况
|
|
23
47
|
var showsTraffic: Bool = false
|
|
48
|
+
/// 是否显示建筑物
|
|
24
49
|
var showsBuildings: Bool = false
|
|
50
|
+
/// 是否显示室内地图
|
|
25
51
|
var showsIndoorMap: Bool = false
|
|
52
|
+
/// 最大缩放级别
|
|
26
53
|
var maxZoomLevel: CGFloat = 20
|
|
54
|
+
/// 最小缩放级别
|
|
27
55
|
var minZoomLevel: CGFloat = 3
|
|
28
56
|
|
|
57
|
+
// MARK: - 事件派发器
|
|
58
|
+
|
|
29
59
|
let onMapPress = EventDispatcher()
|
|
30
60
|
let onMapLongPress = EventDispatcher()
|
|
31
61
|
let onLoad = EventDispatcher()
|
|
32
62
|
|
|
63
|
+
// MARK: - 私有属性
|
|
64
|
+
|
|
65
|
+
/// 高德地图视图实例
|
|
33
66
|
private var mapView: MAMapView!
|
|
67
|
+
/// 相机管理器
|
|
34
68
|
private var cameraManager: CameraManager!
|
|
69
|
+
/// UI 管理器
|
|
35
70
|
private var uiManager: UIManager!
|
|
71
|
+
/// 覆盖物管理器
|
|
36
72
|
private var overlayManager: OverlayManager!
|
|
73
|
+
/// 地图是否已加载完成
|
|
37
74
|
private var isMapLoaded = false
|
|
38
75
|
|
|
76
|
+
// MARK: - 初始化
|
|
77
|
+
|
|
39
78
|
required init(appContext: AppContext? = nil) {
|
|
40
79
|
super.init(appContext: appContext)
|
|
41
80
|
|
|
@@ -60,10 +99,13 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
60
99
|
mapView.frame = bounds
|
|
61
100
|
}
|
|
62
101
|
|
|
102
|
+
/**
|
|
103
|
+
* 添加子视图时自动连接到地图
|
|
104
|
+
* 将地图实例传递给覆盖物子视图
|
|
105
|
+
*/
|
|
63
106
|
override func addSubview(_ view: UIView) {
|
|
64
107
|
super.addSubview(view)
|
|
65
108
|
|
|
66
|
-
// 自动将地图实例传递给覆盖物子视图
|
|
67
109
|
if let markerView = view as? MarkerView {
|
|
68
110
|
markerView.setMap(mapView)
|
|
69
111
|
} else if let circleView = view as? CircleView {
|
|
@@ -81,6 +123,9 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
81
123
|
}
|
|
82
124
|
}
|
|
83
125
|
|
|
126
|
+
/**
|
|
127
|
+
* 设置默认配置
|
|
128
|
+
*/
|
|
84
129
|
private func setupDefaultConfig() {
|
|
85
130
|
uiManager.setMapType(0)
|
|
86
131
|
uiManager.setShowsScale(showsScale)
|
|
@@ -92,6 +137,10 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
92
137
|
uiManager.setShowsUserLocation(showsUserLocation, followUser: followUserLocation)
|
|
93
138
|
}
|
|
94
139
|
|
|
140
|
+
/**
|
|
141
|
+
* 应用所有属性配置
|
|
142
|
+
* 在 Props 更新时调用
|
|
143
|
+
*/
|
|
95
144
|
func applyProps() {
|
|
96
145
|
uiManager.setMapType(mapType)
|
|
97
146
|
|
|
@@ -111,6 +160,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
111
160
|
uiManager.setShowsIndoorMap(showsIndoorMap)
|
|
112
161
|
}
|
|
113
162
|
|
|
163
|
+
// MARK: - 缩放控制
|
|
164
|
+
|
|
114
165
|
func setMaxZoom(_ maxZoom: Double) {
|
|
115
166
|
cameraManager.setMaxZoomLevel(CGFloat(maxZoom))
|
|
116
167
|
}
|
|
@@ -119,6 +170,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
119
170
|
cameraManager.setMinZoomLevel(CGFloat(minZoom))
|
|
120
171
|
}
|
|
121
172
|
|
|
173
|
+
// MARK: - 相机控制
|
|
174
|
+
|
|
122
175
|
func moveCamera(position: [String: Any], duration: Int) {
|
|
123
176
|
cameraManager.moveCamera(position: position, duration: duration)
|
|
124
177
|
}
|
|
@@ -139,6 +192,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
139
192
|
return cameraManager.getCameraPosition()
|
|
140
193
|
}
|
|
141
194
|
|
|
195
|
+
// MARK: - 覆盖物管理
|
|
196
|
+
|
|
142
197
|
func addCircle(id: String, props: [String: Any]) {
|
|
143
198
|
overlayManager.addCircle(id: id, props: props)
|
|
144
199
|
}
|
|
@@ -187,6 +242,8 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
187
242
|
overlayManager.updatePolygon(id: id, props: props)
|
|
188
243
|
}
|
|
189
244
|
|
|
245
|
+
// MARK: - 图层控制
|
|
246
|
+
|
|
190
247
|
func setShowsTraffic(_ show: Bool) {
|
|
191
248
|
showsTraffic = show
|
|
192
249
|
uiManager.setShowsTraffic(show)
|
|
@@ -204,7 +261,6 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
204
261
|
|
|
205
262
|
func setFollowUserLocation(_ follow: Bool) {
|
|
206
263
|
followUserLocation = follow
|
|
207
|
-
// 如果定位已开启,立即应用新设置
|
|
208
264
|
uiManager.setShowsUserLocation(showsUserLocation, followUser: follow)
|
|
209
265
|
}
|
|
210
266
|
|
|
@@ -223,13 +279,29 @@ class ExpoGaodeMapView: ExpoView, MAMapViewDelegate {
|
|
|
223
279
|
}
|
|
224
280
|
}
|
|
225
281
|
|
|
282
|
+
/**
|
|
283
|
+
* 应用用户位置样式
|
|
284
|
+
*/
|
|
226
285
|
private func applyUserLocationStyle() {
|
|
227
286
|
guard let config = userLocationRepresentation else { return }
|
|
228
287
|
uiManager.setUserLocationRepresentation(config)
|
|
229
288
|
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* 析构函数 - 清理资源
|
|
292
|
+
*/
|
|
293
|
+
deinit {
|
|
294
|
+
mapView?.delegate = nil
|
|
295
|
+
overlayManager?.clear()
|
|
296
|
+
}
|
|
230
297
|
}
|
|
231
298
|
|
|
299
|
+
// MARK: - MAMapViewDelegate
|
|
300
|
+
|
|
232
301
|
extension ExpoGaodeMapView {
|
|
302
|
+
/**
|
|
303
|
+
* 地图加载完成回调
|
|
304
|
+
*/
|
|
233
305
|
public func mapViewDidFinishLoadingMap(_ mapView: MAMapView) {
|
|
234
306
|
guard !isMapLoaded else { return }
|
|
235
307
|
isMapLoaded = true
|
|
@@ -239,16 +311,25 @@ extension ExpoGaodeMapView {
|
|
|
239
311
|
onLoad(["loaded": true])
|
|
240
312
|
}
|
|
241
313
|
|
|
314
|
+
/**
|
|
315
|
+
* 地图单击事件
|
|
316
|
+
*/
|
|
242
317
|
public func mapView(_ mapView: MAMapView, didSingleTappedAt coordinate: CLLocationCoordinate2D) {
|
|
243
318
|
onMapPress(["latitude": coordinate.latitude, "longitude": coordinate.longitude])
|
|
244
319
|
}
|
|
245
320
|
|
|
321
|
+
/**
|
|
322
|
+
* 地图长按事件
|
|
323
|
+
*/
|
|
246
324
|
public func mapView(_ mapView: MAMapView, didLongPressedAt coordinate: CLLocationCoordinate2D) {
|
|
247
325
|
onMapLongPress(["latitude": coordinate.latitude, "longitude": coordinate.longitude])
|
|
248
326
|
}
|
|
249
327
|
|
|
328
|
+
/**
|
|
329
|
+
* 创建标注视图
|
|
330
|
+
* 定位蓝点返回 nil 使用系统默认样式
|
|
331
|
+
*/
|
|
250
332
|
public func mapView(_ mapView: MAMapView, viewFor annotation: MAAnnotation) -> MAAnnotationView? {
|
|
251
|
-
// 定位蓝点返回 nil,使用系统默认样式
|
|
252
333
|
if annotation.isKind(of: MAUserLocation.self) {
|
|
253
334
|
return nil
|
|
254
335
|
}
|
|
@@ -265,8 +346,11 @@ extension ExpoGaodeMapView {
|
|
|
265
346
|
return nil
|
|
266
347
|
}
|
|
267
348
|
|
|
349
|
+
/**
|
|
350
|
+
* 创建覆盖物渲染器
|
|
351
|
+
* 优先使用子视图的渲染器,否则使用 OverlayManager 的渲染器
|
|
352
|
+
*/
|
|
268
353
|
public func mapView(_ mapView: MAMapView, rendererFor overlay: MAOverlay) -> MAOverlayRenderer {
|
|
269
|
-
// 首先检查是否是通过子视图添加的覆盖物
|
|
270
354
|
for subview in subviews {
|
|
271
355
|
if let circleView = subview as? CircleView, circleView.circle === overlay {
|
|
272
356
|
return circleView.getRenderer()
|
|
@@ -277,8 +361,6 @@ extension ExpoGaodeMapView {
|
|
|
277
361
|
}
|
|
278
362
|
}
|
|
279
363
|
|
|
280
|
-
// 否则使用 OverlayManager 的渲染器
|
|
281
364
|
return overlayManager.getRenderer(for: overlay) ?? MAOverlayRenderer(overlay: overlay)
|
|
282
365
|
}
|
|
283
366
|
}
|
|
284
|
-
|
|
@@ -1,21 +1,47 @@
|
|
|
1
1
|
import ExpoModulesCore
|
|
2
2
|
import MAMapKit
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* 相机管理器
|
|
6
|
+
*
|
|
7
|
+
* 负责:
|
|
8
|
+
* - 相机位置和视角控制
|
|
9
|
+
* - 缩放级别管理
|
|
10
|
+
* - 坐标转换
|
|
11
|
+
*/
|
|
4
12
|
class CameraManager {
|
|
13
|
+
/// 弱引用地图视图,避免循环引用
|
|
5
14
|
private weak var mapView: MAMapView?
|
|
6
15
|
|
|
7
16
|
init(mapView: MAMapView) {
|
|
8
17
|
self.mapView = mapView
|
|
9
18
|
}
|
|
10
19
|
|
|
20
|
+
// MARK: - 缩放级别控制
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* 设置最大缩放级别
|
|
24
|
+
* @param maxZoom 最大缩放级别 (3-20)
|
|
25
|
+
*/
|
|
11
26
|
func setMaxZoomLevel(_ maxZoom: CGFloat) {
|
|
12
27
|
mapView?.maxZoomLevel = maxZoom
|
|
13
28
|
}
|
|
14
29
|
|
|
30
|
+
/**
|
|
31
|
+
* 设置最小缩放级别
|
|
32
|
+
* @param minZoom 最小缩放级别 (3-20)
|
|
33
|
+
*/
|
|
15
34
|
func setMinZoomLevel(_ minZoom: CGFloat) {
|
|
16
35
|
mapView?.minZoomLevel = minZoom
|
|
17
36
|
}
|
|
18
37
|
|
|
38
|
+
// MARK: - 相机位置控制
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 设置初始相机位置
|
|
42
|
+
* 不带动画,用于地图初始化
|
|
43
|
+
* @param position 包含 target、zoom、bearing、tilt 的字典
|
|
44
|
+
*/
|
|
19
45
|
func setInitialCameraPosition(_ position: [String: Any]) {
|
|
20
46
|
guard let mapView = mapView else { return }
|
|
21
47
|
let status = MAMapStatus()
|
|
@@ -41,10 +67,18 @@ class CameraManager {
|
|
|
41
67
|
mapView.setMapStatus(status, animated: false, duration: 0)
|
|
42
68
|
}
|
|
43
69
|
|
|
70
|
+
/**
|
|
71
|
+
* 移动相机到指定位置
|
|
72
|
+
* 支持动画效果
|
|
73
|
+
* @param position 目标位置信息
|
|
74
|
+
* @param duration 动画时长(毫秒)
|
|
75
|
+
*/
|
|
44
76
|
func moveCamera(position: [String: Any], duration: Int) {
|
|
45
77
|
guard let mapView = mapView else { return }
|
|
78
|
+
|
|
46
79
|
let status = MAMapStatus()
|
|
47
80
|
|
|
81
|
+
// 设置中心点,如果未提供则保持当前值
|
|
48
82
|
if let target = position["target"] as? [String: Double],
|
|
49
83
|
let latitude = target["latitude"],
|
|
50
84
|
let longitude = target["longitude"] {
|
|
@@ -53,18 +87,21 @@ class CameraManager {
|
|
|
53
87
|
status.centerCoordinate = mapView.centerCoordinate
|
|
54
88
|
}
|
|
55
89
|
|
|
90
|
+
// 设置缩放级别
|
|
56
91
|
if let zoom = position["zoom"] as? Double {
|
|
57
92
|
status.zoomLevel = CGFloat(zoom)
|
|
58
93
|
} else {
|
|
59
94
|
status.zoomLevel = mapView.zoomLevel
|
|
60
95
|
}
|
|
61
96
|
|
|
97
|
+
// 设置旋转角度
|
|
62
98
|
if let bearing = position["bearing"] as? Double {
|
|
63
99
|
status.rotationDegree = CGFloat(bearing)
|
|
64
100
|
} else {
|
|
65
101
|
status.rotationDegree = mapView.rotationDegree
|
|
66
102
|
}
|
|
67
103
|
|
|
104
|
+
// 设置倾斜角度
|
|
68
105
|
if let tilt = position["tilt"] as? Double {
|
|
69
106
|
status.cameraDegree = CGFloat(tilt)
|
|
70
107
|
} else {
|
|
@@ -74,6 +111,11 @@ class CameraManager {
|
|
|
74
111
|
mapView.setMapStatus(status, animated: duration > 0, duration: TimeInterval(duration) / 1000.0)
|
|
75
112
|
}
|
|
76
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 设置地图中心点
|
|
116
|
+
* @param center 中心点坐标
|
|
117
|
+
* @param animated 是否使用动画
|
|
118
|
+
*/
|
|
77
119
|
func setCenter(center: [String: Double], animated: Bool) {
|
|
78
120
|
guard let mapView = mapView,
|
|
79
121
|
let latitude = center["latitude"],
|
|
@@ -81,10 +123,21 @@ class CameraManager {
|
|
|
81
123
|
mapView.setCenter(CLLocationCoordinate2D(latitude: latitude, longitude: longitude), animated: animated)
|
|
82
124
|
}
|
|
83
125
|
|
|
126
|
+
/**
|
|
127
|
+
* 设置缩放级别
|
|
128
|
+
* @param zoom 缩放级别 (3-20)
|
|
129
|
+
* @param animated 是否使用动画
|
|
130
|
+
*/
|
|
84
131
|
func setZoomLevel(zoom: CGFloat, animated: Bool) {
|
|
85
132
|
mapView?.setZoomLevel(zoom, animated: animated)
|
|
86
133
|
}
|
|
87
134
|
|
|
135
|
+
// MARK: - 相机信息获取
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* 获取当前相机位置信息
|
|
139
|
+
* @return 包含 target、zoom、bearing、tilt 的字典
|
|
140
|
+
*/
|
|
88
141
|
func getCameraPosition() -> [String: Any] {
|
|
89
142
|
guard let mapView = mapView else { return [:] }
|
|
90
143
|
let center = mapView.centerCoordinate
|
|
@@ -96,6 +149,11 @@ class CameraManager {
|
|
|
96
149
|
]
|
|
97
150
|
}
|
|
98
151
|
|
|
152
|
+
/**
|
|
153
|
+
* 将屏幕坐标转换为地理坐标
|
|
154
|
+
* @param point 屏幕坐标点 {x, y}
|
|
155
|
+
* @return 地理坐标 {latitude, longitude}
|
|
156
|
+
*/
|
|
99
157
|
func getLatLng(point: [String: Double]) -> [String: Double] {
|
|
100
158
|
guard let mapView = mapView,
|
|
101
159
|
let x = point["x"],
|
|
@@ -1,17 +1,39 @@
|
|
|
1
1
|
import MAMapKit
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* 覆盖物管理器
|
|
5
|
+
*
|
|
6
|
+
* 负责:
|
|
7
|
+
* - 管理地图覆盖物(圆形、折线、多边形)
|
|
8
|
+
* - 管理标记点(Marker)
|
|
9
|
+
* - 处理覆盖物样式和渲染
|
|
10
|
+
* - 支持纹理贴图
|
|
11
|
+
*/
|
|
3
12
|
class OverlayManager {
|
|
13
|
+
/// 地图视图弱引用
|
|
4
14
|
private weak var mapView: MAMapView?
|
|
15
|
+
/// 覆盖物字典 (id -> overlay)
|
|
5
16
|
private var overlays: [String: MAOverlay] = [:]
|
|
17
|
+
/// 覆盖物样式字典 (id -> style)
|
|
6
18
|
private var overlayStyles: [String: [String: Any]] = [:]
|
|
19
|
+
/// 标记点字典 (id -> annotation)
|
|
7
20
|
private var annotations: [String: MAPointAnnotation] = [:]
|
|
8
21
|
|
|
22
|
+
/**
|
|
23
|
+
* 初始化覆盖物管理器
|
|
24
|
+
* @param mapView 地图视图实例
|
|
25
|
+
*/
|
|
9
26
|
init(mapView: MAMapView) {
|
|
10
27
|
self.mapView = mapView
|
|
11
28
|
}
|
|
12
29
|
|
|
13
|
-
// MARK: - Circle
|
|
30
|
+
// MARK: - Circle 圆形
|
|
14
31
|
|
|
32
|
+
/**
|
|
33
|
+
* 添加圆形覆盖物
|
|
34
|
+
* @param id 圆形唯一标识
|
|
35
|
+
* @param props 圆形属性(center, radius, fillColor, strokeColor, strokeWidth)
|
|
36
|
+
*/
|
|
15
37
|
func addCircle(id: String, props: [String: Any]) {
|
|
16
38
|
guard let mapView = mapView,
|
|
17
39
|
let center = props["center"] as? [String: Double],
|
|
@@ -25,6 +47,10 @@ class OverlayManager {
|
|
|
25
47
|
overlays[id] = circle
|
|
26
48
|
}
|
|
27
49
|
|
|
50
|
+
/**
|
|
51
|
+
* 移除圆形覆盖物
|
|
52
|
+
* @param id 圆形唯一标识
|
|
53
|
+
*/
|
|
28
54
|
func removeCircle(id: String) {
|
|
29
55
|
guard let mapView = mapView, let circle = overlays[id] else { return }
|
|
30
56
|
mapView.remove(circle)
|
|
@@ -32,13 +58,23 @@ class OverlayManager {
|
|
|
32
58
|
overlayStyles.removeValue(forKey: id)
|
|
33
59
|
}
|
|
34
60
|
|
|
61
|
+
/**
|
|
62
|
+
* 更新圆形覆盖物
|
|
63
|
+
* @param id 圆形唯一标识
|
|
64
|
+
* @param props 新的圆形属性
|
|
65
|
+
*/
|
|
35
66
|
func updateCircle(id: String, props: [String: Any]) {
|
|
36
67
|
removeCircle(id: id)
|
|
37
68
|
addCircle(id: id, props: props)
|
|
38
69
|
}
|
|
39
70
|
|
|
40
|
-
// MARK: - Marker
|
|
71
|
+
// MARK: - Marker 标记点
|
|
41
72
|
|
|
73
|
+
/**
|
|
74
|
+
* 添加标记点
|
|
75
|
+
* @param id 标记点唯一标识
|
|
76
|
+
* @param props 标记点属性(position, title, description)
|
|
77
|
+
*/
|
|
42
78
|
func addMarker(id: String, props: [String: Any]) {
|
|
43
79
|
guard let mapView = mapView,
|
|
44
80
|
let position = props["position"] as? [String: Double],
|
|
@@ -53,25 +89,36 @@ class OverlayManager {
|
|
|
53
89
|
annotations[id] = annotation
|
|
54
90
|
}
|
|
55
91
|
|
|
92
|
+
/**
|
|
93
|
+
* 移除标记点
|
|
94
|
+
* @param id 标记点唯一标识
|
|
95
|
+
*/
|
|
56
96
|
func removeMarker(id: String) {
|
|
57
97
|
guard let mapView = mapView, let annotation = annotations[id] else { return }
|
|
58
98
|
mapView.removeAnnotation(annotation)
|
|
59
99
|
annotations.removeValue(forKey: id)
|
|
60
100
|
}
|
|
61
101
|
|
|
102
|
+
/**
|
|
103
|
+
* 更新标记点
|
|
104
|
+
* @param id 标记点唯一标识
|
|
105
|
+
* @param props 新的标记点属性
|
|
106
|
+
*/
|
|
62
107
|
func updateMarker(id: String, props: [String: Any]) {
|
|
63
108
|
removeMarker(id: id)
|
|
64
109
|
addMarker(id: id, props: props)
|
|
65
110
|
}
|
|
66
111
|
|
|
67
|
-
// MARK: - Polyline
|
|
112
|
+
// MARK: - Polyline 折线
|
|
68
113
|
|
|
114
|
+
/**
|
|
115
|
+
* 添加折线覆盖物
|
|
116
|
+
* @param id 折线唯一标识
|
|
117
|
+
* @param props 折线属性(points, strokeWidth, strokeColor, texture)
|
|
118
|
+
*/
|
|
69
119
|
func addPolyline(id: String, props: [String: Any]) {
|
|
70
|
-
print("📏 OverlayManager.addPolyline - id: \(id), props: \(props)")
|
|
71
|
-
|
|
72
120
|
guard let mapView = mapView,
|
|
73
121
|
let points = props["points"] as? [[String: Double]] else {
|
|
74
|
-
print("❌ OverlayManager.addPolyline - mapView 或 points 为空")
|
|
75
122
|
return
|
|
76
123
|
}
|
|
77
124
|
|
|
@@ -81,7 +128,6 @@ class OverlayManager {
|
|
|
81
128
|
coordinates.append(CLLocationCoordinate2D(latitude: lat, longitude: lng))
|
|
82
129
|
}
|
|
83
130
|
guard coordinates.count >= 2 else {
|
|
84
|
-
print("❌ OverlayManager.addPolyline - 坐标点数量不足: \(coordinates.count)")
|
|
85
131
|
return
|
|
86
132
|
}
|
|
87
133
|
|
|
@@ -90,12 +136,13 @@ class OverlayManager {
|
|
|
90
136
|
// 先保存样式和 overlay,再添加到地图
|
|
91
137
|
overlayStyles[id] = props
|
|
92
138
|
overlays[id] = polyline
|
|
93
|
-
|
|
94
|
-
print("✅ OverlayManager.addPolyline - 准备添加到地图,id: \(id)")
|
|
95
139
|
mapView.add(polyline)
|
|
96
|
-
print("✅ OverlayManager.addPolyline - 已添加到地图")
|
|
97
140
|
}
|
|
98
141
|
|
|
142
|
+
/**
|
|
143
|
+
* 移除折线覆盖物
|
|
144
|
+
* @param id 折线唯一标识
|
|
145
|
+
*/
|
|
99
146
|
func removePolyline(id: String) {
|
|
100
147
|
guard let mapView = mapView, let polyline = overlays[id] else { return }
|
|
101
148
|
mapView.remove(polyline)
|
|
@@ -103,13 +150,23 @@ class OverlayManager {
|
|
|
103
150
|
overlayStyles.removeValue(forKey: id)
|
|
104
151
|
}
|
|
105
152
|
|
|
153
|
+
/**
|
|
154
|
+
* 更新折线覆盖物
|
|
155
|
+
* @param id 折线唯一标识
|
|
156
|
+
* @param props 新的折线属性
|
|
157
|
+
*/
|
|
106
158
|
func updatePolyline(id: String, props: [String: Any]) {
|
|
107
159
|
removePolyline(id: id)
|
|
108
160
|
addPolyline(id: id, props: props)
|
|
109
161
|
}
|
|
110
162
|
|
|
111
|
-
// MARK: - Polygon
|
|
163
|
+
// MARK: - Polygon 多边形
|
|
112
164
|
|
|
165
|
+
/**
|
|
166
|
+
* 添加多边形覆盖物
|
|
167
|
+
* @param id 多边形唯一标识
|
|
168
|
+
* @param props 多边形属性(points, fillColor, strokeColor, strokeWidth)
|
|
169
|
+
*/
|
|
113
170
|
func addPolygon(id: String, props: [String: Any]) {
|
|
114
171
|
guard let mapView = mapView,
|
|
115
172
|
let points = props["points"] as? [[String: Double]] else { return }
|
|
@@ -125,6 +182,10 @@ class OverlayManager {
|
|
|
125
182
|
overlays[id] = polygon
|
|
126
183
|
}
|
|
127
184
|
|
|
185
|
+
/**
|
|
186
|
+
* 移除多边形覆盖物
|
|
187
|
+
* @param id 多边形唯一标识
|
|
188
|
+
*/
|
|
128
189
|
func removePolygon(id: String) {
|
|
129
190
|
guard let mapView = mapView, let polygon = overlays[id] else { return }
|
|
130
191
|
mapView.remove(polygon)
|
|
@@ -132,30 +193,41 @@ class OverlayManager {
|
|
|
132
193
|
overlayStyles.removeValue(forKey: id)
|
|
133
194
|
}
|
|
134
195
|
|
|
196
|
+
/**
|
|
197
|
+
* 更新多边形覆盖物
|
|
198
|
+
* @param id 多边形唯一标识
|
|
199
|
+
* @param props 新的多边形属性
|
|
200
|
+
*/
|
|
135
201
|
func updatePolygon(id: String, props: [String: Any]) {
|
|
136
202
|
removePolygon(id: id)
|
|
137
203
|
addPolygon(id: id, props: props)
|
|
138
204
|
}
|
|
139
205
|
|
|
140
|
-
// MARK: - Renderer
|
|
206
|
+
// MARK: - Renderer 渲染器
|
|
141
207
|
|
|
208
|
+
/**
|
|
209
|
+
* 获取覆盖物渲染器
|
|
210
|
+
* @param overlay 覆盖物对象
|
|
211
|
+
* @return 对应的渲染器
|
|
212
|
+
*/
|
|
142
213
|
func getRenderer(for overlay: MAOverlay) -> MAOverlayRenderer? {
|
|
143
214
|
let id = overlays.first(where: { $0.value === overlay })?.key
|
|
144
215
|
let style = id != nil ? overlayStyles[id!] : nil
|
|
145
216
|
|
|
146
|
-
print("🎨 OverlayManager.getRenderer - overlay类型: \(type(of: overlay)), id: \(id ?? "nil"), style: \(style ?? [:])")
|
|
147
|
-
|
|
148
217
|
if let circle = overlay as? MACircle {
|
|
149
218
|
guard let renderer = MACircleRenderer(circle: circle) else {
|
|
150
219
|
return nil
|
|
151
220
|
}
|
|
152
221
|
|
|
222
|
+
// 设置填充颜色
|
|
153
223
|
if let fillColor = style?["fillColor"] {
|
|
154
224
|
renderer.fillColor = ColorParser.parseColor(fillColor)
|
|
155
225
|
}
|
|
226
|
+
// 设置边框颜色
|
|
156
227
|
if let strokeColor = style?["strokeColor"] {
|
|
157
228
|
renderer.strokeColor = ColorParser.parseColor(strokeColor)
|
|
158
229
|
}
|
|
230
|
+
// 设置边框宽度
|
|
159
231
|
if let strokeWidth = style?["strokeWidth"] as? Double {
|
|
160
232
|
renderer.lineWidth = CGFloat(strokeWidth)
|
|
161
233
|
}
|
|
@@ -182,17 +254,12 @@ class OverlayManager {
|
|
|
182
254
|
loadPolylineTexture(url: textureUrl, renderer: renderer)
|
|
183
255
|
} else {
|
|
184
256
|
if let color = style?["color"] {
|
|
185
|
-
print("🎨 color 原始值: \(color), 类型: \(type(of: color))")
|
|
186
257
|
let parsedColor = ColorParser.parseColor(color)
|
|
187
|
-
print("🎨 解析后的颜色: \(String(describing: parsedColor))")
|
|
188
258
|
renderer.strokeColor = parsedColor ?? .red
|
|
189
259
|
} else if let strokeColor = style?["strokeColor"] {
|
|
190
|
-
print("🎨 strokeColor 原始值: \(strokeColor), 类型: \(type(of: strokeColor))")
|
|
191
260
|
let parsedColor = ColorParser.parseColor(strokeColor)
|
|
192
|
-
print("🎨 解析后的颜色: \(String(describing: parsedColor))")
|
|
193
261
|
renderer.strokeColor = parsedColor ?? .red
|
|
194
262
|
} else {
|
|
195
|
-
print("⚠️ 没有找到 color 或 strokeColor,使用默认红色")
|
|
196
263
|
renderer.strokeColor = .red
|
|
197
264
|
}
|
|
198
265
|
}
|
|
@@ -203,12 +270,15 @@ class OverlayManager {
|
|
|
203
270
|
return nil
|
|
204
271
|
}
|
|
205
272
|
|
|
273
|
+
// 设置填充颜色
|
|
206
274
|
if let fillColor = style?["fillColor"] {
|
|
207
275
|
renderer.fillColor = ColorParser.parseColor(fillColor)
|
|
208
276
|
}
|
|
277
|
+
// 设置边框颜色
|
|
209
278
|
if let strokeColor = style?["strokeColor"] {
|
|
210
279
|
renderer.strokeColor = ColorParser.parseColor(strokeColor)
|
|
211
280
|
}
|
|
281
|
+
// 设置边框宽度
|
|
212
282
|
if let strokeWidth = style?["strokeWidth"] as? Double {
|
|
213
283
|
renderer.lineWidth = CGFloat(strokeWidth)
|
|
214
284
|
}
|
|
@@ -219,48 +289,54 @@ class OverlayManager {
|
|
|
219
289
|
return nil
|
|
220
290
|
}
|
|
221
291
|
|
|
292
|
+
/**
|
|
293
|
+
* 加载折线纹理图片
|
|
294
|
+
* @param url 图片 URL (支持 http/https/file/本地资源)
|
|
295
|
+
* @param renderer 折线渲染器
|
|
296
|
+
*/
|
|
222
297
|
private func loadPolylineTexture(url: String, renderer: MAPolylineRenderer) {
|
|
223
298
|
if url.hasPrefix("http://") || url.hasPrefix("https://") {
|
|
299
|
+
// 网络图片
|
|
224
300
|
guard let imageUrl = URL(string: url) else {
|
|
225
|
-
print("❌ OverlayManager: 无效的 URL: \(url)")
|
|
226
301
|
return
|
|
227
302
|
}
|
|
228
303
|
URLSession.shared.dataTask(with: imageUrl) { [weak self] data, _, error in
|
|
229
304
|
if let error = error {
|
|
230
|
-
print("❌ OverlayManager: 下载图片失败: \(error)")
|
|
231
305
|
return
|
|
232
306
|
}
|
|
233
307
|
guard let data = data, let image = UIImage(data: data) else {
|
|
234
|
-
print("❌ OverlayManager: 无法创建图片")
|
|
235
308
|
return
|
|
236
309
|
}
|
|
237
|
-
print("✅ OverlayManager: 图片下载成功,大小: \(image.size)")
|
|
238
310
|
DispatchQueue.main.async {
|
|
239
311
|
self?.applyPolylineTexture(image: image, to: renderer)
|
|
240
312
|
}
|
|
241
313
|
}.resume()
|
|
242
314
|
} else if url.hasPrefix("file://") {
|
|
315
|
+
// 本地文件
|
|
243
316
|
let path = String(url.dropFirst(7))
|
|
244
317
|
if let image = UIImage(contentsOfFile: path) {
|
|
245
|
-
print("✅ OverlayManager: 本地图片加载成功")
|
|
246
318
|
applyPolylineTexture(image: image, to: renderer)
|
|
247
|
-
} else {
|
|
248
|
-
print("❌ OverlayManager: 本地图片加载失败: \(path)")
|
|
249
319
|
}
|
|
250
320
|
} else {
|
|
321
|
+
// 资源文件
|
|
251
322
|
if let image = UIImage(named: url) {
|
|
252
|
-
print("✅ OverlayManager: 资源图片加载成功")
|
|
253
323
|
applyPolylineTexture(image: image, to: renderer)
|
|
254
|
-
} else {
|
|
255
|
-
print("❌ OverlayManager: 资源图片加载失败: \(url)")
|
|
256
324
|
}
|
|
257
325
|
}
|
|
258
326
|
}
|
|
259
327
|
|
|
328
|
+
/**
|
|
329
|
+
* 应用纹理到折线渲染器
|
|
330
|
+
* @param image 纹理图片
|
|
331
|
+
* @param renderer 折线渲染器
|
|
332
|
+
*/
|
|
260
333
|
private func applyPolylineTexture(image: UIImage, to renderer: MAPolylineRenderer) {
|
|
261
334
|
renderer.strokeImage = image
|
|
262
335
|
}
|
|
263
336
|
|
|
337
|
+
/**
|
|
338
|
+
* 清除所有覆盖物和标记点
|
|
339
|
+
*/
|
|
264
340
|
func clear() {
|
|
265
341
|
guard let mapView = mapView else { return }
|
|
266
342
|
for overlay in overlays.values {
|