expo-gaode-map-navigation 2.0.12-next.0 → 2.0.13
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 +296 -7
- package/android/build.gradle +12 -4
- package/android/src/main/AndroidManifest.xml +10 -1
- package/android/src/main/cpp/cluster_jni.cpp +56 -0
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +49 -8
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapOfflineModule.kt +83 -15
- package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapView.kt +13 -3
- package/android/src/main/java/expo/modules/gaodemap/map/managers/UIManager.kt +36 -39
- package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +23 -17
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/ClusterView.kt +5 -2
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapView.kt +122 -10
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/HeatMapViewModule.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +37 -25
- package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +6 -6
- package/android/src/main/java/expo/modules/gaodemap/map/search/ExpoGaodeMapSearchModule.kt +751 -0
- package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +103 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +1408 -394
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviViewModule.kt +121 -1
- package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNavigationModule.kt +14 -28
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/IndependentRouteListener.kt +28 -3
- package/android/src/main/java/expo/modules/gaodemap/navigation/listeners/RouteCalculateListener.kt +2 -2
- package/android/src/main/java/expo/modules/gaodemap/navigation/managers/IndependentRouteManager.kt +114 -15
- package/android/src/main/java/expo/modules/gaodemap/navigation/routes/drive/DriveTruckRouteCalculator.kt +24 -35
- package/android/src/main/java/expo/modules/gaodemap/navigation/services/IndependentRouteService.kt +50 -36
- package/android/src/main/java/expo/modules/gaodemap/navigation/services/NavigationForegroundService.kt +661 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/utils/Converters.kt +21 -12
- package/android/src/main/res/drawable/ic_nav_notification_small.xml +10 -0
- package/android/src/main/res/drawable/landback_0.png +0 -0
- package/android/src/main/res/drawable/landback_1.png +0 -0
- package/android/src/main/res/drawable/landback_2.png +0 -0
- package/android/src/main/res/drawable/landback_3.png +0 -0
- package/android/src/main/res/drawable/landback_4.png +0 -0
- package/android/src/main/res/drawable/landback_5.png +0 -0
- package/android/src/main/res/drawable/landback_6.png +0 -0
- package/android/src/main/res/drawable/landback_7.png +0 -0
- package/android/src/main/res/drawable/landback_8.png +0 -0
- package/android/src/main/res/drawable/landback_9.png +0 -0
- package/android/src/main/res/drawable/landback_a.png +0 -0
- package/android/src/main/res/drawable/landback_b.png +0 -0
- package/android/src/main/res/drawable/landback_c.png +0 -0
- package/android/src/main/res/drawable/landback_d.png +0 -0
- package/android/src/main/res/drawable/landback_e.png +0 -0
- package/android/src/main/res/drawable/landback_f.png +0 -0
- package/android/src/main/res/drawable/landback_g.png +0 -0
- package/android/src/main/res/drawable/landback_h.png +0 -0
- package/android/src/main/res/drawable/landback_i.png +0 -0
- package/android/src/main/res/drawable/landback_j.png +0 -0
- package/android/src/main/res/drawable/landback_k.png +0 -0
- package/android/src/main/res/drawable/landback_l.png +0 -0
- package/android/src/main/res/drawable/landfront_0.png +0 -0
- package/android/src/main/res/drawable/landfront_00.png +0 -0
- package/android/src/main/res/drawable/landfront_1.png +0 -0
- package/android/src/main/res/drawable/landfront_11.png +0 -0
- package/android/src/main/res/drawable/landfront_20.png +0 -0
- package/android/src/main/res/drawable/landfront_21.png +0 -0
- package/android/src/main/res/drawable/landfront_22.png +0 -0
- package/android/src/main/res/drawable/landfront_3.png +0 -0
- package/android/src/main/res/drawable/landfront_33.png +0 -0
- package/android/src/main/res/drawable/landfront_40.png +0 -0
- package/android/src/main/res/drawable/landfront_43.png +0 -0
- package/android/src/main/res/drawable/landfront_44.png +0 -0
- package/android/src/main/res/drawable/landfront_5.png +0 -0
- package/android/src/main/res/drawable/landfront_55.png +0 -0
- package/android/src/main/res/drawable/landfront_61.png +0 -0
- package/android/src/main/res/drawable/landfront_63.png +0 -0
- package/android/src/main/res/drawable/landfront_66.png +0 -0
- package/android/src/main/res/drawable/landfront_70.png +0 -0
- package/android/src/main/res/drawable/landfront_71.png +0 -0
- package/android/src/main/res/drawable/landfront_73.png +0 -0
- package/android/src/main/res/drawable/landfront_77.png +0 -0
- package/android/src/main/res/drawable/landfront_8.png +0 -0
- package/android/src/main/res/drawable/landfront_88.png +0 -0
- package/android/src/main/res/drawable/landfront_90.png +0 -0
- package/android/src/main/res/drawable/landfront_95.png +0 -0
- package/android/src/main/res/drawable/landfront_99.png +0 -0
- package/android/src/main/res/drawable/landfront_a0.png +0 -0
- package/android/src/main/res/drawable/landfront_a8.png +0 -0
- package/android/src/main/res/drawable/landfront_aa.png +0 -0
- package/android/src/main/res/drawable/landfront_b1.png +0 -0
- package/android/src/main/res/drawable/landfront_b5.png +0 -0
- package/android/src/main/res/drawable/landfront_bb.png +0 -0
- package/android/src/main/res/drawable/landfront_c3.png +0 -0
- package/android/src/main/res/drawable/landfront_c8.png +0 -0
- package/android/src/main/res/drawable/landfront_cc.png +0 -0
- package/android/src/main/res/drawable/landfront_d.png +0 -0
- package/android/src/main/res/drawable/landfront_dd.png +0 -0
- package/android/src/main/res/drawable/landfront_e1.png +0 -0
- package/android/src/main/res/drawable/landfront_e5.png +0 -0
- package/android/src/main/res/drawable/landfront_ee.png +0 -0
- package/android/src/main/res/drawable/landfront_f0.png +0 -0
- package/android/src/main/res/drawable/landfront_f1.png +0 -0
- package/android/src/main/res/drawable/landfront_f5.png +0 -0
- package/android/src/main/res/drawable/landfront_ff.png +0 -0
- package/android/src/main/res/drawable/landfront_g3.png +0 -0
- package/android/src/main/res/drawable/landfront_g5.png +0 -0
- package/android/src/main/res/drawable/landfront_gg.png +0 -0
- package/android/src/main/res/drawable/landfront_h1.png +0 -0
- package/android/src/main/res/drawable/landfront_h3.png +0 -0
- package/android/src/main/res/drawable/landfront_h5.png +0 -0
- package/android/src/main/res/drawable/landfront_hh.png +0 -0
- package/android/src/main/res/drawable/landfront_i0.png +0 -0
- package/android/src/main/res/drawable/landfront_i3.png +0 -0
- package/android/src/main/res/drawable/landfront_i5.png +0 -0
- package/android/src/main/res/drawable/landfront_ii.png +0 -0
- package/android/src/main/res/drawable/landfront_j1.png +0 -0
- package/android/src/main/res/drawable/landfront_j8.png +0 -0
- package/android/src/main/res/drawable/landfront_jj.png +0 -0
- package/android/src/main/res/drawable/landfront_kk.png +0 -0
- package/android/src/main/res/drawable/landfront_ll.png +0 -0
- package/android/src/main/res/drawable/nav_notification_brand_icon.xml +16 -0
- package/android/src/main/res/drawable/navi_arrow_leftline.png +0 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_center.xml +5 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_left.xml +8 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_over.xml +6 -0
- package/android/src/main/res/drawable/navi_lane_shape_bg_right.xml +8 -0
- package/android/src/main/res/drawable-nodpi/nav_tracker_car.png +0 -0
- package/build/ExpoGaodeMapNaviView.d.ts +16 -0
- package/build/ExpoGaodeMapNaviView.d.ts.map +1 -1
- package/build/ExpoGaodeMapNaviView.js +74 -1
- package/build/ExpoGaodeMapNaviView.js.map +1 -1
- package/build/index.d.ts +56 -8
- package/build/index.d.ts.map +1 -1
- package/build/index.js +452 -10
- package/build/index.js.map +1 -1
- package/build/map/ExpoGaodeMapModule.d.ts +15 -13
- package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapModule.js +31 -39
- package/build/map/ExpoGaodeMapModule.js.map +1 -1
- package/build/map/ExpoGaodeMapOfflineModule.d.ts +5 -0
- package/build/map/ExpoGaodeMapOfflineModule.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapOfflineModule.js.map +1 -1
- package/build/map/ExpoGaodeMapView.d.ts +3 -4
- package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
- package/build/map/ExpoGaodeMapView.js +28 -25
- package/build/map/ExpoGaodeMapView.js.map +1 -1
- package/build/map/components/overlays/Circle.d.ts.map +1 -1
- package/build/map/components/overlays/Circle.js +1 -30
- package/build/map/components/overlays/Circle.js.map +1 -1
- package/build/map/components/overlays/Cluster.d.ts.map +1 -1
- package/build/map/components/overlays/Cluster.js +1 -42
- package/build/map/components/overlays/Cluster.js.map +1 -1
- package/build/map/components/overlays/HeatMap.d.ts.map +1 -1
- package/build/map/components/overlays/HeatMap.js +21 -21
- package/build/map/components/overlays/HeatMap.js.map +1 -1
- package/build/map/components/overlays/Marker.d.ts.map +1 -1
- package/build/map/components/overlays/Marker.js +76 -80
- package/build/map/components/overlays/Marker.js.map +1 -1
- package/build/map/components/overlays/Polygon.d.ts.map +1 -1
- package/build/map/components/overlays/Polygon.js +1 -25
- package/build/map/components/overlays/Polygon.js.map +1 -1
- package/build/map/components/overlays/Polyline.d.ts.map +1 -1
- package/build/map/components/overlays/Polyline.js +1 -31
- package/build/map/components/overlays/Polyline.js.map +1 -1
- package/build/map/index.d.ts +9 -2
- package/build/map/index.d.ts.map +1 -1
- package/build/map/index.js +9 -2
- package/build/map/index.js.map +1 -1
- package/build/map/search/ExpoGaodeMapSearch.types.d.ts +340 -0
- package/build/map/search/ExpoGaodeMapSearch.types.d.ts.map +1 -0
- package/build/map/search/ExpoGaodeMapSearch.types.js +19 -0
- package/build/map/search/ExpoGaodeMapSearch.types.js.map +1 -0
- package/build/map/search/ExpoGaodeMapSearchModule.d.ts +74 -0
- package/build/map/search/ExpoGaodeMapSearchModule.d.ts.map +1 -0
- package/build/map/search/ExpoGaodeMapSearchModule.js +47 -0
- package/build/map/search/ExpoGaodeMapSearchModule.js.map +1 -0
- package/build/map/search/index.d.ts +156 -0
- package/build/map/search/index.d.ts.map +1 -0
- package/build/map/search/index.js +171 -0
- package/build/map/search/index.js.map +1 -0
- package/build/map/types/index.d.ts +2 -2
- package/build/map/types/index.d.ts.map +1 -1
- package/build/map/types/index.js.map +1 -1
- package/build/map/types/map-view.types.d.ts +4 -2
- package/build/map/types/map-view.types.d.ts.map +1 -1
- package/build/map/types/map-view.types.js.map +1 -1
- package/build/map/types/native-module.types.d.ts +11 -12
- package/build/map/types/native-module.types.d.ts.map +1 -1
- package/build/map/types/native-module.types.js.map +1 -1
- package/build/map/types/overlays.types.d.ts +9 -14
- package/build/map/types/overlays.types.d.ts.map +1 -1
- package/build/map/types/overlays.types.js.map +1 -1
- package/build/map/types/route-playback.types.d.ts +16 -0
- package/build/map/types/route-playback.types.d.ts.map +1 -1
- package/build/map/types/route-playback.types.js.map +1 -1
- package/build/map/utils/ErrorHandler.js +11 -11
- package/build/map/utils/ErrorHandler.js.map +1 -1
- package/build/map/utils/OfflineMapManager.d.ts +4 -0
- package/build/map/utils/OfflineMapManager.d.ts.map +1 -1
- package/build/map/utils/OfflineMapManager.js +6 -0
- package/build/map/utils/OfflineMapManager.js.map +1 -1
- package/build/types/coordinates.types.d.ts +3 -0
- package/build/types/coordinates.types.d.ts.map +1 -1
- package/build/types/coordinates.types.js.map +1 -1
- package/build/types/independent.types.d.ts +111 -12
- package/build/types/independent.types.d.ts.map +1 -1
- package/build/types/independent.types.js.map +1 -1
- package/build/types/native-module.types.d.ts +1 -1
- package/build/types/native-module.types.js.map +1 -1
- package/build/types/naviview.types.d.ts +304 -14
- package/build/types/naviview.types.d.ts.map +1 -1
- package/build/types/naviview.types.js.map +1 -1
- package/build/types/route.types.d.ts +12 -2
- package/build/types/route.types.d.ts.map +1 -1
- package/build/types/route.types.js.map +1 -1
- package/expo-module.config.json +4 -2
- package/ios/ExpoGaodeMapNaviView.swift +2331 -201
- package/ios/ExpoGaodeMapNaviViewModule.swift +109 -1
- package/ios/ExpoGaodeMapNavigation.podspec +2 -1
- package/ios/ExpoGaodeMapNavigationModule.swift +7 -5
- package/ios/managers/IndependentRouteManager.swift +90 -26
- package/ios/map/ExpoGaodeMapModule.swift +72 -21
- package/ios/map/ExpoGaodeMapOfflineModule.swift +61 -0
- package/ios/map/ExpoGaodeMapSearchModule.swift +773 -0
- package/ios/map/ExpoGaodeMapView.swift +23 -5
- package/ios/map/GaodeMapPrivacyManager.swift +26 -18
- package/ios/map/cpp/GeometryEngine.cpp +112 -0
- package/ios/map/cpp/GeometryEngine.hpp +21 -0
- package/ios/map/modules/LocationManager.swift +37 -5
- package/ios/map/overlays/MarkerView.swift +11 -11
- package/ios/map/overlays/MarkerViewModule.swift +4 -4
- package/ios/map/overlays/PolylineView.swift +6 -12
- package/ios/map/utils/ClusterNative.h +8 -0
- package/ios/map/utils/ClusterNative.mm +27 -0
- package/ios/map/utils/PermissionManager.swift +115 -6
- package/ios/routes/drive/DriveTruckRouteCalculator.swift +165 -77
- package/ios/routes/walkride/WalkRideRouteCalculator.swift +127 -1
- package/ios/services/IndependentRouteService.swift +198 -39
- package/ios/services/NavigationLiveActivityAttributes.swift +48 -0
- package/ios/services/NavigationLiveActivityManager.swift +359 -0
- package/package.json +22 -7
- package/plugin/build/withGaodeMap.d.ts +8 -0
- package/plugin/build/withGaodeMap.js +60 -4
- package/scripts/check-expo-modules.js +68 -0
- package/shared/cpp/GeometryEngine.cpp +112 -0
- package/shared/cpp/GeometryEngine.hpp +21 -0
- package/widget-template/README.md +46 -0
- package/widget-template/ios/NavigationLiveActivityWidget.swift +367 -0
- package/android/src/main/java/expo/modules/gaodemap/navigation/managers/RouteCalculator.kt +0 -173
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
package expo.modules.gaodemap.map
|
|
2
2
|
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.Intent
|
|
3
6
|
import android.os.Bundle
|
|
4
7
|
import android.os.StatFs
|
|
5
8
|
import android.os.Environment
|
|
6
9
|
import android.util.Log
|
|
10
|
+
import com.amap.api.maps.offlinemap.OfflineMapActivity
|
|
7
11
|
import com.amap.api.maps.offlinemap.OfflineMapCity
|
|
8
12
|
import com.amap.api.maps.offlinemap.OfflineMapManager
|
|
9
13
|
import com.amap.api.maps.offlinemap.OfflineMapProvince
|
|
10
14
|
import com.amap.api.maps.offlinemap.OfflineMapStatus
|
|
11
15
|
import expo.modules.gaodemap.map.modules.SDKInitializer
|
|
16
|
+
import expo.modules.kotlin.exception.CodedException
|
|
12
17
|
import expo.modules.kotlin.modules.Module
|
|
13
18
|
import expo.modules.kotlin.modules.ModuleDefinition
|
|
14
19
|
|
|
@@ -92,12 +97,18 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
92
97
|
offlineMapManager = null
|
|
93
98
|
downloadingCities.clear()
|
|
94
99
|
}
|
|
100
|
+
|
|
101
|
+
AsyncFunction("openOfflineMapUI") {
|
|
102
|
+
openOfflineMapUI()
|
|
103
|
+
}
|
|
95
104
|
|
|
96
105
|
// ==================== 地图列表管理 ====================
|
|
97
106
|
|
|
98
107
|
AsyncFunction("getAvailableCities") {
|
|
99
|
-
val
|
|
100
|
-
cities
|
|
108
|
+
val manager = getOfflineMapManager()
|
|
109
|
+
val cities = manager?.offlineMapCityList ?: emptyList()
|
|
110
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
111
|
+
cities.map { city -> convertCityToMap(city, downloadedCityKeys) }
|
|
101
112
|
}
|
|
102
113
|
|
|
103
114
|
AsyncFunction("getAvailableProvinces") {
|
|
@@ -106,15 +117,19 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
106
117
|
}
|
|
107
118
|
|
|
108
119
|
AsyncFunction("getCitiesByProvince") { provinceCode: String ->
|
|
109
|
-
val
|
|
110
|
-
|
|
120
|
+
val manager = getOfflineMapManager()
|
|
121
|
+
val province = manager?.offlineMapProvinceList?.find {
|
|
122
|
+
it.provinceCode == provinceCode
|
|
111
123
|
}
|
|
112
|
-
|
|
124
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
125
|
+
province?.cityList?.map { city -> convertCityToMap(city, downloadedCityKeys) } ?: emptyList()
|
|
113
126
|
}
|
|
114
127
|
|
|
115
128
|
AsyncFunction("getDownloadedMaps") {
|
|
116
|
-
val
|
|
117
|
-
cities
|
|
129
|
+
val manager = getOfflineMapManager()
|
|
130
|
+
val cities = manager?.downloadOfflineMapCityList ?: emptyList()
|
|
131
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
132
|
+
cities.map { city -> convertCityToMap(city, downloadedCityKeys) }
|
|
118
133
|
}
|
|
119
134
|
|
|
120
135
|
// ==================== 下载管理 ====================
|
|
@@ -208,14 +223,17 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
208
223
|
// ==================== 状态查询 ====================
|
|
209
224
|
|
|
210
225
|
AsyncFunction("isMapDownloaded") { cityCode: String ->
|
|
211
|
-
val
|
|
212
|
-
city
|
|
213
|
-
city?.state == OfflineMapStatus.
|
|
226
|
+
val manager = getOfflineMapManager()
|
|
227
|
+
val city = manager?.getItemByCityCode(cityCode)
|
|
228
|
+
city?.state == OfflineMapStatus.SUCCESS ||
|
|
229
|
+
city?.let { isDownloadedCity(it, getDownloadedCityKeys(manager)) } == true
|
|
214
230
|
}
|
|
215
231
|
|
|
216
232
|
AsyncFunction("getMapStatus") { cityCode: String ->
|
|
217
|
-
val
|
|
218
|
-
city
|
|
233
|
+
val manager = getOfflineMapManager()
|
|
234
|
+
val city = manager?.getItemByCityCode(cityCode)
|
|
235
|
+
val downloadedCityKeys = getDownloadedCityKeys(manager)
|
|
236
|
+
city?.let { convertCityToMap(it, downloadedCityKeys) } ?: Bundle()
|
|
219
237
|
}
|
|
220
238
|
|
|
221
239
|
AsyncFunction("getTotalProgress") {
|
|
@@ -463,13 +481,37 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
463
481
|
/**
|
|
464
482
|
* 转换城市对象为 Map
|
|
465
483
|
*/
|
|
466
|
-
private fun
|
|
484
|
+
private fun getDownloadedCityKeys(manager: OfflineMapManager?): Set<String> {
|
|
485
|
+
return manager?.downloadOfflineMapCityList
|
|
486
|
+
?.flatMap { getCityIdentityKeys(it) }
|
|
487
|
+
?.toSet()
|
|
488
|
+
?: emptySet()
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
private fun getCityIdentityKeys(city: OfflineMapCity): List<String> {
|
|
492
|
+
return listOfNotNull(
|
|
493
|
+
city.code?.trim()?.takeIf { it.isNotEmpty() }?.let { "code:$it" },
|
|
494
|
+
city.adcode?.trim()?.takeIf { it.isNotEmpty() }?.let { "adcode:$it" },
|
|
495
|
+
city.city?.trim()?.takeIf { it.isNotEmpty() }?.let { "name:$it" }
|
|
496
|
+
)
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
private fun isDownloadedCity(city: OfflineMapCity, downloadedCityKeys: Set<String>): Boolean {
|
|
500
|
+
return getCityIdentityKeys(city).any { downloadedCityKeys.contains(it) }
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
private fun convertCityToMap(
|
|
504
|
+
city: OfflineMapCity,
|
|
505
|
+
downloadedCityKeys: Set<String> = emptySet()
|
|
506
|
+
): Bundle {
|
|
467
507
|
val isPaused = synchronized(lock) { pausedCities.contains(city.code) }
|
|
468
508
|
val isDownloading = synchronized(lock) { downloadingCities.contains(city.code) }
|
|
509
|
+
val isDownloaded = isDownloadedCity(city, downloadedCityKeys)
|
|
469
510
|
|
|
470
511
|
val status = when {
|
|
471
512
|
isPaused -> "paused"
|
|
472
513
|
isDownloading -> "downloading"
|
|
514
|
+
isDownloaded -> "downloaded"
|
|
473
515
|
else -> getStatusString(city.state)
|
|
474
516
|
}
|
|
475
517
|
|
|
@@ -501,8 +543,9 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
501
543
|
}
|
|
502
544
|
|
|
503
545
|
/**
|
|
504
|
-
*
|
|
505
|
-
*
|
|
546
|
+
* 获取状态字符串。
|
|
547
|
+
* CHECKUPDATES / NEW_VERSION 在 10.1.600 冷启动时可能出现在普通城市列表,
|
|
548
|
+
* 不能单独作为已下载依据;已下载状态以 downloadOfflineMapCityList 为准。
|
|
506
549
|
*/
|
|
507
550
|
private fun getStatusString(state: Int): String {
|
|
508
551
|
return when (state) {
|
|
@@ -558,4 +601,29 @@ class ExpoGaodeMapOfflineModule : Module() {
|
|
|
558
601
|
null
|
|
559
602
|
}
|
|
560
603
|
}
|
|
604
|
+
|
|
605
|
+
private fun getActivityLaunchContext(): Context {
|
|
606
|
+
return appContext.currentActivity
|
|
607
|
+
?: appContext.reactContext
|
|
608
|
+
?: throw CodedException("NO_CONTEXT", "React context not available", null)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
private fun openOfflineMapUI() {
|
|
612
|
+
val context = getActivityLaunchContext()
|
|
613
|
+
SDKInitializer.restorePersistedState(context.applicationContext)
|
|
614
|
+
|
|
615
|
+
if (!SDKInitializer.isPrivacyReady()) {
|
|
616
|
+
throw CodedException(
|
|
617
|
+
"PRIVACY_NOT_AGREED",
|
|
618
|
+
"隐私协议未完成确认,请先调用 setPrivacyConfig",
|
|
619
|
+
null
|
|
620
|
+
)
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
val intent = Intent(context, OfflineMapActivity::class.java)
|
|
624
|
+
if (context !is Activity) {
|
|
625
|
+
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
|
626
|
+
}
|
|
627
|
+
context.startActivity(intent)
|
|
628
|
+
}
|
|
561
629
|
}
|
|
@@ -150,6 +150,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
150
150
|
// 缓存初始相机位置,等待地图加载完成后设置
|
|
151
151
|
private var pendingCameraPosition: Map<String, Any?>? = null
|
|
152
152
|
private var isMapLoaded = false
|
|
153
|
+
private var hasAppliedInitialCameraPosition = false
|
|
153
154
|
|
|
154
155
|
init {
|
|
155
156
|
try {
|
|
@@ -192,7 +193,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
192
193
|
|
|
193
194
|
val positionToApply = initialCameraPosition ?: pendingCameraPosition
|
|
194
195
|
positionToApply?.let { position ->
|
|
195
|
-
|
|
196
|
+
applyInitialCameraPositionIfNeeded(position)
|
|
196
197
|
pendingCameraPosition = null
|
|
197
198
|
}
|
|
198
199
|
|
|
@@ -351,10 +352,14 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
351
352
|
fun setInitialCameraPosition(position: Map<String, Any?>) {
|
|
352
353
|
initialCameraPosition = position
|
|
353
354
|
|
|
355
|
+
if (hasAppliedInitialCameraPosition) {
|
|
356
|
+
return
|
|
357
|
+
}
|
|
358
|
+
|
|
354
359
|
// 如果地图已加载,立即应用;否则缓存等待地图加载完成
|
|
355
360
|
if (isMapLoaded) {
|
|
356
361
|
mainHandler.post {
|
|
357
|
-
|
|
362
|
+
applyInitialCameraPositionIfNeeded(position)
|
|
358
363
|
}
|
|
359
364
|
} else {
|
|
360
365
|
pendingCameraPosition = position
|
|
@@ -365,8 +370,12 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
365
370
|
* 实际应用相机位置
|
|
366
371
|
* @param position 相机位置配置
|
|
367
372
|
*/
|
|
368
|
-
private fun
|
|
373
|
+
private fun applyInitialCameraPositionIfNeeded(position: Map<String, Any?>) {
|
|
374
|
+
if (hasAppliedInitialCameraPosition) {
|
|
375
|
+
return
|
|
376
|
+
}
|
|
369
377
|
cameraManager.setInitialCameraPosition(position)
|
|
378
|
+
hasAppliedInitialCameraPosition = true
|
|
370
379
|
}
|
|
371
380
|
|
|
372
381
|
// ==================== UI 控件和手势 ====================
|
|
@@ -643,6 +652,7 @@ class ExpoGaodeMapView(context: Context, appContext: AppContext) : ExpoView(cont
|
|
|
643
652
|
|
|
644
653
|
// 销毁地图实例
|
|
645
654
|
mapView.onDestroy()
|
|
655
|
+
hasAppliedInitialCameraPosition = false
|
|
646
656
|
} catch (e: Exception) {
|
|
647
657
|
// 静默处理异常,确保销毁流程不会中断
|
|
648
658
|
android.util.Log.e("ExpoGaodeMapView", "Error destroying map", e)
|
|
@@ -87,36 +87,46 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
87
87
|
// ==================== 图层显示 ====================
|
|
88
88
|
|
|
89
89
|
private var currentLocationStyle: MyLocationStyle? = null
|
|
90
|
+
private var currentFollowUserLocation: Boolean = false
|
|
91
|
+
private var explicitLocationType: Int? = null
|
|
92
|
+
|
|
93
|
+
private fun parseLocationType(locationType: String): Int? {
|
|
94
|
+
return when (locationType) {
|
|
95
|
+
"SHOW" -> MyLocationStyle.LOCATION_TYPE_SHOW
|
|
96
|
+
"LOCATE" -> MyLocationStyle.LOCATION_TYPE_LOCATE
|
|
97
|
+
"FOLLOW" -> MyLocationStyle.LOCATION_TYPE_FOLLOW
|
|
98
|
+
"MAP_ROTATE" -> MyLocationStyle.LOCATION_TYPE_MAP_ROTATE
|
|
99
|
+
"LOCATION_ROTATE" -> MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE
|
|
100
|
+
"LOCATION_ROTATE_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER
|
|
101
|
+
"FOLLOW_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER
|
|
102
|
+
"MAP_ROTATE_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_MAP_ROTATE_NO_CENTER
|
|
103
|
+
else -> null
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private fun resolveLocationType(followUserLocation: Boolean): Int {
|
|
108
|
+
explicitLocationType?.let { return it }
|
|
109
|
+
return if (followUserLocation) {
|
|
110
|
+
MyLocationStyle.LOCATION_TYPE_FOLLOW
|
|
111
|
+
} else {
|
|
112
|
+
MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER
|
|
113
|
+
}
|
|
114
|
+
}
|
|
90
115
|
|
|
91
116
|
/**
|
|
92
117
|
* 设置是否显示用户位置
|
|
93
118
|
*/
|
|
94
119
|
fun setShowsUserLocation(show: Boolean, followUserLocation: Boolean = false) {
|
|
120
|
+
currentFollowUserLocation = followUserLocation
|
|
95
121
|
if (show) {
|
|
96
|
-
// 创建默认的定位样式
|
|
97
122
|
if (currentLocationStyle == null) {
|
|
98
|
-
currentLocationStyle = MyLocationStyle()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
myLocationType(locationType)
|
|
106
|
-
interval(2000) // 2秒定位一次
|
|
107
|
-
showMyLocation(true)
|
|
108
|
-
}
|
|
109
|
-
} else {
|
|
110
|
-
// 更新定位类型
|
|
111
|
-
val locationType = if (followUserLocation) {
|
|
112
|
-
MyLocationStyle.LOCATION_TYPE_FOLLOW
|
|
113
|
-
} else {
|
|
114
|
-
MyLocationStyle.LOCATION_TYPE_SHOW
|
|
115
|
-
}
|
|
116
|
-
currentLocationStyle?.apply {
|
|
117
|
-
myLocationType(locationType)
|
|
118
|
-
interval(2000)
|
|
119
|
-
}
|
|
123
|
+
currentLocationStyle = MyLocationStyle()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
currentLocationStyle?.apply {
|
|
127
|
+
myLocationType(resolveLocationType(followUserLocation))
|
|
128
|
+
interval(2000) // 2秒定位一次
|
|
129
|
+
showMyLocation(true)
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
// 监听定位变化(用于通知 React Native)
|
|
@@ -171,9 +181,10 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
171
181
|
*/
|
|
172
182
|
@SuppressLint("DiscouragedApi")
|
|
173
183
|
fun setUserLocationRepresentation(config: Map<String, Any>) {
|
|
184
|
+
explicitLocationType = (config["locationType"] as? String)?.let(::parseLocationType)
|
|
185
|
+
|
|
174
186
|
if (currentLocationStyle == null) {
|
|
175
187
|
currentLocationStyle = MyLocationStyle().apply {
|
|
176
|
-
myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE)
|
|
177
188
|
interval(2000)
|
|
178
189
|
showMyLocation(true)
|
|
179
190
|
}
|
|
@@ -182,21 +193,7 @@ class UIManager(private val aMap: AMap, private val context: Context) : Location
|
|
|
182
193
|
val style = currentLocationStyle!!
|
|
183
194
|
|
|
184
195
|
// 定位蓝点展现模式 (locationType) - Android 支持8种模式
|
|
185
|
-
|
|
186
|
-
if (locationType != null) {
|
|
187
|
-
val locationTypeValue = when (locationType) {
|
|
188
|
-
"SHOW" -> MyLocationStyle.LOCATION_TYPE_SHOW
|
|
189
|
-
"LOCATE" -> MyLocationStyle.LOCATION_TYPE_LOCATE
|
|
190
|
-
"FOLLOW" -> MyLocationStyle.LOCATION_TYPE_FOLLOW
|
|
191
|
-
"MAP_ROTATE" -> MyLocationStyle.LOCATION_TYPE_MAP_ROTATE
|
|
192
|
-
"LOCATION_ROTATE" -> MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE
|
|
193
|
-
"LOCATION_ROTATE_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER
|
|
194
|
-
"FOLLOW_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_FOLLOW_NO_CENTER
|
|
195
|
-
"MAP_ROTATE_NO_CENTER" -> MyLocationStyle.LOCATION_TYPE_MAP_ROTATE_NO_CENTER
|
|
196
|
-
else -> MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE // 默认值
|
|
197
|
-
}
|
|
198
|
-
style.myLocationType(locationTypeValue)
|
|
199
|
-
}
|
|
196
|
+
style.myLocationType(resolveLocationType(currentFollowUserLocation))
|
|
200
197
|
|
|
201
198
|
// 是否显示定位蓝点 (showMyLocation) - Android 5.1.0+ 支持
|
|
202
199
|
// 注意:这个属性在 iOS 中没有对应项,是 Android 特有的
|
|
@@ -56,22 +56,6 @@ object SDKInitializer {
|
|
|
56
56
|
applyPrivacyState(appContext)
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
fun setPrivacyShow(context: Context, hasShow: Boolean, hasContainsPrivacy: Boolean) {
|
|
60
|
-
privacyShown = hasShow
|
|
61
|
-
privacyContains = hasContainsPrivacy
|
|
62
|
-
val appContext = resolveContext(context)
|
|
63
|
-
persistState(appContext)
|
|
64
|
-
applyPrivacyState(appContext)
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
fun setPrivacyAgree(context: Context, hasAgree: Boolean) {
|
|
68
|
-
privacyAgreed = hasAgree
|
|
69
|
-
agreedPrivacyVersion = if (hasAgree) privacyVersion else null
|
|
70
|
-
val appContext = resolveContext(context)
|
|
71
|
-
persistState(appContext)
|
|
72
|
-
applyPrivacyState(appContext)
|
|
73
|
-
}
|
|
74
|
-
|
|
75
59
|
fun setPrivacyVersion(context: Context, version: String) {
|
|
76
60
|
val sanitizedVersion = version.trim().takeIf { it.isNotEmpty() }
|
|
77
61
|
privacyVersion = sanitizedVersion
|
|
@@ -89,6 +73,28 @@ object SDKInitializer {
|
|
|
89
73
|
applyPrivacyState(appContext)
|
|
90
74
|
}
|
|
91
75
|
|
|
76
|
+
fun setPrivacyConfig(
|
|
77
|
+
context: Context,
|
|
78
|
+
hasShow: Boolean,
|
|
79
|
+
hasContainsPrivacy: Boolean,
|
|
80
|
+
hasAgree: Boolean,
|
|
81
|
+
version: String?,
|
|
82
|
+
shouldUpdateVersion: Boolean
|
|
83
|
+
) {
|
|
84
|
+
val appContext = resolveContext(context)
|
|
85
|
+
|
|
86
|
+
if (shouldUpdateVersion) {
|
|
87
|
+
privacyVersion = version?.trim()?.takeIf { it.isNotEmpty() }
|
|
88
|
+
}
|
|
89
|
+
privacyShown = hasShow
|
|
90
|
+
privacyContains = hasContainsPrivacy
|
|
91
|
+
privacyAgreed = hasAgree
|
|
92
|
+
agreedPrivacyVersion = if (hasAgree) privacyVersion else null
|
|
93
|
+
|
|
94
|
+
persistState(appContext)
|
|
95
|
+
applyPrivacyState(appContext)
|
|
96
|
+
}
|
|
97
|
+
|
|
92
98
|
fun resetPrivacyConsent(context: Context) {
|
|
93
99
|
val appContext = resolveContext(context)
|
|
94
100
|
clearConsentPersistedState(appContext, keepCurrentVersion = false)
|
|
@@ -138,7 +144,7 @@ object SDKInitializer {
|
|
|
138
144
|
if (!isPrivacyReady()) {
|
|
139
145
|
throw expo.modules.kotlin.exception.CodedException(
|
|
140
146
|
"PRIVACY_NOT_AGREED",
|
|
141
|
-
"隐私协议未完成确认,请先调用
|
|
147
|
+
"隐私协议未完成确认,请先调用 setPrivacyConfig",
|
|
142
148
|
null
|
|
143
149
|
)
|
|
144
150
|
}
|
|
@@ -141,8 +141,11 @@ class ClusterView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
141
141
|
* 设置最小聚合数量
|
|
142
142
|
*/
|
|
143
143
|
fun setMinClusterSize(size: Int) {
|
|
144
|
-
|
|
145
|
-
minClusterSize
|
|
144
|
+
val nextSize = size.coerceAtLeast(1)
|
|
145
|
+
if (minClusterSize != nextSize) {
|
|
146
|
+
minClusterSize = nextSize
|
|
147
|
+
styleChanged = true
|
|
148
|
+
}
|
|
146
149
|
updateClusters()
|
|
147
150
|
}
|
|
148
151
|
|
|
@@ -1,21 +1,27 @@
|
|
|
1
1
|
package expo.modules.gaodemap.map.overlays
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
|
+
import android.graphics.Color
|
|
4
5
|
import android.os.Looper
|
|
5
6
|
import android.util.Log
|
|
6
7
|
import com.amap.api.maps.AMap
|
|
8
|
+
import com.amap.api.maps.CameraUpdateFactory
|
|
9
|
+
import com.amap.api.maps.model.Gradient
|
|
10
|
+
import com.amap.api.maps.model.HeatmapTileProvider
|
|
7
11
|
import com.amap.api.maps.model.LatLng
|
|
8
12
|
import com.amap.api.maps.model.TileOverlay
|
|
9
13
|
import com.amap.api.maps.model.TileOverlayOptions
|
|
10
|
-
import com.amap.api.maps.model.
|
|
11
|
-
import com.amap.api.maps.CameraUpdateFactory
|
|
14
|
+
import com.amap.api.maps.model.WeightedLatLng
|
|
12
15
|
import expo.modules.kotlin.AppContext
|
|
13
16
|
import expo.modules.kotlin.views.ExpoView
|
|
17
|
+
import expo.modules.gaodemap.map.utils.LatLngParser
|
|
14
18
|
import java.util.concurrent.ExecutorService
|
|
15
19
|
import java.util.concurrent.Executors
|
|
16
|
-
import expo.modules.gaodemap.map.utils.LatLngParser
|
|
17
20
|
|
|
18
21
|
class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context, appContext) {
|
|
22
|
+
private companion object {
|
|
23
|
+
const val TAG = "HeatMapView"
|
|
24
|
+
}
|
|
19
25
|
|
|
20
26
|
private val executor: ExecutorService = Executors.newSingleThreadExecutor()
|
|
21
27
|
private val applyUpdateRunnable = Runnable { applyUpdateOnMain() }
|
|
@@ -25,9 +31,10 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
25
31
|
|
|
26
32
|
private var heatmapOverlay: TileOverlay? = null
|
|
27
33
|
private var aMap: AMap? = null
|
|
28
|
-
private var dataList: MutableList<
|
|
34
|
+
private var dataList: MutableList<WeightedLatLng> = mutableListOf()
|
|
29
35
|
private var radius: Int = 50
|
|
30
36
|
private var opacity: Double = 0.6
|
|
37
|
+
private var gradient: Gradient? = null
|
|
31
38
|
|
|
32
39
|
/**
|
|
33
40
|
* 设置地图实例
|
|
@@ -36,6 +43,7 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
36
43
|
fun setMap(map: AMap) {
|
|
37
44
|
aMap = map
|
|
38
45
|
needsRebuild = true
|
|
46
|
+
Log.d(TAG, "setMap: map attached")
|
|
39
47
|
scheduleUpdate()
|
|
40
48
|
}
|
|
41
49
|
|
|
@@ -46,7 +54,8 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
46
54
|
*/
|
|
47
55
|
fun setData(data: List<Any>?) {
|
|
48
56
|
dataList.clear()
|
|
49
|
-
dataList.addAll(
|
|
57
|
+
dataList.addAll(parseWeightedLatLngList(data))
|
|
58
|
+
Log.d(TAG, "setData: raw=${data?.size ?: 0}, parsed=${dataList.size}, ${formatPointStats(dataList)}")
|
|
50
59
|
needsRebuild = true
|
|
51
60
|
scheduleUpdate()
|
|
52
61
|
}
|
|
@@ -56,6 +65,7 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
56
65
|
*/
|
|
57
66
|
fun setRadius(radiusValue: Int) {
|
|
58
67
|
radius = radiusValue
|
|
68
|
+
Log.d(TAG, "setRadius: $radiusValue")
|
|
59
69
|
needsRebuild = true
|
|
60
70
|
scheduleUpdate()
|
|
61
71
|
}
|
|
@@ -65,10 +75,20 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
65
75
|
*/
|
|
66
76
|
fun setOpacity(opacityValue: Double) {
|
|
67
77
|
opacity = opacityValue
|
|
68
|
-
|
|
78
|
+
Log.d(TAG, "setOpacity: $opacityValue")
|
|
79
|
+
needsRebuild = true
|
|
80
|
+
scheduleUpdate()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fun setGradient(gradientValue: Map<String, Any>?) {
|
|
84
|
+
gradient = parseGradient(gradientValue)
|
|
85
|
+
Log.d(TAG, "setGradient: hasGradient=${gradient != null}, raw=${gradientValue != null}")
|
|
86
|
+
needsRebuild = true
|
|
87
|
+
scheduleUpdate()
|
|
69
88
|
}
|
|
70
89
|
|
|
71
90
|
fun setVisible(visibleValue: Boolean) {
|
|
91
|
+
Log.d(TAG, "setVisible: $visibleValue, points=${dataList.size}, hasOverlay=${heatmapOverlay != null}, needsRebuild=$needsRebuild")
|
|
72
92
|
if (!visibleValue) {
|
|
73
93
|
visible = false
|
|
74
94
|
updateToken += 1
|
|
@@ -95,6 +115,7 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
95
115
|
private fun scheduleUpdate() {
|
|
96
116
|
updateToken += 1
|
|
97
117
|
removeCallbacks(applyUpdateRunnable)
|
|
118
|
+
Log.d(TAG, "scheduleUpdate: token=$updateToken, mapAttached=${aMap != null}, visible=$visible, points=${dataList.size}")
|
|
98
119
|
postDelayed(applyUpdateRunnable, 32)
|
|
99
120
|
}
|
|
100
121
|
|
|
@@ -142,8 +163,11 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
142
163
|
val map = aMap ?: return
|
|
143
164
|
val token = updateToken
|
|
144
165
|
val pointsSnapshot = ArrayList(dataList)
|
|
166
|
+
val latLngSnapshot = pointsSnapshot.map { it.latLng }
|
|
145
167
|
val radiusValue = radius.coerceIn(10, 200)
|
|
146
168
|
val opacityValue = opacity.coerceIn(0.0, 1.0)
|
|
169
|
+
val gradientValue = gradient
|
|
170
|
+
Log.d(TAG, "applyUpdate: token=$token, visible=$visible, points=${pointsSnapshot.size}, radius=$radiusValue, opacity=$opacityValue, gradient=${gradientValue != null}, ${formatPointStats(pointsSnapshot)}")
|
|
147
171
|
|
|
148
172
|
if (!visible) {
|
|
149
173
|
applyOverlayVisibility()
|
|
@@ -151,6 +175,7 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
151
175
|
}
|
|
152
176
|
|
|
153
177
|
if (pointsSnapshot.isEmpty()) {
|
|
178
|
+
Log.w(TAG, "applyUpdate: no valid heatmap points, removing overlay")
|
|
154
179
|
heatmapOverlay?.remove()
|
|
155
180
|
heatmapOverlay = null
|
|
156
181
|
return
|
|
@@ -164,19 +189,25 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
164
189
|
|
|
165
190
|
executor.execute {
|
|
166
191
|
try {
|
|
167
|
-
val
|
|
168
|
-
.data(
|
|
192
|
+
val builder = HeatmapTileProvider.Builder()
|
|
193
|
+
.data(latLngSnapshot)
|
|
169
194
|
.radius(radiusValue)
|
|
170
|
-
|
|
195
|
+
|
|
196
|
+
gradientValue?.let { builder.gradient(it) }
|
|
197
|
+
|
|
198
|
+
val provider = builder.build()
|
|
171
199
|
|
|
172
200
|
post {
|
|
173
201
|
if (token != updateToken) {
|
|
202
|
+
Log.d(TAG, "addOverlay skipped: stale token=$token, current=$updateToken")
|
|
174
203
|
return@post
|
|
175
204
|
}
|
|
176
205
|
if (aMap !== map) {
|
|
206
|
+
Log.d(TAG, "addOverlay skipped: map instance changed")
|
|
177
207
|
return@post
|
|
178
208
|
}
|
|
179
209
|
if (!visible) {
|
|
210
|
+
Log.d(TAG, "addOverlay skipped: hidden")
|
|
180
211
|
return@post
|
|
181
212
|
}
|
|
182
213
|
|
|
@@ -200,9 +231,10 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
200
231
|
applyOverlayVisibility()
|
|
201
232
|
applyOverlayOpacity()
|
|
202
233
|
forceRefresh()
|
|
234
|
+
Log.i(TAG, "addOverlay success: id=${runCatching { heatmapOverlay?.id }.getOrNull()}, points=${pointsSnapshot.size}, radius=$radiusValue, opacity=$opacityValue")
|
|
203
235
|
}
|
|
204
236
|
} catch (t: Throwable) {
|
|
205
|
-
Log.e(
|
|
237
|
+
Log.e(TAG, "Failed to build heatmap", t)
|
|
206
238
|
}
|
|
207
239
|
}
|
|
208
240
|
}
|
|
@@ -217,11 +249,91 @@ class HeatMapView(context: Context, appContext: AppContext) : ExpoView(context,
|
|
|
217
249
|
fun removeHeatMap() {
|
|
218
250
|
updateToken += 1
|
|
219
251
|
removeCallbacks(applyUpdateRunnable)
|
|
252
|
+
Log.d(TAG, "removeHeatMap")
|
|
220
253
|
heatmapOverlay?.remove()
|
|
221
254
|
heatmapOverlay = null
|
|
222
255
|
dataList.clear()
|
|
223
256
|
needsRebuild = true
|
|
224
257
|
}
|
|
258
|
+
|
|
259
|
+
private fun parseWeightedLatLngList(data: Any?): List<WeightedLatLng> {
|
|
260
|
+
if (data == null || data !is List<*>) return emptyList()
|
|
261
|
+
|
|
262
|
+
val result = mutableListOf<WeightedLatLng>()
|
|
263
|
+
for (item in data) {
|
|
264
|
+
val point = parseWeightedLatLng(item)
|
|
265
|
+
if (point != null) {
|
|
266
|
+
result.add(point)
|
|
267
|
+
} else if (item is List<*>) {
|
|
268
|
+
result.addAll(parseWeightedLatLngList(item))
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return result
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
private fun parseWeightedLatLng(data: Any?): WeightedLatLng? {
|
|
275
|
+
val latLng = LatLngParser.parseLatLng(data) ?: return null
|
|
276
|
+
val intensity = when (data) {
|
|
277
|
+
is Map<*, *> -> listOf(data["intensity"], data["weight"], data["count"], data["value"])
|
|
278
|
+
.firstOrNull { it is Number }
|
|
279
|
+
is List<*> -> data.getOrNull(2)
|
|
280
|
+
else -> null
|
|
281
|
+
}
|
|
282
|
+
val weight = ((intensity as? Number)?.toDouble() ?: 1.0).takeIf { it.isFinite() && it > 0.0 } ?: 1.0
|
|
283
|
+
return WeightedLatLng(latLng, weight)
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private fun parseGradient(gradientValue: Map<String, Any>?): Gradient? {
|
|
287
|
+
if (gradientValue == null) return null
|
|
288
|
+
val rawColors = gradientValue["colors"] as? List<*> ?: return null
|
|
289
|
+
val rawStartPoints = gradientValue["startPoints"] as? List<*> ?: return null
|
|
290
|
+
if (rawColors.size < 2 || rawColors.size != rawStartPoints.size) {
|
|
291
|
+
Log.w(TAG, "parseGradient ignored: colors=${rawColors.size}, startPoints=${rawStartPoints.size}")
|
|
292
|
+
return null
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
val colors = IntArray(rawColors.size)
|
|
296
|
+
val startPoints = FloatArray(rawStartPoints.size)
|
|
297
|
+
for (index in rawColors.indices) {
|
|
298
|
+
val color = parseColor(rawColors[index]) ?: run {
|
|
299
|
+
Log.w(TAG, "parseGradient ignored: invalid color at index=$index, value=${rawColors[index]}")
|
|
300
|
+
return null
|
|
301
|
+
}
|
|
302
|
+
val startPoint = (rawStartPoints[index] as? Number)?.toFloat() ?: run {
|
|
303
|
+
Log.w(TAG, "parseGradient ignored: invalid startPoint at index=$index, value=${rawStartPoints[index]}")
|
|
304
|
+
return null
|
|
305
|
+
}
|
|
306
|
+
colors[index] = color
|
|
307
|
+
startPoints[index] = startPoint.coerceIn(0f, 1f)
|
|
308
|
+
}
|
|
309
|
+
return Gradient(colors, startPoints)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
private fun parseColor(value: Any?): Int? {
|
|
313
|
+
return when (value) {
|
|
314
|
+
is Number -> value.toInt()
|
|
315
|
+
is String -> runCatching { Color.parseColor(value) }.getOrNull()
|
|
316
|
+
else -> null
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private fun formatPointStats(points: List<WeightedLatLng>): String {
|
|
321
|
+
if (points.isEmpty()) return "bounds=empty"
|
|
322
|
+
var minLat = Double.POSITIVE_INFINITY
|
|
323
|
+
var maxLat = Double.NEGATIVE_INFINITY
|
|
324
|
+
var minLng = Double.POSITIVE_INFINITY
|
|
325
|
+
var maxLng = Double.NEGATIVE_INFINITY
|
|
326
|
+
var totalIntensity = 0.0
|
|
327
|
+
points.forEach { point ->
|
|
328
|
+
val latLng: LatLng = point.latLng
|
|
329
|
+
minLat = minOf(minLat, latLng.latitude)
|
|
330
|
+
maxLat = maxOf(maxLat, latLng.latitude)
|
|
331
|
+
minLng = minOf(minLng, latLng.longitude)
|
|
332
|
+
maxLng = maxOf(maxLng, latLng.longitude)
|
|
333
|
+
totalIntensity += point.intensity
|
|
334
|
+
}
|
|
335
|
+
return "bounds=[$minLat,$minLng]-[$maxLat,$maxLng], totalIntensity=$totalIntensity"
|
|
336
|
+
}
|
|
225
337
|
|
|
226
338
|
override fun onDetachedFromWindow() {
|
|
227
339
|
super.onDetachedFromWindow()
|
|
@@ -27,8 +27,8 @@ class HeatMapViewModule : Module() {
|
|
|
27
27
|
view.setOpacity(opacity)
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
Prop<Map<String, Any>?>("gradient") {
|
|
31
|
-
|
|
30
|
+
Prop<Map<String, Any>?>("gradient") { view: HeatMapView, gradient ->
|
|
31
|
+
view.setGradient(gradient)
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
Prop<Boolean>("allowRetinaAdapting") { _: HeatMapView, _ ->
|