expo-gaode-map-navigation 2.0.11 → 2.0.12

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 (71) hide show
  1. package/android/src/main/cpp/cluster_jni.cpp +56 -0
  2. package/android/src/main/java/expo/modules/gaodemap/map/ExpoGaodeMapModule.kt +45 -6
  3. package/android/src/main/java/expo/modules/gaodemap/map/modules/SDKInitializer.kt +23 -17
  4. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerView.kt +37 -25
  5. package/android/src/main/java/expo/modules/gaodemap/map/overlays/MarkerViewModule.kt +6 -6
  6. package/android/src/main/java/expo/modules/gaodemap/map/utils/GeometryUtils.kt +103 -0
  7. package/android/src/main/java/expo/modules/gaodemap/navigation/ExpoGaodeMapNaviView.kt +1 -1
  8. package/build/index.d.ts +10 -1
  9. package/build/index.d.ts.map +1 -1
  10. package/build/index.js +6 -3
  11. package/build/index.js.map +1 -1
  12. package/build/map/ExpoGaodeMapModule.d.ts +15 -13
  13. package/build/map/ExpoGaodeMapModule.d.ts.map +1 -1
  14. package/build/map/ExpoGaodeMapModule.js +31 -39
  15. package/build/map/ExpoGaodeMapModule.js.map +1 -1
  16. package/build/map/ExpoGaodeMapView.d.ts +3 -4
  17. package/build/map/ExpoGaodeMapView.d.ts.map +1 -1
  18. package/build/map/ExpoGaodeMapView.js +28 -25
  19. package/build/map/ExpoGaodeMapView.js.map +1 -1
  20. package/build/map/components/overlays/Circle.d.ts.map +1 -1
  21. package/build/map/components/overlays/Circle.js +1 -30
  22. package/build/map/components/overlays/Circle.js.map +1 -1
  23. package/build/map/components/overlays/Cluster.d.ts.map +1 -1
  24. package/build/map/components/overlays/Cluster.js +1 -42
  25. package/build/map/components/overlays/Cluster.js.map +1 -1
  26. package/build/map/components/overlays/HeatMap.d.ts.map +1 -1
  27. package/build/map/components/overlays/HeatMap.js +1 -20
  28. package/build/map/components/overlays/HeatMap.js.map +1 -1
  29. package/build/map/components/overlays/Marker.d.ts.map +1 -1
  30. package/build/map/components/overlays/Marker.js +76 -80
  31. package/build/map/components/overlays/Marker.js.map +1 -1
  32. package/build/map/components/overlays/Polygon.d.ts.map +1 -1
  33. package/build/map/components/overlays/Polygon.js +1 -25
  34. package/build/map/components/overlays/Polygon.js.map +1 -1
  35. package/build/map/components/overlays/Polyline.d.ts.map +1 -1
  36. package/build/map/components/overlays/Polyline.js +1 -31
  37. package/build/map/components/overlays/Polyline.js.map +1 -1
  38. package/build/map/index.d.ts +6 -2
  39. package/build/map/index.d.ts.map +1 -1
  40. package/build/map/index.js +6 -2
  41. package/build/map/index.js.map +1 -1
  42. package/build/map/types/index.d.ts +2 -2
  43. package/build/map/types/index.d.ts.map +1 -1
  44. package/build/map/types/index.js.map +1 -1
  45. package/build/map/types/native-module.types.d.ts +11 -12
  46. package/build/map/types/native-module.types.d.ts.map +1 -1
  47. package/build/map/types/native-module.types.js.map +1 -1
  48. package/build/map/types/overlays.types.d.ts +9 -14
  49. package/build/map/types/overlays.types.d.ts.map +1 -1
  50. package/build/map/types/overlays.types.js.map +1 -1
  51. package/build/map/types/route-playback.types.d.ts +16 -0
  52. package/build/map/types/route-playback.types.d.ts.map +1 -1
  53. package/build/map/types/route-playback.types.js.map +1 -1
  54. package/build/types/coordinates.types.d.ts +3 -0
  55. package/build/types/coordinates.types.d.ts.map +1 -1
  56. package/build/types/coordinates.types.js.map +1 -1
  57. package/ios/ExpoGaodeMapNaviView.swift +31 -2
  58. package/ios/map/ExpoGaodeMapModule.swift +38 -6
  59. package/ios/map/ExpoGaodeMapView.swift +10 -3
  60. package/ios/map/GaodeMapPrivacyManager.swift +26 -18
  61. package/ios/map/cpp/GeometryEngine.cpp +112 -0
  62. package/ios/map/cpp/GeometryEngine.hpp +21 -0
  63. package/ios/map/modules/LocationManager.swift +1 -1
  64. package/ios/map/overlays/MarkerView.swift +11 -11
  65. package/ios/map/overlays/MarkerViewModule.swift +4 -4
  66. package/ios/map/utils/ClusterNative.h +8 -0
  67. package/ios/map/utils/ClusterNative.mm +27 -0
  68. package/package.json +6 -4
  69. package/scripts/check-expo-modules.js +68 -0
  70. package/shared/cpp/GeometryEngine.cpp +112 -0
  71. package/shared/cpp/GeometryEngine.hpp +21 -0
@@ -653,6 +653,62 @@ Java_expo_modules_gaodemap_map_utils_GeometryUtils_nativeCalculateCentroid(
653
653
  #endif
654
654
  }
655
655
 
656
+ extern "C" JNIEXPORT jdouble JNICALL
657
+ Java_expo_modules_gaodemap_map_utils_GeometryUtils_nativeCalculateFitZoom(
658
+ JNIEnv* env,
659
+ jclass,
660
+ jdoubleArray latitudes,
661
+ jdoubleArray longitudes,
662
+ jdouble viewportWidthPx,
663
+ jdouble viewportHeightPx,
664
+ jdouble paddingPx,
665
+ jint minZoom,
666
+ jint maxZoom
667
+ ) {
668
+ #if GAODE_HAVE_JNI
669
+ if (!latitudes || !longitudes) {
670
+ return static_cast<jdouble>(minZoom);
671
+ }
672
+
673
+ const jsize countLat = env->GetArrayLength(latitudes);
674
+ const jsize countLon = env->GetArrayLength(longitudes);
675
+ if (countLat == 0 || countLat != countLon) {
676
+ return static_cast<jdouble>(minZoom);
677
+ }
678
+
679
+ jdouble* latValues = env->GetDoubleArrayElements(latitudes, nullptr);
680
+ jdouble* lonValues = env->GetDoubleArrayElements(longitudes, nullptr);
681
+
682
+ std::vector<gaodemap::GeoPoint> points;
683
+ points.reserve(static_cast<size_t>(countLat));
684
+ for (jsize i = 0; i < countLat; ++i) {
685
+ points.push_back({latValues[i], lonValues[i]});
686
+ }
687
+
688
+ env->ReleaseDoubleArrayElements(latitudes, latValues, JNI_ABORT);
689
+ env->ReleaseDoubleArrayElements(longitudes, lonValues, JNI_ABORT);
690
+
691
+ return static_cast<jdouble>(gaodemap::calculateFitZoomForPoints(
692
+ points,
693
+ static_cast<double>(viewportWidthPx),
694
+ static_cast<double>(viewportHeightPx),
695
+ static_cast<double>(paddingPx),
696
+ static_cast<int>(minZoom),
697
+ static_cast<int>(maxZoom)
698
+ ));
699
+ #else
700
+ (void)env;
701
+ (void)latitudes;
702
+ (void)longitudes;
703
+ (void)viewportWidthPx;
704
+ (void)viewportHeightPx;
705
+ (void)paddingPx;
706
+ (void)minZoom;
707
+ (void)maxZoom;
708
+ return 3.0;
709
+ #endif
710
+ }
711
+
656
712
  extern "C" JNIEXPORT jstring JNICALL
657
713
  Java_expo_modules_gaodemap_map_utils_GeometryUtils_nativeEncodeGeoHash(
658
714
  JNIEnv* env,
@@ -51,7 +51,7 @@ class ExpoGaodeMapModule : Module() {
51
51
  } else if (!SDKInitializer.isPrivacyReady()) {
52
52
  throw expo.modules.kotlin.exception.CodedException(
53
53
  "PRIVACY_NOT_AGREED",
54
- "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
54
+ "隐私协议未完成确认,请先调用 setPrivacyConfig",
55
55
  null
56
56
  )
57
57
  } else {
@@ -68,12 +68,20 @@ class ExpoGaodeMapModule : Module() {
68
68
  }
69
69
  }
70
70
 
71
- Function("setPrivacyShow") { hasShow: Boolean, hasContainsPrivacy: Boolean ->
72
- SDKInitializer.setPrivacyShow(appContext.reactContext!!, hasShow, hasContainsPrivacy)
73
- }
71
+ Function("setPrivacyConfig") { config: Map<String, Any?> ->
72
+ val hasShow = config["hasShow"] as? Boolean ?: false
73
+ val hasContainsPrivacy = config["hasContainsPrivacy"] as? Boolean ?: hasShow
74
+ val hasAgree = config["hasAgree"] as? Boolean ?: false
75
+ val privacyVersion = config["privacyVersion"] as? String
74
76
 
75
- Function("setPrivacyAgree") { hasAgree: Boolean ->
76
- SDKInitializer.setPrivacyAgree(appContext.reactContext!!, hasAgree)
77
+ SDKInitializer.setPrivacyConfig(
78
+ appContext.reactContext!!,
79
+ hasShow,
80
+ hasContainsPrivacy,
81
+ hasAgree,
82
+ privacyVersion,
83
+ config.containsKey("privacyVersion")
84
+ )
77
85
  }
78
86
 
79
87
  Function("setPrivacyVersion") { version: String ->
@@ -186,6 +194,37 @@ class ExpoGaodeMapModule : Module() {
186
194
  }
187
195
  }
188
196
 
197
+ /**
198
+ * 根据多个坐标点计算可同时可见的推荐缩放级别
199
+ * @param points 坐标点(至少 1 个)
200
+ * @param viewportWidthPx 视口宽度(像素)
201
+ * @param viewportHeightPx 视口高度(像素)
202
+ * @param paddingPx 内边距(像素)
203
+ * @param minZoom 最小缩放
204
+ * @param maxZoom 最大缩放
205
+ */
206
+ Function("calculateFitZoom") {
207
+ points: List<Any>?,
208
+ viewportWidthPx: Double?,
209
+ viewportHeightPx: Double?,
210
+ paddingPx: Double?,
211
+ minZoom: Int?,
212
+ maxZoom: Int? ->
213
+ val normalized = LatLngParser.parseLatLngList(points)
214
+ val safeMinZoom = minZoom ?: 3
215
+ val safeMaxZoom = maxZoom ?: 20
216
+ if (normalized.isEmpty()) return@Function safeMinZoom.toDouble()
217
+
218
+ GeometryUtils.calculateFitZoom(
219
+ normalized,
220
+ viewportWidthPx ?: 390.0,
221
+ viewportHeightPx ?: 844.0,
222
+ paddingPx ?: 48.0,
223
+ safeMinZoom,
224
+ safeMaxZoom
225
+ )
226
+ }
227
+
189
228
  /**
190
229
  * 计算多边形面积
191
230
  * @param points 多边形顶点坐标数组,支持嵌套数组(多边形空洞)
@@ -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
- "隐私协议未完成确认,请先调用 setPrivacyShow/setPrivacyAgree",
147
+ "隐私协议未完成确认,请先调用 setPrivacyConfig",
142
148
  null
143
149
  )
144
150
  }
@@ -99,16 +99,16 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
99
99
  override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
100
100
  val selfParams = this.layoutParams
101
101
  if (selfParams == null || selfParams !is LayoutParams) {
102
- val width = if (customViewWidth > 0) {
103
- customViewWidth
102
+ val width = if (contentWidth > 0) {
103
+ contentWidth
104
104
  } else if (selfParams != null && selfParams.width > 0) {
105
105
  selfParams.width
106
106
  } else {
107
107
  LayoutParams.WRAP_CONTENT
108
108
  }
109
109
 
110
- val height = if (customViewHeight > 0) {
111
- customViewHeight
110
+ val height = if (contentHeight > 0) {
111
+ contentHeight
112
112
  } else if (selfParams != null && selfParams.height > 0) {
113
113
  selfParams.height
114
114
  } else {
@@ -140,12 +140,12 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
140
140
  val fallbackHeightSize = resolveExplicitMeasureSize(parentHeightSize, false)
141
141
 
142
142
  val contentWidthSpec = when {
143
- customViewWidth > 0 -> MeasureSpec.makeMeasureSpec(customViewWidth, MeasureSpec.EXACTLY)
143
+ contentWidth > 0 -> MeasureSpec.makeMeasureSpec(contentWidth, MeasureSpec.EXACTLY)
144
144
  fallbackWidthSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackWidthSize, MeasureSpec.AT_MOST)
145
145
  else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
146
146
  }
147
147
  val contentHeightSpec = when {
148
- customViewHeight > 0 -> MeasureSpec.makeMeasureSpec(customViewHeight, MeasureSpec.EXACTLY)
148
+ contentHeight > 0 -> MeasureSpec.makeMeasureSpec(contentHeight, MeasureSpec.EXACTLY)
149
149
  fallbackHeightSize > 0 -> MeasureSpec.makeMeasureSpec(fallbackHeightSize, MeasureSpec.AT_MOST)
150
150
  else -> MeasureSpec.makeMeasureSpec(1, MeasureSpec.EXACTLY)
151
151
  }
@@ -175,13 +175,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
175
175
  measuredContentHeight = max(measuredContentHeight, childBounds?.height() ?: child.measuredHeight)
176
176
  }
177
177
 
178
- val desiredWidth = if (customViewWidth > 0) {
179
- customViewWidth
178
+ val desiredWidth = if (contentWidth > 0) {
179
+ contentWidth
180
180
  } else {
181
181
  measuredContentWidth + paddingLeft + paddingRight
182
182
  }
183
- val desiredHeight = if (customViewHeight > 0) {
184
- customViewHeight
183
+ val desiredHeight = if (contentHeight > 0) {
184
+ contentHeight
185
185
  } else {
186
186
  measuredContentHeight + paddingTop + paddingBottom
187
187
  }
@@ -235,8 +235,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
235
235
  private var pendingLongitude: Double? = null // 临时存储经度
236
236
  private var iconWidth: Int = 0 // 用于自定义图标的宽度
237
237
  private var iconHeight: Int = 0 // 用于自定义图标的高度
238
- private var customViewWidth: Int = 0 // 用于自定义视图(children)的宽度
239
- private var customViewHeight: Int = 0 // 用于自定义视图(children)的高度
238
+ private var contentWidth: Int = 0 // 用于自定义视图(children)的宽度
239
+ private var contentHeight: Int = 0 // 用于自定义视图(children)的高度
240
240
  private val mainHandler = Handler(Looper.getMainLooper())
241
241
  private var isRemoving = false // 标记是否正在被移除
242
242
  private var pendingMarkerIconUpdate: Runnable? = null
@@ -733,21 +733,33 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
733
733
  }
734
734
 
735
735
  /**
736
- * 设置自定义视图宽度(用于 children 属性)
736
+ * 设置内容宽度(用于 children 属性)
737
737
  * 注意:React Native 传入的是 DP 值,需要转换为 PX
738
738
  */
739
- fun setCustomViewWidth(width: Int) {
739
+ fun setContentWidth(width: Int) {
740
740
  val density = context.resources.displayMetrics.density
741
- customViewWidth = (width * density).toInt()
741
+ val resolvedWidth = (width * density).toInt()
742
+ if (contentWidth == resolvedWidth) {
743
+ return
744
+ }
745
+
746
+ contentWidth = resolvedWidth
747
+ scheduleMarkerIconUpdate()
742
748
  }
743
749
 
744
750
  /**
745
- * 设置自定义视图高度(用于 children 属性)
751
+ * 设置内容高度(用于 children 属性)
746
752
  * 注意:React Native 传入的是 DP 值,需要转换为 PX
747
753
  */
748
- fun setCustomViewHeight(height: Int) {
754
+ fun setContentHeight(height: Int) {
749
755
  val density = context.resources.displayMetrics.density
750
- customViewHeight = (height * density).toInt()
756
+ val resolvedHeight = (height * density).toInt()
757
+ if (contentHeight == resolvedHeight) {
758
+ return
759
+ }
760
+
761
+ contentHeight = resolvedHeight
762
+ scheduleMarkerIconUpdate()
751
763
  }
752
764
 
753
765
  /**
@@ -897,8 +909,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
897
909
  val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: measuredChild?.measuredWidth ?: 0
898
910
  val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: measuredChild?.measuredHeight ?: 0
899
911
 
900
- val finalWidth = if (measuredWidth > 0) measuredWidth else (if (customViewWidth > 0) customViewWidth else 0)
901
- val finalHeight = if (measuredHeight > 0) measuredHeight else (if (customViewHeight > 0) customViewHeight else 0)
912
+ val finalWidth = if (measuredWidth > 0) measuredWidth else (if (contentWidth > 0) contentWidth else 0)
913
+ val finalHeight = if (measuredHeight > 0) measuredHeight else (if (contentHeight > 0) contentHeight else 0)
902
914
 
903
915
  // 🔑 修复:如果尺寸为 0,说明 View 还没准备好,不要生成 Bitmap,否则会导致动画位置偏移
904
916
  if (finalWidth <= 0 || finalHeight <= 0) {
@@ -978,7 +990,7 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
978
990
  childView.isDrawingCacheEnabled = false
979
991
  childView.destroyDrawingCache()
980
992
 
981
- val shouldTrimTransparentPadding = customViewWidth <= 0 && customViewHeight <= 0
993
+ val shouldTrimTransparentPadding = contentWidth <= 0 && contentHeight <= 0
982
994
  return if (shouldTrimTransparentPadding) trimTransparentPadding(bitmap) else bitmap
983
995
  } catch (_: Exception) {
984
996
  // 遇到异常时返回 null,让上层使用默认图标
@@ -1079,8 +1091,8 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1079
1091
  val child = getChildAt(0)
1080
1092
  val contentView = resolveRenderableContentView(child)
1081
1093
  val contentBounds = computeContentBounds(child)
1082
- val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: child?.measuredWidth ?: customViewWidth
1083
- val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: child?.measuredHeight ?: customViewHeight
1094
+ val measuredWidth = contentBounds?.width() ?: contentView?.measuredWidth ?: child?.measuredWidth ?: contentWidth
1095
+ val measuredHeight = contentBounds?.height() ?: contentView?.measuredHeight ?: child?.measuredHeight ?: contentHeight
1084
1096
  val fullCacheKey = "$keyPart|${measuredWidth}x${measuredHeight}"
1085
1097
 
1086
1098
  // 确定锚点:优先使用用户指定的 pendingAnchor,否则对于自定义 View 使用中心点 (0.5, 0.5)
@@ -1236,13 +1248,13 @@ class MarkerView(context: Context, appContext: AppContext) : ExpoView(context, a
1236
1248
  val childCountBefore = childCount
1237
1249
 
1238
1250
  val sourceWidth = when {
1239
- customViewWidth > 0 -> customViewWidth
1251
+ contentWidth > 0 -> contentWidth
1240
1252
  params?.width != null && params.width > 0 -> params.width
1241
1253
  else -> LayoutParams.WRAP_CONTENT
1242
1254
  }
1243
1255
 
1244
1256
  val sourceHeight = when {
1245
- customViewHeight > 0 -> customViewHeight
1257
+ contentHeight > 0 -> contentHeight
1246
1258
  params?.height != null && params.height > 0 -> params.height
1247
1259
  else -> LayoutParams.WRAP_CONTENT
1248
1260
  }
@@ -69,13 +69,13 @@ class MarkerViewModule : Module() {
69
69
  Prop<Int>("iconHeight") { view, height ->
70
70
  view.setIconHeight(height)
71
71
  }
72
- // 自定义视图宽度
73
- Prop<Int>("customViewWidth") { view, width ->
74
- view.setCustomViewWidth(width)
72
+ // 内容宽度
73
+ Prop<Int>("contentWidth") { view, width ->
74
+ view.setContentWidth(width)
75
75
  }
76
- // 自定义视图高度
77
- Prop<Int>("customViewHeight") { view, height ->
78
- view.setCustomViewHeight(height)
76
+ // 内容高度
77
+ Prop<Int>("contentHeight") { view, height ->
78
+ view.setContentHeight(height)
79
79
  }
80
80
  // 缓存key
81
81
  Prop<String?>("cacheKey") { view, key ->
@@ -83,6 +83,16 @@ object GeometryUtils {
83
83
  longitudes: DoubleArray
84
84
  ): DoubleArray?
85
85
 
86
+ private external fun nativeCalculateFitZoom(
87
+ latitudes: DoubleArray,
88
+ longitudes: DoubleArray,
89
+ viewportWidthPx: Double,
90
+ viewportHeightPx: Double,
91
+ paddingPx: Double,
92
+ minZoom: Int,
93
+ maxZoom: Int
94
+ ): Double
95
+
86
96
  private external fun nativeEncodeGeoHash(
87
97
  lat: Double,
88
98
  lon: Double,
@@ -401,6 +411,99 @@ object GeometryUtils {
401
411
  }
402
412
  }
403
413
 
414
+ private fun mercatorX01(lon: Double): Double {
415
+ var wrapped = (lon + 180.0) % 360.0
416
+ if (wrapped < 0.0) wrapped += 360.0
417
+ return wrapped / 360.0
418
+ }
419
+
420
+ private fun mercatorY01(lat: Double): Double {
421
+ val clamped = lat.coerceIn(-85.05112878, 85.05112878)
422
+ val rad = Math.toRadians(clamped)
423
+ val y = (1.0 - asinh(tan(rad)) / Math.PI) * 0.5
424
+ return y.coerceIn(0.0, 1.0)
425
+ }
426
+
427
+ private fun wrappedSpan01(values: List<Double>): Double {
428
+ if (values.size <= 1) return 0.0
429
+ val sorted = values.sorted()
430
+ var maxGap = 0.0
431
+ for (i in 0 until sorted.size - 1) {
432
+ maxGap = max(maxGap, sorted[i + 1] - sorted[i])
433
+ }
434
+ maxGap = max(maxGap, sorted.first() + 1.0 - sorted.last())
435
+ return (1.0 - maxGap).coerceAtLeast(0.0)
436
+ }
437
+
438
+ private fun fallbackCalculateFitZoom(
439
+ points: List<LatLng>,
440
+ viewportWidthPx: Double,
441
+ viewportHeightPx: Double,
442
+ paddingPx: Double,
443
+ minZoom: Int,
444
+ maxZoom: Int
445
+ ): Double {
446
+ if (points.isEmpty()) return minZoom.toDouble()
447
+ if (points.size == 1) return maxZoom.toDouble()
448
+
449
+ val safeMinZoom = min(minZoom, maxZoom)
450
+ val safeMaxZoom = max(minZoom, maxZoom)
451
+ val safeWidth = if (viewportWidthPx > 1.0) viewportWidthPx else 390.0
452
+ val safeHeight = if (viewportHeightPx > 1.0) viewportHeightPx else 844.0
453
+ val safePadding = max(0.0, paddingPx)
454
+ val availableWidth = max(1.0, safeWidth - safePadding * 2.0)
455
+ val availableHeight = max(1.0, safeHeight - safePadding * 2.0)
456
+
457
+ val xs = points.map { mercatorX01(it.longitude) }
458
+ val ys = points.map { mercatorY01(it.latitude) }
459
+ val spanX = wrappedSpan01(xs)
460
+ val spanY = (ys.maxOrNull() ?: 0.0) - (ys.minOrNull() ?: 0.0)
461
+ val tileSize = 256.0
462
+
463
+ val zoomX = if (spanX <= 1e-12) safeMaxZoom.toDouble() else log2(availableWidth / (tileSize * spanX))
464
+ val zoomY = if (spanY <= 1e-12) safeMaxZoom.toDouble() else log2(availableHeight / (tileSize * spanY))
465
+ val fitZoom = min(zoomX, zoomY)
466
+ if (!fitZoom.isFinite()) return safeMinZoom.toDouble()
467
+ return fitZoom.coerceIn(safeMinZoom.toDouble(), safeMaxZoom.toDouble())
468
+ }
469
+
470
+ fun calculateFitZoom(
471
+ points: List<LatLng>,
472
+ viewportWidthPx: Double,
473
+ viewportHeightPx: Double,
474
+ paddingPx: Double,
475
+ minZoom: Int,
476
+ maxZoom: Int
477
+ ): Double {
478
+ if (points.isEmpty()) return minZoom.toDouble()
479
+ return try {
480
+ val latitudes = DoubleArray(points.size)
481
+ val longitudes = DoubleArray(points.size)
482
+ for (i in points.indices) {
483
+ latitudes[i] = points[i].latitude
484
+ longitudes[i] = points[i].longitude
485
+ }
486
+ nativeCalculateFitZoom(
487
+ latitudes,
488
+ longitudes,
489
+ viewportWidthPx,
490
+ viewportHeightPx,
491
+ paddingPx,
492
+ minZoom,
493
+ maxZoom
494
+ )
495
+ } catch (_: Throwable) {
496
+ fallbackCalculateFitZoom(
497
+ points,
498
+ viewportWidthPx,
499
+ viewportHeightPx,
500
+ paddingPx,
501
+ minZoom,
502
+ maxZoom
503
+ )
504
+ }
505
+ }
506
+
404
507
  fun encodeGeoHash(point: LatLng, precision: Int): String {
405
508
  return try {
406
509
  nativeEncodeGeoHash(point.latitude, point.longitude, precision)
@@ -1267,7 +1267,7 @@ class ExpoGaodeMapNaviView(context: Context, appContext: AppContext) : ExpoView(
1267
1267
  SDKInitializer.restorePersistedState(appCtx)
1268
1268
  if (!SDKInitializer.isPrivacyReady()) {
1269
1269
  throw IllegalStateException(
1270
- "隐私协议未完成确认,请先调用 setPrivacyConfig(或 setPrivacyShow/setPrivacyAgree)"
1270
+ "隐私协议未完成确认,请先调用 setPrivacyConfig"
1271
1271
  )
1272
1272
  }
1273
1273
 
package/build/index.d.ts CHANGED
@@ -1,10 +1,19 @@
1
1
  import ExpoGaodeMapNavigationModule from './ExpoGaodeMapNavigationModule';
2
+ import { ExpoGaodeMapNaviView, type ExpoGaodeMapNaviViewRef } from './ExpoGaodeMapNaviView';
2
3
  export * from './map';
3
4
  import { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, type TransitRouteOptions } from './types';
4
5
  import type { NaviPoint, RouteOptions, DriveRouteOptions, WalkRouteOptions, RideRouteOptions, EBikeRouteOptions, TransitRouteOptions as TransitRouteOptionsType, TruckRouteOptions, OfficialNaviPageOptions, RouteResult, DriveRouteResult, IndependentRouteResult, IndependentDriveRouteOptions, IndependentTruckRouteOptions, IndependentWalkRouteOptions, IndependentRideRouteOptions, SelectIndependentRouteOptions, StartNaviWithIndependentPathOptions, ClearIndependentRouteOptions, MotorcycleRouteOptions, IndependentMotorcycleRouteOptions, BuildAnchorWaypointsOptions, FollowWebPlannedRouteOptions, FollowWebPlannedRouteResult, FollowWebPlannedRouteCandidate, WebPlannedRoute, NaviInfoUpdateEvent, NaviLaneInfoEvent, NaviTrafficStatusesEvent, NaviVisualStateEvent, ExpoGaodeMapNaviViewProps } from './types';
5
6
  export declare function buildAnchorWaypointsFromWebRoute(options: BuildAnchorWaypointsOptions): NaviPoint[];
6
7
  export declare function followWebPlannedRoute(options: FollowWebPlannedRouteOptions): Promise<FollowWebPlannedRouteResult>;
7
- export { ExpoGaodeMapNaviView, type ExpoGaodeMapNaviViewRef, ExpoGaodeMapNaviView as NaviView, type ExpoGaodeMapNaviViewRef as NaviViewRef } from './ExpoGaodeMapNaviView';
8
+ export { ExpoGaodeMapNaviView, type ExpoGaodeMapNaviViewRef };
9
+ /**
10
+ * @deprecated 请使用 `ExpoGaodeMapNaviView`
11
+ */
12
+ export declare const NaviView: import("react").ForwardRefExoticComponent<ExpoGaodeMapNaviViewProps & import("react").RefAttributes<ExpoGaodeMapNaviViewRef>>;
13
+ /**
14
+ * @deprecated 请使用 `ExpoGaodeMapNaviViewRef`
15
+ */
16
+ export type NaviViewRef = ExpoGaodeMapNaviViewRef;
8
17
  /**
9
18
  * 初始化导航模块(可选)
10
19
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/build/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import ExpoGaodeMapNavigationModule from './ExpoGaodeMapNavigationModule';
2
2
  import { ExpoGaodeMapModule } from './map';
3
+ import { ExpoGaodeMapNaviView, } from './ExpoGaodeMapNaviView';
3
4
  // 重新导出地图模块的所有内容
4
5
  export * from './map';
5
6
  import { RouteType, DriveStrategy, WalkStrategy, RideStrategy, TruckSize, TravelStrategy, } from './types';
@@ -461,9 +462,11 @@ function isMotorcycleRouteOptions(options) {
461
462
  return 'motorcycleCC' in options;
462
463
  }
463
464
  // 导出官方导航界面组件
464
- export { ExpoGaodeMapNaviView,
465
- // 兼容旧版本名称
466
- ExpoGaodeMapNaviView as NaviView } from './ExpoGaodeMapNaviView';
465
+ export { ExpoGaodeMapNaviView };
466
+ /**
467
+ * @deprecated 请使用 `ExpoGaodeMapNaviView`
468
+ */
469
+ export const NaviView = ExpoGaodeMapNaviView;
467
470
  /**
468
471
  * 初始化导航模块(可选)
469
472
  */