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.
- package/README.md +1 -0
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +87 -5
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +132 -1
- package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +271 -6
- package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +40 -51
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +3 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonView.kt +22 -2
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineView.kt +83 -17
- package/build/ExpoGaodeMapView.d.ts +46 -1
- package/build/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/ExpoGaodeMapView.js +134 -1
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/overlays/Circle.d.ts +0 -20
- package/build/components/overlays/Circle.d.ts.map +1 -1
- package/build/components/overlays/Circle.js +28 -45
- package/build/components/overlays/Circle.js.map +1 -1
- package/build/components/overlays/Marker.d.ts +2 -16
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +60 -37
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/components/overlays/Polygon.d.ts +1 -0
- package/build/components/overlays/Polygon.d.ts.map +1 -1
- package/build/components/overlays/Polygon.js +38 -47
- package/build/components/overlays/Polygon.js.map +1 -1
- package/build/components/overlays/Polyline.d.ts +1 -0
- package/build/components/overlays/Polyline.d.ts.map +1 -1
- package/build/components/overlays/Polyline.js +38 -12
- package/build/components/overlays/Polyline.js.map +1 -1
- package/build/types/map-view.types.d.ts +42 -0
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/build/types/overlays.types.d.ts +31 -1
- package/build/types/overlays.types.d.ts.map +1 -1
- package/build/types/overlays.types.js.map +1 -1
- package/docs/API.md +121 -7
- package/docs/EXAMPLES.md +86 -2
- package/docs/INITIALIZATION.md +14 -4
- package/expo-module.config.json +1 -1
- package/ios/ExpoGaodeMapModule.swift +132 -18
- package/ios/ExpoGaodeMapView.swift +361 -12
- package/ios/managers/OverlayManager.swift +188 -12
- package/ios/modules/LocationManager.swift +3 -0
- package/ios/overlays/CircleView.swift +41 -12
- package/ios/overlays/MarkerView.swift +55 -3
- package/ios/overlays/PolygonView.swift +27 -5
- package/ios/overlays/PolylineView.swift +37 -4
- package/ios/utils/ColorParser.swift +0 -5
- package/ios/utils/PermissionManager.swift +62 -7
- package/package.json +1 -1
- package/src/ExpoGaodeMapView.tsx +194 -1
- package/src/components/overlays/Circle.tsx +31 -48
- package/src/components/overlays/Marker.tsx +69 -42
- package/src/components/overlays/Polygon.tsx +46 -49
- package/src/components/overlays/Polyline.tsx +46 -15
- package/src/types/map-view.types.ts +35 -0
- package/src/types/overlays.types.ts +36 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
package expo.modules.gaodemap
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
}
|