expo-gaode-map 1.0.2 → 1.0.4

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 (56) hide show
  1. package/README.md +1 -0
  2. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +87 -5
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +132 -1
  4. package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +271 -6
  5. package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +40 -51
  6. package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +3 -0
  7. package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonView.kt +22 -2
  8. package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineView.kt +83 -17
  9. package/build/ExpoGaodeMapView.d.ts +46 -1
  10. package/build/ExpoGaodeMapView.d.ts.map +1 -1
  11. package/build/ExpoGaodeMapView.js +134 -1
  12. package/build/ExpoGaodeMapView.js.map +1 -1
  13. package/build/components/overlays/Circle.d.ts +0 -20
  14. package/build/components/overlays/Circle.d.ts.map +1 -1
  15. package/build/components/overlays/Circle.js +28 -45
  16. package/build/components/overlays/Circle.js.map +1 -1
  17. package/build/components/overlays/Marker.d.ts +2 -16
  18. package/build/components/overlays/Marker.d.ts.map +1 -1
  19. package/build/components/overlays/Marker.js +60 -37
  20. package/build/components/overlays/Marker.js.map +1 -1
  21. package/build/components/overlays/Polygon.d.ts +1 -0
  22. package/build/components/overlays/Polygon.d.ts.map +1 -1
  23. package/build/components/overlays/Polygon.js +38 -47
  24. package/build/components/overlays/Polygon.js.map +1 -1
  25. package/build/components/overlays/Polyline.d.ts +1 -0
  26. package/build/components/overlays/Polyline.d.ts.map +1 -1
  27. package/build/components/overlays/Polyline.js +38 -12
  28. package/build/components/overlays/Polyline.js.map +1 -1
  29. package/build/types/map-view.types.d.ts +42 -0
  30. package/build/types/map-view.types.d.ts.map +1 -1
  31. package/build/types/map-view.types.js.map +1 -1
  32. package/build/types/overlays.types.d.ts +31 -1
  33. package/build/types/overlays.types.d.ts.map +1 -1
  34. package/build/types/overlays.types.js.map +1 -1
  35. package/docs/API.md +121 -7
  36. package/docs/EXAMPLES.md +86 -2
  37. package/docs/INITIALIZATION.md +14 -4
  38. package/expo-module.config.json +1 -1
  39. package/ios/ExpoGaodeMapModule.swift +132 -18
  40. package/ios/ExpoGaodeMapView.swift +361 -12
  41. package/ios/managers/OverlayManager.swift +188 -12
  42. package/ios/modules/LocationManager.swift +3 -0
  43. package/ios/overlays/CircleView.swift +41 -12
  44. package/ios/overlays/MarkerView.swift +55 -3
  45. package/ios/overlays/PolygonView.swift +27 -5
  46. package/ios/overlays/PolylineView.swift +37 -4
  47. package/ios/utils/ColorParser.swift +0 -5
  48. package/ios/utils/PermissionManager.swift +62 -7
  49. package/package.json +1 -1
  50. package/src/ExpoGaodeMapView.tsx +194 -1
  51. package/src/components/overlays/Circle.tsx +31 -48
  52. package/src/components/overlays/Marker.tsx +69 -42
  53. package/src/components/overlays/Polygon.tsx +46 -49
  54. package/src/components/overlays/Polyline.tsx +46 -15
  55. package/src/types/map-view.types.ts +35 -0
  56. package/src/types/overlays.types.ts +36 -1
package/README.md CHANGED
@@ -580,3 +580,4 @@ MIT
580
580
  - 📝 提交 [GitHub Issue](https://github.com/TomWq/expo-gaode-map/issues)
581
581
  - 💬 参与 [Discussions](https://github.com/TomWq/expo-gaode-map/discussions)
582
582
  - ⭐ 给项目点个 Star 支持一下
583
+ - 💬 加入 QQ 群:952241387
@@ -1,6 +1,6 @@
1
1
  package expo.modules.gaodemap
2
2
 
3
- import android.util.Log
3
+
4
4
  import expo.modules.kotlin.modules.Module
5
5
  import expo.modules.kotlin.modules.ModuleDefinition
6
6
  import expo.modules.gaodemap.modules.SDKInitializer
@@ -17,9 +17,7 @@ import expo.modules.gaodemap.overlays.*
17
17
  * - 地图视图和覆盖物注册
18
18
  */
19
19
  class ExpoGaodeMapModule : Module() {
20
- companion object {
21
- private const val TAG = "ExpoGaodeMapModule"
22
- }
20
+
23
21
 
24
22
  /** 定位管理器实例 */
25
23
  private var locationManager: LocationManager? = null
@@ -181,6 +179,62 @@ class ExpoGaodeMapModule : Module() {
181
179
  getLocationManager().setHttpTimeOut(httpTimeOut)
182
180
  }
183
181
 
182
+ /**
183
+ * 设置定位精度 (iOS 专用,Android 空实现)
184
+ * @param accuracy 精度级别
185
+ */
186
+ Function("setDesiredAccuracy") { _: Int ->
187
+ // Android 不支持此配置
188
+ }
189
+
190
+ /**
191
+ * 设置定位超时时间 (iOS 专用,Android 空实现)
192
+ * @param timeout 超时时间(秒)
193
+ */
194
+ Function("setLocationTimeout") { _: Int ->
195
+ // Android 不支持此配置
196
+ }
197
+
198
+ /**
199
+ * 设置逆地理超时时间 (iOS 专用,Android 空实现)
200
+ * @param timeout 超时时间(秒)
201
+ */
202
+ Function("setReGeocodeTimeout") { _: Int ->
203
+ // Android 不支持此配置
204
+ }
205
+
206
+ /**
207
+ * 设置距离过滤器 (iOS 专用,Android 空实现)
208
+ * @param distance 最小距离变化(米)
209
+ */
210
+ Function("setDistanceFilter") { _: Double ->
211
+ // Android 不支持此配置
212
+ }
213
+
214
+ /**
215
+ * 设置是否自动暂停定位更新 (iOS 专用,Android 空实现)
216
+ * @param pauses 是否自动暂停
217
+ */
218
+ Function("setPausesLocationUpdatesAutomatically") { _: Boolean ->
219
+ // Android 不支持此配置
220
+ }
221
+
222
+ /**
223
+ * 设置是否允许后台定位 (iOS 专用,Android 空实现)
224
+ * @param allows 是否允许后台定位
225
+ */
226
+ Function("setAllowsBackgroundLocationUpdates") { _: Boolean ->
227
+ // Android 不支持此配置
228
+ }
229
+
230
+ /**
231
+ * 设置定位协议 (未实现)
232
+ * @param protocol 协议类型
233
+ */
234
+ Function("setLocationProtocol") { _: Int ->
235
+ // 未实现
236
+ }
237
+
184
238
  // ==================== 权限管理 ====================
185
239
 
186
240
  /**
@@ -271,7 +325,7 @@ class ExpoGaodeMapModule : Module() {
271
325
  View(ExpoGaodeMapView::class) {
272
326
 
273
327
  // 事件
274
- Events("onMapPress", "onMapLongPress", "onLoad")
328
+ Events("onMapPress", "onMapLongPress", "onLoad", "onMarkerPress", "onMarkerDragStart", "onMarkerDrag", "onMarkerDragEnd", "onCirclePress", "onPolygonPress", "onPolylinePress")
275
329
 
276
330
  // 地图类型
277
331
  Prop<Int>("mapType") { view, type ->
@@ -416,6 +470,22 @@ class ExpoGaodeMapModule : Module() {
416
470
  Prop<Boolean>("draggable") { view: MarkerView, draggable ->
417
471
  view.setDraggable(draggable)
418
472
  }
473
+
474
+ Prop<Float>("opacity") { view: MarkerView, opacity ->
475
+ view.setOpacity(opacity)
476
+ }
477
+
478
+ Prop<Boolean>("flat") { view: MarkerView, flat ->
479
+ view.setFlat(flat)
480
+ }
481
+
482
+ Prop<Float>("zIndex") { view: MarkerView, zIndex ->
483
+ view.setZIndex(zIndex)
484
+ }
485
+
486
+ Prop<Map<String, Float>>("anchor") { view: MarkerView, anchor ->
487
+ view.setAnchor(anchor)
488
+ }
419
489
  }
420
490
 
421
491
  // Circle - 圆形
@@ -462,6 +532,18 @@ class ExpoGaodeMapModule : Module() {
462
532
  Prop<String?>("texture") { view: PolylineView, texture ->
463
533
  view.setTexture(texture)
464
534
  }
535
+
536
+ Prop<Boolean>("dotted") { view: PolylineView, dotted ->
537
+ view.setDotted(dotted)
538
+ }
539
+
540
+ Prop<Boolean>("geodesic") { view: PolylineView, geodesic ->
541
+ view.setGeodesic(geodesic)
542
+ }
543
+
544
+ Prop<Float>("zIndex") { view: PolylineView, zIndex ->
545
+ view.setZIndex(zIndex)
546
+ }
465
547
  }
466
548
 
467
549
  // Polygon - 多边形
@@ -6,6 +6,7 @@ import android.view.View
6
6
  import com.amap.api.maps.AMap
7
7
  import com.amap.api.maps.MapView
8
8
  import com.amap.api.maps.MapsInitializer
9
+ import com.amap.api.maps.model.LatLng
9
10
  import expo.modules.kotlin.AppContext
10
11
  import expo.modules.kotlin.viewevent.EventDispatcher
11
12
  import expo.modules.kotlin.views.ExpoView
@@ -45,6 +46,13 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
45
46
  private val onMapPress by EventDispatcher()
46
47
  private val onMapLongPress by EventDispatcher()
47
48
  private val onLoad by EventDispatcher()
49
+ private val onMarkerPress by EventDispatcher()
50
+ private val onMarkerDragStart by EventDispatcher()
51
+ private val onMarkerDrag by EventDispatcher()
52
+ private val onMarkerDragEnd by EventDispatcher()
53
+ private val onCirclePress by EventDispatcher()
54
+ private val onPolygonPress by EventDispatcher()
55
+ private val onPolylinePress by EventDispatcher()
48
56
 
49
57
  // 高德地图视图
50
58
  private lateinit var mapView: MapView
@@ -73,7 +81,57 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
73
81
  // 初始化管理器
74
82
  cameraManager = CameraManager(aMap)
75
83
  uiManager = UIManager(aMap, context)
76
- overlayManager = OverlayManager(aMap)
84
+ overlayManager = OverlayManager(aMap, context).apply {
85
+ onMarkerPress = { id, lat, lng ->
86
+ this@ExpoGaodeMapView.onMarkerPress(mapOf(
87
+ "markerId" to id,
88
+ "latitude" to lat,
89
+ "longitude" to lng
90
+ ))
91
+ }
92
+ onMarkerDragStart = { id, lat, lng ->
93
+ this@ExpoGaodeMapView.onMarkerDragStart(mapOf(
94
+ "markerId" to id,
95
+ "latitude" to lat,
96
+ "longitude" to lng
97
+ ))
98
+ }
99
+ onMarkerDrag = { id, lat, lng ->
100
+ this@ExpoGaodeMapView.onMarkerDrag(mapOf(
101
+ "markerId" to id,
102
+ "latitude" to lat,
103
+ "longitude" to lng
104
+ ))
105
+ }
106
+ onMarkerDragEnd = { id, lat, lng ->
107
+ this@ExpoGaodeMapView.onMarkerDragEnd(mapOf(
108
+ "markerId" to id,
109
+ "latitude" to lat,
110
+ "longitude" to lng
111
+ ))
112
+ }
113
+ onCirclePress = { id, lat, lng ->
114
+ this@ExpoGaodeMapView.onCirclePress(mapOf(
115
+ "circleId" to id,
116
+ "latitude" to lat,
117
+ "longitude" to lng
118
+ ))
119
+ }
120
+ onPolygonPress = { id, lat, lng ->
121
+ this@ExpoGaodeMapView.onPolygonPress(mapOf(
122
+ "polygonId" to id,
123
+ "latitude" to lat,
124
+ "longitude" to lng
125
+ ))
126
+ }
127
+ onPolylinePress = { id, lat, lng ->
128
+ this@ExpoGaodeMapView.onPolylinePress(mapOf(
129
+ "polylineId" to id,
130
+ "latitude" to lat,
131
+ "longitude" to lng
132
+ ))
133
+ }
134
+ }
77
135
 
78
136
  // 添加地图视图到布局
79
137
  addView(mapView, LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT))
@@ -108,6 +166,43 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
108
166
  */
109
167
  private fun setupMapListeners() {
110
168
  aMap.setOnMapClickListener { latLng ->
169
+ // 检查声明式 PolylineView
170
+ if (checkDeclarativePolylinePress(latLng)) {
171
+ return@setOnMapClickListener
172
+ }
173
+
174
+ // 检查声明式 PolygonView
175
+ if (checkDeclarativePolygonPress(latLng)) {
176
+ return@setOnMapClickListener
177
+ }
178
+
179
+ // 检查声明式 CircleView
180
+ if (checkDeclarativeCirclePress(latLng)) {
181
+ return@setOnMapClickListener
182
+ }
183
+
184
+ // 检查命令式圆形
185
+ val circleId = overlayManager.checkCirclePress(latLng)
186
+ if (circleId != null) {
187
+ overlayManager.onCirclePress?.invoke(circleId, latLng.latitude, latLng.longitude)
188
+ return@setOnMapClickListener
189
+ }
190
+
191
+ // 检查命令式多边形
192
+ val polygonId = overlayManager.checkPolygonPress(latLng)
193
+ if (polygonId != null) {
194
+ overlayManager.onPolygonPress?.invoke(polygonId, latLng.latitude, latLng.longitude)
195
+ return@setOnMapClickListener
196
+ }
197
+
198
+ // 检查命令式折线
199
+ val polylineId = overlayManager.checkPolylinePress(latLng)
200
+ if (polylineId != null) {
201
+ overlayManager.onPolylinePress?.invoke(polylineId, latLng.latitude, latLng.longitude)
202
+ return@setOnMapClickListener
203
+ }
204
+
205
+ // 如果都没点击,触发地图点击事件
111
206
  onMapPress(mapOf(
112
207
  "latitude" to latLng.latitude,
113
208
  "longitude" to latLng.longitude
@@ -400,6 +495,42 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
400
495
  }
401
496
  }
402
497
 
498
+ private fun checkDeclarativePolylinePress(latLng: LatLng): Boolean {
499
+ for (i in 0 until childCount) {
500
+ val child = getChildAt(i)
501
+ if (child is PolylineView) {
502
+ if (child.checkPress(latLng)) {
503
+ return true
504
+ }
505
+ }
506
+ }
507
+ return false
508
+ }
509
+
510
+ private fun checkDeclarativePolygonPress(latLng: LatLng): Boolean {
511
+ for (i in 0 until childCount) {
512
+ val child = getChildAt(i)
513
+ if (child is PolygonView) {
514
+ if (child.checkPress(latLng)) {
515
+ return true
516
+ }
517
+ }
518
+ }
519
+ return false
520
+ }
521
+
522
+ private fun checkDeclarativeCirclePress(latLng: LatLng): Boolean {
523
+ for (i in 0 until childCount) {
524
+ val child = getChildAt(i)
525
+ if (child is CircleView) {
526
+ if (child.checkPress(latLng)) {
527
+ return true
528
+ }
529
+ }
530
+ }
531
+ return false
532
+ }
533
+
403
534
  override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
404
535
  super.onLayout(changed, left, top, right, bottom)
405
536
  }
@@ -1,6 +1,8 @@
1
1
  package expo.modules.gaodemap.managers
2
2
 
3
+ import android.content.Context
3
4
  import android.util.Log
5
+ import android.graphics.Bitmap
4
6
  import android.graphics.BitmapFactory
5
7
  import com.amap.api.maps.AMap
6
8
  import com.amap.api.maps.model.LatLng
@@ -12,7 +14,7 @@ import java.net.URL
12
14
  * 覆盖物管理器
13
15
  * 负责地图上所有覆盖物的添加、删除、更新
14
16
  */
15
- class OverlayManager(private val aMap: AMap) {
17
+ class OverlayManager(private val aMap: AMap, private val context: Context) {
16
18
 
17
19
  companion object {
18
20
  private const val TAG = "OverlayManager"
@@ -24,6 +26,29 @@ class OverlayManager(private val aMap: AMap) {
24
26
  private val polylines = mutableMapOf<String, com.amap.api.maps.model.Polyline>()
25
27
  private val polygons = mutableMapOf<String, com.amap.api.maps.model.Polygon>()
26
28
 
29
+ // Marker ID 映射
30
+ private val markerIdMap = mutableMapOf<com.amap.api.maps.model.Marker, String>()
31
+
32
+ // Circle ID 映射
33
+ private val circleIdMap = mutableMapOf<com.amap.api.maps.model.Circle, String>()
34
+
35
+ // Polygon ID 映射
36
+ private val polygonIdMap = mutableMapOf<com.amap.api.maps.model.Polygon, String>()
37
+
38
+ // Polyline ID 映射
39
+ private val polylineIdMap = mutableMapOf<com.amap.api.maps.model.Polyline, String>()
40
+
41
+ // 事件回调
42
+ var onMarkerPress: ((String, Double, Double) -> Unit)? = null
43
+ var onMarkerDragStart: ((String, Double, Double) -> Unit)? = null
44
+ var onMarkerDrag: ((String, Double, Double) -> Unit)? = null
45
+ var onMarkerDragEnd: ((String, Double, Double) -> Unit)? = null
46
+ var onCirclePress: ((String, Double, Double) -> Unit)? = null
47
+ var onPolygonPress: ((String, Double, Double) -> Unit)? = null
48
+ var onPolylinePress: ((String, Double, Double) -> Unit)? = null
49
+
50
+ private val mainHandler = android.os.Handler(android.os.Looper.getMainLooper())
51
+
27
52
  // ==================== 圆形覆盖物 ====================
28
53
 
29
54
  fun addCircle(id: String, props: Map<String, Any>) {
@@ -49,11 +74,13 @@ class OverlayManager(private val aMap: AMap) {
49
74
 
50
75
  val circle = aMap.addCircle(options)
51
76
  circles[id] = circle
77
+ circleIdMap[circle] = id
52
78
  }
53
79
  }
54
80
 
55
81
  fun removeCircle(id: String) {
56
82
  circles[id]?.let { circle ->
83
+ circleIdMap.remove(circle)
57
84
  circle.remove()
58
85
  circles.remove(id)
59
86
  }
@@ -84,11 +111,22 @@ class OverlayManager(private val aMap: AMap) {
84
111
  // ==================== 标记点 ====================
85
112
 
86
113
  fun addMarker(id: String, props: Map<String, Any>) {
114
+ Log.d(TAG, "addMarker 调用 - ID: $id")
115
+ Log.d(TAG, "addMarker props: $props")
87
116
 
88
117
  @Suppress("UNCHECKED_CAST")
89
118
  val position = props["position"] as? Map<String, Double>
90
119
  val title = props["title"] as? String
120
+ val snippet = props["snippet"] as? String
91
121
  val draggable = props["draggable"] as? Boolean ?: false
122
+ val icon = props["icon"]
123
+ // 将 RN 的点(points)转换为 Android 的 dp
124
+ val iconWidth = dpToPx((props["iconWidth"] as? Number)?.toFloat() ?: 40f)
125
+ val iconHeight = dpToPx((props["iconHeight"] as? Number)?.toFloat() ?: 40f)
126
+ val opacity = (props["opacity"] as? Number)?.toFloat() ?: 1.0f
127
+ val flat = props["flat"] as? Boolean ?: false
128
+ val zIndex = (props["zIndex"] as? Number)?.toFloat() ?: 0f
129
+ val anchor = props["anchor"] as? Map<String, Double>
92
130
 
93
131
  if (position != null) {
94
132
  val lat = position["latitude"] ?: 0.0
@@ -98,21 +136,115 @@ class OverlayManager(private val aMap: AMap) {
98
136
  val options = com.amap.api.maps.model.MarkerOptions()
99
137
  .position(latLng)
100
138
  .draggable(draggable)
139
+ .setFlat(flat)
140
+ .zIndex(zIndex)
101
141
 
102
142
  title?.let { options.title(it) }
143
+ snippet?.let { options.snippet(it) }
144
+
145
+ anchor?.let {
146
+ val x = (it["x"] as? Number)?.toFloat() ?: 0.5f
147
+ val y = (it["y"] as? Number)?.toFloat() ?: 1.0f
148
+ options.anchor(x, y)
149
+ }
103
150
 
104
151
  val marker = aMap.addMarker(options)
152
+ marker?.alpha = opacity
153
+
154
+ markers[id] = marker
155
+ marker?.let { markerIdMap[it] = id }
105
156
 
106
- if (!title.isNullOrEmpty()) {
107
- marker?.showInfoWindow()
157
+ // 加载自定义图标
158
+ icon?.let {
159
+ val uri = when (it) {
160
+ is String -> it
161
+ is Map<*, *> -> it["uri"] as? String
162
+ else -> null
163
+ }
164
+ uri?.let { iconUri ->
165
+ loadMarkerIcon(iconUri, iconWidth, iconHeight) { bitmap ->
166
+ mainHandler.post {
167
+ marker?.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap))
168
+ }
169
+ }
170
+ }
108
171
  }
109
172
 
110
- markers[id] = marker
173
+ // 设置事件监听器(只设置一次)
174
+ setupMarkerListeners()
111
175
  }
112
176
  }
113
177
 
178
+ private fun setupMarkerListeners() {
179
+ aMap.setOnMarkerClickListener { marker ->
180
+ markerIdMap[marker]?.let { id ->
181
+ onMarkerPress?.invoke(id, marker.position.latitude, marker.position.longitude)
182
+ }
183
+ false // 返回 false 允许显示 InfoWindow
184
+ }
185
+
186
+ aMap.setOnMarkerDragListener(object : AMap.OnMarkerDragListener {
187
+ override fun onMarkerDragStart(marker: com.amap.api.maps.model.Marker?) {
188
+ marker?.let { m ->
189
+ markerIdMap[m]?.let { id ->
190
+ onMarkerDragStart?.invoke(id, m.position.latitude, m.position.longitude)
191
+ }
192
+ }
193
+ }
194
+
195
+ override fun onMarkerDrag(marker: com.amap.api.maps.model.Marker?) {
196
+ marker?.let { m ->
197
+ markerIdMap[m]?.let { id ->
198
+ onMarkerDrag?.invoke(id, m.position.latitude, m.position.longitude)
199
+ }
200
+ }
201
+ }
202
+
203
+ override fun onMarkerDragEnd(marker: com.amap.api.maps.model.Marker?) {
204
+ marker?.let { m ->
205
+ markerIdMap[m]?.let { id ->
206
+ onMarkerDragEnd?.invoke(id, m.position.latitude, m.position.longitude)
207
+ }
208
+ }
209
+ }
210
+ })
211
+ }
212
+
213
+ private fun loadMarkerIcon(uri: String, width: Int, height: Int, callback: (Bitmap) -> Unit) {
214
+ Thread {
215
+ try {
216
+ Log.d(TAG, "开始加载图标: $uri")
217
+ val bitmap = when {
218
+ uri.startsWith("http://") || uri.startsWith("https://") -> {
219
+ Log.d(TAG, "加载网络图片")
220
+ BitmapFactory.decodeStream(URL(uri).openStream())
221
+ }
222
+ uri.startsWith("file://") -> {
223
+ Log.d(TAG, "加载本地文件")
224
+ BitmapFactory.decodeFile(uri.substring(7))
225
+ }
226
+ else -> {
227
+ Log.d(TAG, "未知 URI 格式")
228
+ null
229
+ }
230
+ }
231
+
232
+ if (bitmap == null) {
233
+ Log.e(TAG, "图标加载失败: bitmap 为 null")
234
+ } else {
235
+ Log.d(TAG, "图标加载成功: ${bitmap.width}x${bitmap.height}")
236
+ val resized = Bitmap.createScaledBitmap(bitmap, width, height, true)
237
+ callback(resized)
238
+ }
239
+ } catch (e: Exception) {
240
+ Log.e(TAG, "图标加载异常: ${e.message}", e)
241
+ }
242
+ }.start()
243
+ }
244
+
114
245
  fun removeMarker(id: String) {
115
246
  markers[id]?.let { marker ->
247
+ markerIdMap.remove(marker)
116
248
  marker.remove()
117
249
  markers.remove(id)
118
250
  }
@@ -123,7 +255,15 @@ class OverlayManager(private val aMap: AMap) {
123
255
  @Suppress("UNCHECKED_CAST")
124
256
  val position = props["position"] as? Map<String, Double>
125
257
  val title = props["title"] as? String
258
+ val snippet = props["snippet"] as? String
126
259
  val draggable = props["draggable"] as? Boolean
260
+ val icon = props["icon"]
261
+ val iconWidth = dpToPx((props["iconWidth"] as? Number)?.toFloat() ?: 40f)
262
+ val iconHeight = dpToPx((props["iconHeight"] as? Number)?.toFloat() ?: 40f)
263
+ val opacity = props["opacity"] as? Number
264
+ val flat = props["flat"] as? Boolean
265
+ val zIndex = props["zIndex"] as? Number
266
+ val anchor = props["anchor"] as? Map<String, Double>
127
267
 
128
268
  position?.let {
129
269
  val lat = it["latitude"] ?: 0.0
@@ -132,7 +272,32 @@ class OverlayManager(private val aMap: AMap) {
132
272
  }
133
273
 
134
274
  title?.let { marker.title = it }
275
+ snippet?.let { marker.snippet = it }
135
276
  draggable?.let { marker.isDraggable = it }
277
+ opacity?.let { marker.alpha = it.toFloat() }
278
+ flat?.let { marker.isFlat = it }
279
+ zIndex?.let { marker.zIndex = it.toFloat() }
280
+
281
+ anchor?.let {
282
+ val x = (it["x"] as? Number)?.toFloat() ?: 0.5f
283
+ val y = (it["y"] as? Number)?.toFloat() ?: 1.0f
284
+ marker.setAnchor(x, y)
285
+ }
286
+
287
+ icon?.let {
288
+ val uri = when (it) {
289
+ is String -> it
290
+ is Map<*, *> -> it["uri"] as? String
291
+ else -> null
292
+ }
293
+ uri?.let { iconUri ->
294
+ loadMarkerIcon(iconUri, iconWidth, iconHeight) { bitmap ->
295
+ mainHandler.post {
296
+ marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap))
297
+ }
298
+ }
299
+ }
300
+ }
136
301
  }
137
302
  }
138
303
 
@@ -142,12 +307,12 @@ class OverlayManager(private val aMap: AMap) {
142
307
 
143
308
  @Suppress("UNCHECKED_CAST")
144
309
  val points = props["points"] as? List<Map<String, Double>>
145
- val width = (props["strokeWidth"] as? Number)?.toFloat() ?: 10f
310
+ val width = (props["width"] as? Number)?.toFloat() ?: (props["strokeWidth"] as? Number)?.toFloat() ?: 10f
146
311
  val texture = props["texture"] as? String
147
312
  val color = if (!texture.isNullOrEmpty()) {
148
313
  android.graphics.Color.TRANSPARENT
149
314
  } else {
150
- ColorParser.parseColor(props["strokeColor"])
315
+ ColorParser.parseColor(props["color"] ?: props["strokeColor"])
151
316
  }
152
317
 
153
318
  if (points != null && points.size >= 2) {
@@ -188,11 +353,13 @@ class OverlayManager(private val aMap: AMap) {
188
353
  }
189
354
 
190
355
  polylines[id] = polyline
356
+ polylineIdMap[polyline] = id
191
357
  }
192
358
  }
193
359
 
194
360
  fun removePolyline(id: String) {
195
361
  polylines[id]?.let { polyline ->
362
+ polylineIdMap.remove(polyline)
196
363
  polyline.remove()
197
364
  polylines.remove(id)
198
365
  }
@@ -247,12 +414,14 @@ class OverlayManager(private val aMap: AMap) {
247
414
 
248
415
  val polygon = aMap.addPolygon(options)
249
416
  polygons[id] = polygon
417
+ polygonIdMap[polygon] = id
250
418
  Log.d(TAG, "✅ 多边形创建成功")
251
419
  }
252
420
  }
253
421
 
254
422
  fun removePolygon(id: String) {
255
423
  polygons[id]?.let { polygon ->
424
+ polygonIdMap.remove(polygon)
256
425
  polygon.remove()
257
426
  polygons.remove(id)
258
427
  }
@@ -287,9 +456,94 @@ class OverlayManager(private val aMap: AMap) {
287
456
  /**
288
457
  * 清理所有覆盖物
289
458
  */
459
+ /**
460
+ * 检查点击位置是否在某个圆形内
461
+ */
462
+ fun checkCirclePress(latLng: LatLng): String? {
463
+ for ((circle, id) in circleIdMap) {
464
+ if (circle.contains(latLng)) {
465
+ return id
466
+ }
467
+ }
468
+ return null
469
+ }
470
+
471
+ /**
472
+ * 检查点击位置是否在某个多边形内
473
+ */
474
+ fun checkPolygonPress(latLng: LatLng): String? {
475
+ for ((polygon, id) in polygonIdMap) {
476
+ if (polygon.contains(latLng)) {
477
+ return id
478
+ }
479
+ }
480
+ return null
481
+ }
482
+
483
+ /**
484
+ * 检查点击位置是否在某条折线附近
485
+ */
486
+ fun checkPolylinePress(latLng: LatLng): String? {
487
+ val threshold = 20.0 // 20米容差
488
+ for ((polyline, id) in polylineIdMap) {
489
+ val points = polyline.points
490
+ if (points.size < 2) continue
491
+
492
+ for (i in 0 until points.size - 1) {
493
+ val distance = distanceToSegment(latLng, points[i], points[i + 1])
494
+ if (distance <= threshold) {
495
+ return id
496
+ }
497
+ }
498
+ }
499
+ return null
500
+ }
501
+
502
+ /**
503
+ * 计算点到线段的距离(米)
504
+ */
505
+ private fun distanceToSegment(point: LatLng, start: LatLng, end: LatLng): Double {
506
+ val p = android.location.Location("").apply {
507
+ latitude = point.latitude
508
+ longitude = point.longitude
509
+ }
510
+ val a = android.location.Location("").apply {
511
+ latitude = start.latitude
512
+ longitude = start.longitude
513
+ }
514
+ val b = android.location.Location("").apply {
515
+ latitude = end.latitude
516
+ longitude = end.longitude
517
+ }
518
+
519
+ val ab = a.distanceTo(b).toDouble()
520
+ if (ab == 0.0) return a.distanceTo(p).toDouble()
521
+
522
+ val ap = a.distanceTo(p).toDouble()
523
+ val bp = b.distanceTo(p).toDouble()
524
+
525
+ val t = maxOf(0.0, minOf(1.0,
526
+ ((point.latitude - start.latitude) * (end.latitude - start.latitude) +
527
+ (point.longitude - start.longitude) * (end.longitude - start.longitude)) / (ab * ab)
528
+ ))
529
+
530
+ val projection = LatLng(
531
+ start.latitude + t * (end.latitude - start.latitude),
532
+ start.longitude + t * (end.longitude - start.longitude)
533
+ )
534
+
535
+ val proj = android.location.Location("").apply {
536
+ latitude = projection.latitude
537
+ longitude = projection.longitude
538
+ }
539
+
540
+ return p.distanceTo(proj).toDouble()
541
+ }
542
+
290
543
  fun clear() {
291
544
  circles.values.forEach { it.remove() }
292
545
  circles.clear()
546
+ circleIdMap.clear()
293
547
 
294
548
  markers.values.forEach { it.remove() }
295
549
  markers.clear()
@@ -299,5 +553,16 @@ class OverlayManager(private val aMap: AMap) {
299
553
 
300
554
  polygons.values.forEach { it.remove() }
301
555
  polygons.clear()
556
+ polygonIdMap.clear()
557
+
558
+ polylineIdMap.clear()
559
+ }
560
+
561
+ /**
562
+ * 将 dp 转换为 px
563
+ */
564
+ private fun dpToPx(dp: Float): Int {
565
+ val density = context.resources.displayMetrics.density
566
+ return (dp * density + 0.5f).toInt()
302
567
  }
303
568
  }