expo-gaode-map 0.1.6 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/PUBLISHING.md +1 -1
  2. package/README.md +68 -27
  3. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +117 -26
  4. package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +97 -36
  5. package/android/src/main/java/expo/modules/gaodemap/managers/CameraManager.kt +27 -21
  6. package/android/src/main/java/expo/modules/gaodemap/managers/OverlayManager.kt +4 -23
  7. package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +30 -27
  8. package/android/src/main/java/expo/modules/gaodemap/modules/LocationManager.kt +49 -4
  9. package/android/src/main/java/expo/modules/gaodemap/modules/SDKInitializer.kt +13 -5
  10. package/android/src/main/java/expo/modules/gaodemap/overlays/CircleView.kt +0 -6
  11. package/build/ExpoGaodeMapView.js +2 -2
  12. package/build/ExpoGaodeMapView.js.map +1 -1
  13. package/build/index.d.ts +2 -0
  14. package/build/index.d.ts.map +1 -1
  15. package/build/index.js +3 -1
  16. package/build/index.js.map +1 -1
  17. package/build/modules/AMapPermissions.d.ts +27 -0
  18. package/build/modules/AMapPermissions.d.ts.map +1 -0
  19. package/build/modules/AMapPermissions.js +31 -0
  20. package/build/modules/AMapPermissions.js.map +1 -0
  21. package/build/modules/AMapView.d.ts +7 -2
  22. package/build/modules/AMapView.d.ts.map +1 -1
  23. package/build/modules/AMapView.js +15 -3
  24. package/build/modules/AMapView.js.map +1 -1
  25. package/build/types/common.types.d.ts +1 -0
  26. package/build/types/common.types.d.ts.map +1 -1
  27. package/build/types/common.types.js +1 -0
  28. package/build/types/common.types.js.map +1 -1
  29. package/docs/API.md +5 -1
  30. package/docs/ARCHITECTURE.md +421 -0
  31. package/docs/EXAMPLES.md +166 -24
  32. package/docs/INITIALIZATION.md +335 -0
  33. package/ios/ExpoGaodeMapModule.swift +95 -9
  34. package/ios/ExpoGaodeMapView.swift +88 -6
  35. package/ios/managers/CameraManager.swift +58 -0
  36. package/ios/managers/OverlayManager.swift +105 -29
  37. package/ios/managers/UIManager.swift +73 -1
  38. package/ios/modules/LocationManager.swift +109 -3
  39. package/ios/overlays/CircleView.swift +53 -0
  40. package/ios/overlays/HeatMapView.swift +27 -0
  41. package/ios/overlays/MarkerView.swift +29 -1
  42. package/ios/overlays/PolygonView.swift +51 -0
  43. package/ios/overlays/PolylineView.swift +61 -14
  44. package/ios/utils/PermissionManager.swift +58 -0
  45. package/package.json +1 -1
  46. package/src/ExpoGaodeMapView.tsx +2 -2
  47. package/src/index.ts +9 -1
  48. package/src/modules/AMapPermissions.ts +48 -0
  49. package/src/modules/AMapView.ts +15 -3
  50. package/src/types/common.types.ts +1 -0
@@ -0,0 +1,335 @@
1
+ # 初始化指南
2
+
3
+ 本文档详细说明如何正确初始化和配置 expo-gaode-map。
4
+
5
+ ## 目录
6
+
7
+ - [基本初始化流程](#基本初始化流程)
8
+ - [权限管理](#权限管理)
9
+ - [完整示例](#完整示例)
10
+ - [常见问题](#常见问题)
11
+
12
+ ## 基本初始化流程
13
+
14
+ ### 1. SDK 初始化
15
+
16
+ 在应用启动时初始化 SDK(通常在 App 组件的 useEffect 中):
17
+
18
+ ```tsx
19
+ import { initSDK } from 'expo-gaode-map';
20
+
21
+ useEffect(() => {
22
+ initSDK({
23
+ androidKey: 'your-android-api-key',
24
+ iosKey: 'your-ios-api-key',
25
+ });
26
+ }, []);
27
+ ```
28
+
29
+ ### 2. 权限检查和请求
30
+
31
+ 在使用定位功能前,必须先检查和请求权限:
32
+
33
+ ```tsx
34
+ import {
35
+ checkLocationPermission,
36
+ requestLocationPermission
37
+ } from 'expo-gaode-map';
38
+
39
+ // 检查权限状态
40
+ const status = await checkLocationPermission();
41
+ console.log('权限状态:', status);
42
+ // { granted: boolean, canAskAgain: boolean }
43
+
44
+ // 请求权限
45
+ if (!status.granted) {
46
+ const result = await requestLocationPermission();
47
+ if (result.granted) {
48
+ console.log('权限已授予');
49
+ } else {
50
+ console.log('权限被拒绝');
51
+ }
52
+ }
53
+ ```
54
+
55
+ ### 3. 获取位置
56
+
57
+ 权限授予后,可以获取当前位置:
58
+
59
+ ```tsx
60
+ import { getCurrentLocation } from 'expo-gaode-map';
61
+
62
+ try {
63
+ const location = await getCurrentLocation();
64
+ console.log('当前位置:', location);
65
+ } catch (error) {
66
+ console.error('获取位置失败:', error);
67
+ }
68
+ ```
69
+
70
+ ## 权限管理
71
+
72
+ ### 权限 API
73
+
74
+ | API | 说明 | 返回值 |
75
+ |-----|------|--------|
76
+ | `checkLocationPermission()` | 检查定位权限状态 | `Promise<PermissionStatus>` |
77
+ | `requestLocationPermission()` | 请求定位权限 | `Promise<PermissionStatus>` |
78
+
79
+ ### PermissionStatus 类型
80
+
81
+ ```typescript
82
+ interface PermissionStatus {
83
+ granted: boolean; // 是否已授予权限
84
+ canAskAgain: boolean; // 是否可以再次请求
85
+ }
86
+ ```
87
+
88
+ ### 权限状态说明
89
+
90
+ - **granted: true** - 用户已授予权限,可以使用定位功能
91
+ - **granted: false, canAskAgain: true** - 用户拒绝了权限,但可以再次请求
92
+ - **granted: false, canAskAgain: false** - 用户永久拒绝了权限,需要引导用户到设置中手动开启
93
+
94
+ ## 完整示例
95
+
96
+ ### 推荐的初始化流程
97
+
98
+ ```tsx
99
+ import { useEffect, useState } from 'react';
100
+ import { Alert, Platform } from 'react-native';
101
+ import {
102
+ MapView,
103
+ initSDK,
104
+ checkLocationPermission,
105
+ requestLocationPermission,
106
+ getCurrentLocation,
107
+ type LatLng,
108
+ } from 'expo-gaode-map';
109
+
110
+ export default function App() {
111
+ const [initialPosition, setInitialPosition] = useState<{
112
+ target: LatLng;
113
+ zoom: number;
114
+ } | null>(null);
115
+
116
+ useEffect(() => {
117
+ const initializeApp = async () => {
118
+ try {
119
+ // 1. 初始化 SDK
120
+ console.log('正在初始化高德地图 SDK...');
121
+ initSDK({
122
+ androidKey: 'your-android-api-key',
123
+ iosKey: 'your-ios-api-key',
124
+ });
125
+
126
+ // 2. 检查权限
127
+ const status = await checkLocationPermission();
128
+
129
+ // 3. 如果没有权限,请求权限
130
+ if (!status.granted) {
131
+ const result = await requestLocationPermission();
132
+
133
+ if (!result.granted) {
134
+ // 权限被拒绝,使用默认位置
135
+ console.log('定位权限被拒绝,使用默认位置');
136
+ setInitialPosition({
137
+ target: { latitude: 39.90923, longitude: 116.397428 },
138
+ zoom: 10
139
+ });
140
+
141
+ // 如果不能再次请求,引导用户到设置
142
+ if (!result.canAskAgain) {
143
+ Alert.alert(
144
+ '需要定位权限',
145
+ '请在设置中开启定位权限以使用完整功能',
146
+ [
147
+ { text: '取消', style: 'cancel' },
148
+ { text: '去设置', onPress: () => {
149
+ // 打开应用设置
150
+ if (Platform.OS === 'ios') {
151
+ Linking.openURL('app-settings:');
152
+ } else {
153
+ Linking.openSettings();
154
+ }
155
+ }}
156
+ ]
157
+ );
158
+ }
159
+ return;
160
+ }
161
+ }
162
+
163
+ // 4. 获取当前位置
164
+ const location = await getCurrentLocation();
165
+ console.log('当前位置:', location);
166
+
167
+ // 5. 设置地图初始位置
168
+ setInitialPosition({
169
+ target: {
170
+ latitude: location.latitude,
171
+ longitude: location.longitude
172
+ },
173
+ zoom: 15
174
+ });
175
+
176
+ } catch (error) {
177
+ console.error('初始化失败:', error);
178
+ // 使用默认位置
179
+ setInitialPosition({
180
+ target: { latitude: 39.90923, longitude: 116.397428 },
181
+ zoom: 10
182
+ });
183
+ }
184
+ };
185
+
186
+ initializeApp();
187
+ }, []);
188
+
189
+ // 等待初始化完成
190
+ if (!initialPosition) {
191
+ return <LoadingScreen />;
192
+ }
193
+
194
+ return (
195
+ <MapView
196
+ style={{ flex: 1 }}
197
+ initialCameraPosition={initialPosition}
198
+ myLocationEnabled={true}
199
+ onLoad={() => console.log('地图加载完成')}
200
+ />
201
+ );
202
+ }
203
+ ```
204
+
205
+ ### 关键要点
206
+
207
+ 1. **初始化顺序**:
208
+ ```
209
+ initSDK → checkPermission → requestPermission → getCurrentLocation → 渲染地图
210
+ ```
211
+
212
+ 2. **权限处理**:
213
+ - ✅ 总是先检查权限状态
214
+ - ✅ 只在需要时请求权限
215
+ - ✅ 处理权限被拒绝的情况
216
+ - ✅ 提供默认位置作为后备方案
217
+
218
+ 3. **地图渲染**:
219
+ - ✅ 等待初始化完成后再渲染地图
220
+ - ✅ 使用 `initialCameraPosition` 设置初始位置
221
+ - ✅ Android 和 iOS 会自动处理位置更新
222
+
223
+ ## 常见问题
224
+
225
+ ### Q: 如何处理用户拒绝权限的情况?
226
+
227
+ **A:** 提供默认位置并引导用户:
228
+
229
+ ```tsx
230
+ if (!result.granted) {
231
+ // 使用默认位置
232
+ setInitialPosition({
233
+ target: { latitude: 39.90923, longitude: 116.397428 },
234
+ zoom: 10
235
+ });
236
+
237
+ // 如果不能再次请求,引导到设置
238
+ if (!result.canAskAgain) {
239
+ Alert.alert(
240
+ '需要定位权限',
241
+ '请在设置中开启定位权限',
242
+ [
243
+ { text: '取消' },
244
+ { text: '去设置', onPress: () => Linking.openSettings() }
245
+ ]
246
+ );
247
+ }
248
+ }
249
+ ```
250
+
251
+ ### Q: 可以在地图加载后更新位置吗?
252
+
253
+ **A:** 可以,使用 `moveCamera` 方法:
254
+
255
+ ```tsx
256
+ const mapRef = useRef<MapViewRef>(null);
257
+
258
+ // 更新地图中心
259
+ await mapRef.current?.moveCamera({
260
+ target: { latitude: 40.0, longitude: 116.5 },
261
+ zoom: 15,
262
+ }, 1000); // 1秒动画
263
+ ```
264
+
265
+ ### Q: 如何配置定位参数?
266
+
267
+ **A:** 使用 `configure` 函数:
268
+
269
+ ```tsx
270
+ import { configure } from 'expo-gaode-map';
271
+
272
+ configure({
273
+ withReGeocode: true, // 返回地址信息
274
+ mode: 0, // 高精度模式
275
+ interval: 2000, // 2秒更新一次
276
+ });
277
+ ```
278
+
279
+ ### Q: Android 和 iOS 的初始化有区别吗?
280
+
281
+ **A:** 初始化流程相同,但有以下差异:
282
+
283
+ **Android:**
284
+ - 需要在 `AndroidManifest.xml` 中配置权限
285
+ - 支持更多定位配置选项
286
+
287
+ **iOS:**
288
+ - 需要在 `Info.plist` 中配置权限描述
289
+ - 支持后台定位和方向更新
290
+
291
+ 详细配置请参考 [高德地图官方文档](https://lbs.amap.com/)。
292
+
293
+ ## 最佳实践
294
+
295
+ 1. **总是处理权限**:
296
+ ```tsx
297
+ // ✅ 好的做法
298
+ const status = await checkLocationPermission();
299
+ if (!status.granted) {
300
+ await requestLocationPermission();
301
+ }
302
+
303
+ // ❌ 不好的做法
304
+ await getCurrentLocation(); // 可能因为没有权限而失败
305
+ ```
306
+
307
+ 2. **提供加载状态**:
308
+ ```tsx
309
+ if (!initialPosition) {
310
+ return <LoadingScreen />;
311
+ }
312
+ ```
313
+
314
+ 3. **错误处理**:
315
+ ```tsx
316
+ try {
317
+ const location = await getCurrentLocation();
318
+ } catch (error) {
319
+ console.error('获取位置失败:', error);
320
+ // 使用默认位置
321
+ }
322
+ ```
323
+
324
+ 4. **避免重复初始化**:
325
+ ```tsx
326
+ useEffect(() => {
327
+ initSDK({ ... });
328
+ }, []); // 空依赖数组,只初始化一次
329
+ ```
330
+
331
+ ## 相关文档
332
+
333
+ - [API 文档](./API.md) - 完整的 API 参考
334
+ - [使用示例](./EXAMPLES.md) - 详细的代码示例
335
+ - [README](../README.md) - 快速开始指南
@@ -3,47 +3,81 @@ import AMapFoundationKit
3
3
  import AMapLocationKit
4
4
  import MAMapKit
5
5
 
6
+ /**
7
+ * 高德地图 Expo 模块
8
+ *
9
+ * 负责:
10
+ * - SDK 初始化和配置
11
+ * - 定位功能管理
12
+ * - 权限管理
13
+ * - 地图视图和覆盖物注册
14
+ */
6
15
  public class ExpoGaodeMapModule: Module {
16
+ /// 定位管理器实例
7
17
  private var locationManager: LocationManager?
18
+ /// 权限管理器实例
19
+ private var permissionManager: PermissionManager?
8
20
 
9
21
  public func definition() -> ModuleDefinition {
10
22
  Name("ExpoGaodeMap")
11
23
 
24
+ // 模块初始化时设置隐私合规
25
+ OnCreate {
26
+ MAMapView.updatePrivacyAgree(AMapPrivacyAgreeStatus.didAgree)
27
+ MAMapView.updatePrivacyShow(AMapPrivacyShowStatus.didShow, privacyInfo: AMapPrivacyInfoStatus.didContain)
28
+ }
29
+
12
30
  // ==================== SDK 初始化 ====================
13
31
 
32
+ /**
33
+ * 初始化高德地图 SDK
34
+ * @param config 配置字典,包含 iosKey
35
+ */
14
36
  Function("initSDK") { (config: [String: String]) in
15
37
  guard let iosKey = config["iosKey"] else { return }
16
38
  AMapServices.shared().apiKey = iosKey
17
39
  AMapServices.shared().enableHTTPS = true
18
- MAMapView.updatePrivacyAgree(AMapPrivacyAgreeStatus.didAgree)
19
- MAMapView.updatePrivacyShow(AMapPrivacyShowStatus.didShow, privacyInfo: AMapPrivacyInfoStatus.didContain)
20
40
  self.getLocationManager()
21
41
  }
22
42
 
43
+ /**
44
+ * 获取 SDK 版本号
45
+ */
23
46
  Function("getVersion") {
24
47
  "iOS SDK Version"
25
48
  }
26
49
 
27
50
  // ==================== 定位功能 ====================
28
51
 
52
+ /**
53
+ * 开始连续定位
54
+ */
29
55
  Function("start") {
30
56
  self.getLocationManager().start()
31
57
  }
32
58
 
59
+ /**
60
+ * 停止定位
61
+ */
33
62
  Function("stop") {
34
63
  self.getLocationManager().stop()
35
64
  }
36
65
 
66
+ /**
67
+ * 检查是否正在定位
68
+ */
37
69
  AsyncFunction("isStarted") { (promise: Promise) in
38
70
  promise.resolve(self.getLocationManager().isStarted())
39
71
  }
40
72
 
73
+ /**
74
+ * 获取当前位置(单次定位)
75
+ * 返回位置信息和逆地理编码结果
76
+ */
41
77
  AsyncFunction("getCurrentLocation") { (promise: Promise) in
42
78
  let status = CLLocationManager.authorizationStatus()
43
- let clManager = CLLocationManager()
44
- clManager.requestWhenInUseAuthorization()
45
79
 
46
- if status == .authorizedAlways || status == .authorizedWhenInUse || status == .notDetermined {
80
+ if status == .authorizedAlways || status == .authorizedWhenInUse {
47
81
  let manager = self.getLocationManager()
48
82
  manager.locationManager?.requestLocation(withReGeocode: manager.locationManager?.locatingWithReGeocode ?? true, completionBlock: { location, regeocode, error in
49
83
  if let error = error {
@@ -85,8 +119,11 @@ public class ExpoGaodeMapModule: Module {
85
119
  }
86
120
  }
87
121
 
122
+ /**
123
+ * 坐标转换
124
+ * iOS 高德地图 SDK 使用 GCJ-02 坐标系,不需要转换
125
+ */
88
126
  AsyncFunction("coordinateConvert") { (coordinate: [String: Double], type: Int, promise: Promise) in
89
- // iOS 高德地图 SDK 坐标转换
90
127
  guard let latitude = coordinate["latitude"],
91
128
  let longitude = coordinate["longitude"] else {
92
129
  promise.reject("INVALID_ARGUMENT", "无效的坐标参数")
@@ -143,6 +180,37 @@ public class ExpoGaodeMapModule: Module {
143
180
  self.getLocationManager().stopUpdatingHeading()
144
181
  }
145
182
 
183
+ // ==================== 权限管理 ====================
184
+
185
+ /**
186
+ * 检查位置权限状态
187
+ */
188
+ AsyncFunction("checkLocationPermission") { (promise: Promise) in
189
+ let status = CLLocationManager.authorizationStatus()
190
+ let granted = status == .authorizedAlways || status == .authorizedWhenInUse
191
+
192
+ promise.resolve([
193
+ "granted": granted,
194
+ "status": self.getAuthorizationStatusString(status)
195
+ ])
196
+ }
197
+
198
+ /**
199
+ * 请求位置权限
200
+ */
201
+ AsyncFunction("requestLocationPermission") { (promise: Promise) in
202
+ if self.permissionManager == nil {
203
+ self.permissionManager = PermissionManager()
204
+ }
205
+
206
+ self.permissionManager?.requestPermission { granted, status in
207
+ promise.resolve([
208
+ "granted": granted,
209
+ "status": status
210
+ ])
211
+ }
212
+ }
213
+
146
214
  // ==================== 事件 ====================
147
215
 
148
216
  Events("onHeadingUpdate")
@@ -218,11 +286,11 @@ public class ExpoGaodeMapModule: Module {
218
286
  }
219
287
 
220
288
  Prop("trafficEnabled") { (view: ExpoGaodeMapView, show: Bool) in
221
- view.showsTraffic = show
289
+ view.setShowsTraffic(show)
222
290
  }
223
291
 
224
292
  Prop("buildingsEnabled") { (view: ExpoGaodeMapView, show: Bool) in
225
- view.showsBuildings = show
293
+ view.setShowsBuildings(show)
226
294
  }
227
295
 
228
296
  Prop("indoorViewEnabled") { (view: ExpoGaodeMapView, show: Bool) in
@@ -445,8 +513,12 @@ public class ExpoGaodeMapModule: Module {
445
513
  }
446
514
  }
447
515
 
448
- // MARK: - Private Methods
516
+ // MARK: - 私有方法
449
517
 
518
+ /**
519
+ * 获取或创建定位管理器实例
520
+ * 使用懒加载模式,并设置事件回调
521
+ */
450
522
  private func getLocationManager() -> LocationManager {
451
523
  if locationManager == nil {
452
524
  locationManager = LocationManager()
@@ -459,4 +531,18 @@ public class ExpoGaodeMapModule: Module {
459
531
  }
460
532
  return locationManager!
461
533
  }
534
+
535
+ /**
536
+ * 将权限状态转换为字符串
537
+ */
538
+ private func getAuthorizationStatusString(_ status: CLAuthorizationStatus) -> String {
539
+ switch status {
540
+ case .notDetermined: return "notDetermined"
541
+ case .restricted: return "restricted"
542
+ case .denied: return "denied"
543
+ case .authorizedAlways: return "authorizedAlways"
544
+ case .authorizedWhenInUse: return "authorizedWhenInUse"
545
+ @unknown default: return "unknown"
546
+ }
547
+ }
462
548
  }