expo-gaode-map 1.0.6 → 1.0.8
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.en.md +320 -0
- package/README.md +2 -0
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapModule.kt +1 -298
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapView.kt +19 -3
- package/android/src/main/java/expo/modules/gaodemap/ExpoGaodeMapViewModule.kt +126 -0
- package/android/src/main/java/expo/modules/gaodemap/managers/UIManager.kt +22 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/CircleViewModule.kt +41 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/ClusterViewModule.kt +29 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/HeatMapViewModule.kt +27 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerView.kt +53 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/MarkerViewModule.kt +49 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/MultiPointViewModule.kt +21 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolygonViewModule.kt +37 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineView.kt +2 -0
- package/android/src/main/java/expo/modules/gaodemap/overlays/PolylineViewModule.kt +45 -0
- package/build/ExpoGaodeMapView.js +1 -1
- package/build/ExpoGaodeMapView.js.map +1 -1
- package/build/components/overlays/Cluster.d.ts.map +1 -1
- package/build/components/overlays/Cluster.js +1 -1
- package/build/components/overlays/Cluster.js.map +1 -1
- package/build/components/overlays/HeatMap.d.ts.map +1 -1
- package/build/components/overlays/HeatMap.js +1 -1
- package/build/components/overlays/HeatMap.js.map +1 -1
- package/build/components/overlays/Marker.d.ts.map +1 -1
- package/build/components/overlays/Marker.js +29 -0
- package/build/components/overlays/Marker.js.map +1 -1
- package/build/components/overlays/MultiPoint.d.ts.map +1 -1
- package/build/components/overlays/MultiPoint.js +1 -1
- package/build/components/overlays/MultiPoint.js.map +1 -1
- package/build/types/map-view.types.d.ts +2 -1
- package/build/types/map-view.types.d.ts.map +1 -1
- package/build/types/map-view.types.js.map +1 -1
- package/docs/API.en.md +418 -0
- package/docs/API.md +2 -0
- package/docs/ARCHITECTURE.en.md +423 -0
- package/docs/ARCHITECTURE.md +2 -0
- package/docs/EXAMPLES.en.md +642 -0
- package/docs/EXAMPLES.md +2 -0
- package/docs/INITIALIZATION.en.md +346 -0
- package/docs/INITIALIZATION.md +2 -0
- package/expo-module.config.json +22 -2
- package/ios/ExpoGaodeMapModule.swift +0 -334
- package/ios/ExpoGaodeMapView.swift +19 -5
- package/ios/ExpoGaodeMapViewModule.swift +155 -0
- package/ios/managers/UIManager.swift +32 -4
- package/ios/overlays/CircleViewModule.swift +31 -0
- package/ios/overlays/ClusterViewModule.swift +23 -0
- package/ios/overlays/HeatMapViewModule.swift +21 -0
- package/ios/overlays/MarkerView.swift +50 -2
- package/ios/overlays/MarkerViewModule.swift +53 -0
- package/ios/overlays/MultiPointViewModule.swift +15 -0
- package/ios/overlays/PolygonViewModule.swift +27 -0
- package/ios/overlays/PolylineViewModule.swift +39 -0
- package/package.json +1 -1
- package/src/ExpoGaodeMapView.tsx +1 -1
- package/src/components/overlays/Cluster.tsx +2 -1
- package/src/components/overlays/HeatMap.tsx +2 -1
- package/src/components/overlays/Marker.tsx +47 -0
- package/src/components/overlays/MultiPoint.tsx +2 -1
- package/src/types/map-view.types.ts +2 -1
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
# Usage Examples
|
|
2
|
+
|
|
3
|
+
English | [简体中文](./EXAMPLES.md)
|
|
4
|
+
|
|
5
|
+
Complete usage examples and best practices.
|
|
6
|
+
|
|
7
|
+
> 📖 **Recommended Reading**: [Initialization Guide](./INITIALIZATION.en.md) - Detailed initialization process and permission handling
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
- [Complete Application Example](#complete-application-example)
|
|
12
|
+
- [Basic Map Application](#basic-map-application)
|
|
13
|
+
- [Location Tracking Application](#location-tracking-application)
|
|
14
|
+
- [Overlay Examples](#overlay-examples)
|
|
15
|
+
- [Advanced Usage](#advanced-usage)
|
|
16
|
+
|
|
17
|
+
## Complete Application Example
|
|
18
|
+
|
|
19
|
+
Complete example with permission management, error handling, and loading states:
|
|
20
|
+
|
|
21
|
+
```tsx
|
|
22
|
+
import { useEffect, useState } from 'react';
|
|
23
|
+
import { View, Text, Alert, Linking, Platform } from 'react-native';
|
|
24
|
+
import {
|
|
25
|
+
MapView,
|
|
26
|
+
initSDK,
|
|
27
|
+
checkLocationPermission,
|
|
28
|
+
requestLocationPermission,
|
|
29
|
+
getCurrentLocation,
|
|
30
|
+
type LatLng,
|
|
31
|
+
} from 'expo-gaode-map';
|
|
32
|
+
|
|
33
|
+
export default function App() {
|
|
34
|
+
const [initialPosition, setInitialPosition] = useState<{
|
|
35
|
+
target: LatLng;
|
|
36
|
+
zoom: number;
|
|
37
|
+
} | null>(null);
|
|
38
|
+
const [error, setError] = useState<string | null>(null);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const initialize = async () => {
|
|
42
|
+
try {
|
|
43
|
+
// 1. Initialize SDK
|
|
44
|
+
initSDK({
|
|
45
|
+
androidKey: 'your-android-api-key',
|
|
46
|
+
iosKey: 'your-ios-api-key',
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// 2. Check permission
|
|
50
|
+
const status = await checkLocationPermission();
|
|
51
|
+
|
|
52
|
+
// 3. Request permission if needed
|
|
53
|
+
if (!status.granted) {
|
|
54
|
+
const result = await requestLocationPermission();
|
|
55
|
+
|
|
56
|
+
if (!result.granted) {
|
|
57
|
+
// Permission denied
|
|
58
|
+
setInitialPosition({
|
|
59
|
+
target: { latitude: 39.9, longitude: 116.4 },
|
|
60
|
+
zoom: 10
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Guide user to settings
|
|
64
|
+
if (!result.canAskAgain) {
|
|
65
|
+
Alert.alert(
|
|
66
|
+
'Location Permission Required',
|
|
67
|
+
'Please enable location permission in settings',
|
|
68
|
+
[
|
|
69
|
+
{ text: 'Cancel' },
|
|
70
|
+
{ text: 'Settings', onPress: () => {
|
|
71
|
+
if (Platform.OS === 'ios') {
|
|
72
|
+
Linking.openURL('app-settings:');
|
|
73
|
+
} else {
|
|
74
|
+
Linking.openSettings();
|
|
75
|
+
}
|
|
76
|
+
}}
|
|
77
|
+
]
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 4. Get location
|
|
85
|
+
const location = await getCurrentLocation();
|
|
86
|
+
setInitialPosition({
|
|
87
|
+
target: {
|
|
88
|
+
latitude: location.latitude,
|
|
89
|
+
longitude: location.longitude
|
|
90
|
+
},
|
|
91
|
+
zoom: 15
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
} catch (err) {
|
|
95
|
+
console.error('Initialization failed:', err);
|
|
96
|
+
setError('Initialization failed');
|
|
97
|
+
setInitialPosition({
|
|
98
|
+
target: { latitude: 39.9, longitude: 116.4 },
|
|
99
|
+
zoom: 10
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
initialize();
|
|
105
|
+
}, []);
|
|
106
|
+
|
|
107
|
+
// Loading state
|
|
108
|
+
if (!initialPosition && !error) {
|
|
109
|
+
return (
|
|
110
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
111
|
+
<Text>Loading map...</Text>
|
|
112
|
+
</View>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Error state
|
|
117
|
+
if (error) {
|
|
118
|
+
return (
|
|
119
|
+
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
|
120
|
+
<Text>{error}</Text>
|
|
121
|
+
</View>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<MapView
|
|
127
|
+
style={{ flex: 1 }}
|
|
128
|
+
initialCameraPosition={initialPosition!}
|
|
129
|
+
myLocationEnabled={true}
|
|
130
|
+
onLoad={() => console.log('Map loaded')}
|
|
131
|
+
/>
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Basic Map Application
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
import React, { useRef, useEffect } from 'react';
|
|
140
|
+
import { View, StyleSheet, Button } from 'react-native';
|
|
141
|
+
import {
|
|
142
|
+
MapView,
|
|
143
|
+
initSDK,
|
|
144
|
+
Circle,
|
|
145
|
+
Marker,
|
|
146
|
+
Polyline,
|
|
147
|
+
Polygon,
|
|
148
|
+
type MapViewRef
|
|
149
|
+
} from 'expo-gaode-map';
|
|
150
|
+
|
|
151
|
+
export default function App() {
|
|
152
|
+
const mapRef = useRef<MapViewRef>(null);
|
|
153
|
+
|
|
154
|
+
useEffect(() => {
|
|
155
|
+
const initialize = async () => {
|
|
156
|
+
initSDK({
|
|
157
|
+
androidKey: 'your-android-api-key',
|
|
158
|
+
iosKey: 'your-ios-api-key',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Check and request permission
|
|
162
|
+
const status = await checkLocationPermission();
|
|
163
|
+
if (!status.granted) {
|
|
164
|
+
await requestLocationPermission();
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
initialize();
|
|
169
|
+
}, []);
|
|
170
|
+
|
|
171
|
+
const handleMoveCamera = async () => {
|
|
172
|
+
await mapRef.current?.moveCamera(
|
|
173
|
+
{
|
|
174
|
+
target: { latitude: 40.0, longitude: 116.5 },
|
|
175
|
+
zoom: 15,
|
|
176
|
+
},
|
|
177
|
+
1000
|
|
178
|
+
);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
return (
|
|
182
|
+
<View style={styles.container}>
|
|
183
|
+
<MapView
|
|
184
|
+
ref={mapRef}
|
|
185
|
+
style={styles.map}
|
|
186
|
+
initialCameraPosition={{
|
|
187
|
+
target: { latitude: 39.9, longitude: 116.4 },
|
|
188
|
+
zoom: 10,
|
|
189
|
+
}}
|
|
190
|
+
myLocationEnabled={true}
|
|
191
|
+
followUserLocation={false}
|
|
192
|
+
trafficEnabled={true}
|
|
193
|
+
onMapPress={(e) => console.log('Map pressed', e)}
|
|
194
|
+
onLoad={() => console.log('Map loaded')}
|
|
195
|
+
>
|
|
196
|
+
{/* Circle overlay */}
|
|
197
|
+
<Circle
|
|
198
|
+
center={{ latitude: 39.9, longitude: 116.4 }}
|
|
199
|
+
radius={1000}
|
|
200
|
+
fillColor="#8800FF00"
|
|
201
|
+
strokeColor="#FFFF0000"
|
|
202
|
+
strokeWidth={2}
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
{/* Marker */}
|
|
206
|
+
<Marker
|
|
207
|
+
position={{ latitude: 39.95, longitude: 116.45 }}
|
|
208
|
+
title="This is a marker"
|
|
209
|
+
draggable={true}
|
|
210
|
+
/>
|
|
211
|
+
|
|
212
|
+
{/* Polyline */}
|
|
213
|
+
<Polyline
|
|
214
|
+
points={[
|
|
215
|
+
{ latitude: 39.9, longitude: 116.4 },
|
|
216
|
+
{ latitude: 39.95, longitude: 116.45 },
|
|
217
|
+
{ latitude: 40.0, longitude: 116.5 },
|
|
218
|
+
]}
|
|
219
|
+
width={5}
|
|
220
|
+
color="#FF0000FF"
|
|
221
|
+
/>
|
|
222
|
+
|
|
223
|
+
{/* Polygon */}
|
|
224
|
+
<Polygon
|
|
225
|
+
points={[
|
|
226
|
+
{ latitude: 39.85, longitude: 116.35 },
|
|
227
|
+
{ latitude: 39.85, longitude: 116.45 },
|
|
228
|
+
{ latitude: 39.75, longitude: 116.40 },
|
|
229
|
+
]}
|
|
230
|
+
fillColor="#880000FF"
|
|
231
|
+
strokeColor="#FFFF0000"
|
|
232
|
+
strokeWidth={2}
|
|
233
|
+
/>
|
|
234
|
+
</MapView>
|
|
235
|
+
|
|
236
|
+
<View style={styles.controls}>
|
|
237
|
+
<Button title="Move Camera" onPress={handleMoveCamera} />
|
|
238
|
+
</View>
|
|
239
|
+
</View>
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const styles = StyleSheet.create({
|
|
244
|
+
container: {
|
|
245
|
+
flex: 1,
|
|
246
|
+
},
|
|
247
|
+
map: {
|
|
248
|
+
flex: 1,
|
|
249
|
+
},
|
|
250
|
+
controls: {
|
|
251
|
+
position: 'absolute',
|
|
252
|
+
bottom: 20,
|
|
253
|
+
left: 20,
|
|
254
|
+
right: 20,
|
|
255
|
+
},
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
## Location Tracking Application
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
import React, { useEffect, useState } from 'react';
|
|
263
|
+
import { View, Text, Button, StyleSheet } from 'react-native';
|
|
264
|
+
import {
|
|
265
|
+
MapView,
|
|
266
|
+
initSDK,
|
|
267
|
+
configure,
|
|
268
|
+
start,
|
|
269
|
+
stop,
|
|
270
|
+
getCurrentLocation,
|
|
271
|
+
addLocationListener,
|
|
272
|
+
type Location,
|
|
273
|
+
} from 'expo-gaode-map';
|
|
274
|
+
|
|
275
|
+
export default function LocationApp() {
|
|
276
|
+
const [location, setLocation] = useState<Location | null>(null);
|
|
277
|
+
const [isTracking, setIsTracking] = useState(false);
|
|
278
|
+
|
|
279
|
+
useEffect(() => {
|
|
280
|
+
const initialize = async () => {
|
|
281
|
+
// Initialize SDK
|
|
282
|
+
initSDK({
|
|
283
|
+
androidKey: 'your-android-api-key',
|
|
284
|
+
iosKey: 'your-ios-api-key',
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// Check and request permission
|
|
288
|
+
const status = await checkLocationPermission();
|
|
289
|
+
if (!status.granted) {
|
|
290
|
+
await requestLocationPermission();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// Configure location parameters
|
|
294
|
+
configure({
|
|
295
|
+
withReGeocode: true,
|
|
296
|
+
mode: 0,
|
|
297
|
+
interval: 2000,
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// Listen to location updates
|
|
301
|
+
const subscription = addLocationListener((loc) => {
|
|
302
|
+
console.log('Location update:', loc);
|
|
303
|
+
setLocation(loc);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
return () => subscription.remove();
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
initialize();
|
|
310
|
+
}, []);
|
|
311
|
+
|
|
312
|
+
const handleStartTracking = () => {
|
|
313
|
+
start();
|
|
314
|
+
setIsTracking(true);
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const handleStopTracking = () => {
|
|
318
|
+
stop();
|
|
319
|
+
setIsTracking(false);
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const handleGetLocation = async () => {
|
|
323
|
+
try {
|
|
324
|
+
const loc = await getCurrentLocation();
|
|
325
|
+
setLocation(loc);
|
|
326
|
+
} catch (error) {
|
|
327
|
+
console.error('Get location failed:', error);
|
|
328
|
+
}
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
return (
|
|
332
|
+
<View style={styles.container}>
|
|
333
|
+
<MapView
|
|
334
|
+
style={styles.map}
|
|
335
|
+
myLocationEnabled={true}
|
|
336
|
+
followUserLocation={isTracking}
|
|
337
|
+
initialCameraPosition={{
|
|
338
|
+
target: {
|
|
339
|
+
latitude: location?.latitude || 39.9,
|
|
340
|
+
longitude: location?.longitude || 116.4
|
|
341
|
+
},
|
|
342
|
+
zoom: 15,
|
|
343
|
+
}}
|
|
344
|
+
/>
|
|
345
|
+
|
|
346
|
+
{location && (
|
|
347
|
+
<View style={styles.info}>
|
|
348
|
+
<Text style={styles.infoText}>
|
|
349
|
+
Latitude: {location.latitude.toFixed(6)}
|
|
350
|
+
</Text>
|
|
351
|
+
<Text style={styles.infoText}>
|
|
352
|
+
Longitude: {location.longitude.toFixed(6)}
|
|
353
|
+
</Text>
|
|
354
|
+
<Text style={styles.infoText}>
|
|
355
|
+
Accuracy: {location.accuracy.toFixed(2)} m
|
|
356
|
+
</Text>
|
|
357
|
+
{location.address && (
|
|
358
|
+
<Text style={styles.infoText}>
|
|
359
|
+
Address: {location.address}
|
|
360
|
+
</Text>
|
|
361
|
+
)}
|
|
362
|
+
</View>
|
|
363
|
+
)}
|
|
364
|
+
|
|
365
|
+
<View style={styles.controls}>
|
|
366
|
+
<Button
|
|
367
|
+
title="Get Location"
|
|
368
|
+
onPress={handleGetLocation}
|
|
369
|
+
/>
|
|
370
|
+
<View style={{ height: 10 }} />
|
|
371
|
+
<Button
|
|
372
|
+
title={isTracking ? 'Stop Tracking' : 'Start Tracking'}
|
|
373
|
+
onPress={isTracking ? handleStopTracking : handleStartTracking}
|
|
374
|
+
color={isTracking ? '#FF3B30' : '#007AFF'}
|
|
375
|
+
/>
|
|
376
|
+
</View>
|
|
377
|
+
</View>
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const styles = StyleSheet.create({
|
|
382
|
+
container: {
|
|
383
|
+
flex: 1,
|
|
384
|
+
},
|
|
385
|
+
map: {
|
|
386
|
+
flex: 1,
|
|
387
|
+
},
|
|
388
|
+
info: {
|
|
389
|
+
position: 'absolute',
|
|
390
|
+
top: 50,
|
|
391
|
+
left: 20,
|
|
392
|
+
right: 20,
|
|
393
|
+
backgroundColor: 'white',
|
|
394
|
+
padding: 15,
|
|
395
|
+
borderRadius: 10,
|
|
396
|
+
shadowColor: '#000',
|
|
397
|
+
shadowOffset: { width: 0, height: 2 },
|
|
398
|
+
shadowOpacity: 0.25,
|
|
399
|
+
shadowRadius: 3.84,
|
|
400
|
+
elevation: 5,
|
|
401
|
+
},
|
|
402
|
+
infoText: {
|
|
403
|
+
fontSize: 14,
|
|
404
|
+
marginBottom: 5,
|
|
405
|
+
color: '#333',
|
|
406
|
+
},
|
|
407
|
+
controls: {
|
|
408
|
+
position: 'absolute',
|
|
409
|
+
bottom: 30,
|
|
410
|
+
left: 20,
|
|
411
|
+
right: 20,
|
|
412
|
+
},
|
|
413
|
+
});
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
## Overlay Examples
|
|
417
|
+
|
|
418
|
+
### Circle
|
|
419
|
+
|
|
420
|
+
**Declarative usage:**
|
|
421
|
+
```tsx
|
|
422
|
+
<MapView style={{ flex: 1 }}>
|
|
423
|
+
<Circle
|
|
424
|
+
center={{ latitude: 39.9, longitude: 116.4 }}
|
|
425
|
+
radius={1000}
|
|
426
|
+
fillColor="#8800FF00"
|
|
427
|
+
strokeColor="#FFFF0000"
|
|
428
|
+
strokeWidth={2}
|
|
429
|
+
onPress={() => console.log('Circle pressed')}
|
|
430
|
+
/>
|
|
431
|
+
</MapView>
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
**Imperative usage:**
|
|
435
|
+
```tsx
|
|
436
|
+
const mapRef = useRef<MapViewRef>(null);
|
|
437
|
+
|
|
438
|
+
await mapRef.current?.addCircle('circle1', {
|
|
439
|
+
center: { latitude: 39.9, longitude: 116.4 },
|
|
440
|
+
radius: 1000,
|
|
441
|
+
fillColor: 0x8800FF00,
|
|
442
|
+
strokeColor: 0xFFFF0000,
|
|
443
|
+
strokeWidth: 2,
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
await mapRef.current?.updateCircle('circle1', {
|
|
447
|
+
radius: 2000,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
await mapRef.current?.removeCircle('circle1');
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
### Marker
|
|
454
|
+
|
|
455
|
+
**Declarative usage:**
|
|
456
|
+
```tsx
|
|
457
|
+
<MapView style={{ flex: 1 }}>
|
|
458
|
+
<Marker
|
|
459
|
+
position={{ latitude: 39.9, longitude: 116.4 }}
|
|
460
|
+
title="Title"
|
|
461
|
+
snippet="Description"
|
|
462
|
+
draggable={true}
|
|
463
|
+
onPress={() => console.log('Marker pressed')}
|
|
464
|
+
onDragEnd={(e) => console.log('Drag ended', e.nativeEvent)}
|
|
465
|
+
/>
|
|
466
|
+
</MapView>
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
**Imperative usage:**
|
|
470
|
+
```tsx
|
|
471
|
+
await mapRef.current?.addMarker('marker1', {
|
|
472
|
+
position: { latitude: 39.9, longitude: 116.4 },
|
|
473
|
+
title: 'Title',
|
|
474
|
+
snippet: 'Description',
|
|
475
|
+
draggable: true,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
await mapRef.current?.updateMarker('marker1', {
|
|
479
|
+
position: { latitude: 40.0, longitude: 116.5 },
|
|
480
|
+
});
|
|
481
|
+
|
|
482
|
+
await mapRef.current?.removeMarker('marker1');
|
|
483
|
+
```
|
|
484
|
+
|
|
485
|
+
> **⚠️ Limitation**: Markers added via imperative API **do not support event callbacks** (onPress, onDragEnd, etc.). Use declarative `<Marker>` component for event handling.
|
|
486
|
+
|
|
487
|
+
### Polyline
|
|
488
|
+
|
|
489
|
+
**Declarative usage - Normal polyline:**
|
|
490
|
+
```tsx
|
|
491
|
+
<MapView style={{ flex: 1 }}>
|
|
492
|
+
<Polyline
|
|
493
|
+
points={[
|
|
494
|
+
{ latitude: 39.9, longitude: 116.4 },
|
|
495
|
+
{ latitude: 39.95, longitude: 116.45 },
|
|
496
|
+
{ latitude: 40.0, longitude: 116.5 },
|
|
497
|
+
]}
|
|
498
|
+
width={5}
|
|
499
|
+
color="#FFFF0000"
|
|
500
|
+
onPress={() => console.log('Polyline pressed')}
|
|
501
|
+
/>
|
|
502
|
+
</MapView>
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**Declarative usage - Textured polyline:**
|
|
506
|
+
```tsx
|
|
507
|
+
import { Image } from 'react-native';
|
|
508
|
+
|
|
509
|
+
const iconUri = Image.resolveAssetSource(require('./assets/arrow.png')).uri;
|
|
510
|
+
|
|
511
|
+
<MapView style={{ flex: 1 }}>
|
|
512
|
+
<Polyline
|
|
513
|
+
points={[
|
|
514
|
+
{ latitude: 39.9, longitude: 116.4 },
|
|
515
|
+
{ latitude: 39.95, longitude: 116.45 },
|
|
516
|
+
{ latitude: 40.0, longitude: 116.5 },
|
|
517
|
+
]}
|
|
518
|
+
width={20}
|
|
519
|
+
color="#FFFF0000"
|
|
520
|
+
texture={iconUri}
|
|
521
|
+
onPress={() => console.log('Textured polyline pressed')}
|
|
522
|
+
/>
|
|
523
|
+
</MapView>
|
|
524
|
+
```
|
|
525
|
+
|
|
526
|
+
> **Note**:
|
|
527
|
+
> - Color format uses ARGB (`#AARRGGBB`), e.g., `#FFFF0000` for opaque red
|
|
528
|
+
> - `texture` supports network images (http/https) and local files (file://)
|
|
529
|
+
> - Texture tiles along the polyline direction
|
|
530
|
+
> - Recommend larger `width` values (e.g., 20) for better texture display
|
|
531
|
+
> - **Segment Texture Limitation**: Single Polyline can only have one texture. For different textures on segments, create multiple Polyline components
|
|
532
|
+
|
|
533
|
+
### Polygon
|
|
534
|
+
|
|
535
|
+
**Declarative usage:**
|
|
536
|
+
```tsx
|
|
537
|
+
<MapView style={{ flex: 1 }}>
|
|
538
|
+
<Polygon
|
|
539
|
+
points={[
|
|
540
|
+
{ latitude: 39.9, longitude: 116.3 },
|
|
541
|
+
{ latitude: 39.9, longitude: 116.4 },
|
|
542
|
+
{ latitude: 39.8, longitude: 116.4 },
|
|
543
|
+
]}
|
|
544
|
+
fillColor="#8800FF00"
|
|
545
|
+
strokeColor="#FFFF0000"
|
|
546
|
+
strokeWidth={2}
|
|
547
|
+
onPress={() => console.log('Polygon pressed')}
|
|
548
|
+
/>
|
|
549
|
+
</MapView>
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
**Imperative usage:**
|
|
553
|
+
```tsx
|
|
554
|
+
await mapRef.current?.addPolygon('polygon1', {
|
|
555
|
+
points: [
|
|
556
|
+
{ latitude: 39.9, longitude: 116.3 },
|
|
557
|
+
{ latitude: 39.9, longitude: 116.4 },
|
|
558
|
+
{ latitude: 39.8, longitude: 116.4 },
|
|
559
|
+
],
|
|
560
|
+
fillColor: 0x8800FF00,
|
|
561
|
+
strokeColor: 0xFFFF0000,
|
|
562
|
+
strokeWidth: 2,
|
|
563
|
+
});
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Advanced Usage
|
|
567
|
+
|
|
568
|
+
### Custom Location Blue Dot
|
|
569
|
+
|
|
570
|
+
```tsx
|
|
571
|
+
import { Image } from 'react-native';
|
|
572
|
+
|
|
573
|
+
const iconUri = Image.resolveAssetSource(require('./assets/location-icon.png')).uri;
|
|
574
|
+
|
|
575
|
+
<MapView
|
|
576
|
+
myLocationEnabled={true}
|
|
577
|
+
userLocationRepresentation={{
|
|
578
|
+
showsAccuracyRing: true,
|
|
579
|
+
fillColor: '#4285F4',
|
|
580
|
+
strokeColor: '#1967D2',
|
|
581
|
+
lineWidth: 2,
|
|
582
|
+
image: iconUri,
|
|
583
|
+
imageWidth: 40,
|
|
584
|
+
imageHeight: 40,
|
|
585
|
+
}}
|
|
586
|
+
/>
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
### Batch Overlay Operations
|
|
590
|
+
|
|
591
|
+
```tsx
|
|
592
|
+
const mapRef = useRef<MapViewRef>(null);
|
|
593
|
+
|
|
594
|
+
const addMultipleOverlays = async () => {
|
|
595
|
+
await mapRef.current?.addCircle('circle1', {
|
|
596
|
+
center: { latitude: 39.9, longitude: 116.4 },
|
|
597
|
+
radius: 1000,
|
|
598
|
+
fillColor: 0x8800FF00,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
await mapRef.current?.addCircle('circle2', {
|
|
602
|
+
center: { latitude: 40.0, longitude: 116.5 },
|
|
603
|
+
radius: 500,
|
|
604
|
+
fillColor: 0x880000FF,
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
await mapRef.current?.addMarker('marker1', {
|
|
608
|
+
position: { latitude: 39.95, longitude: 116.45 },
|
|
609
|
+
title: 'Beijing',
|
|
610
|
+
});
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
const clearAll = async () => {
|
|
614
|
+
await mapRef.current?.removeCircle('circle1');
|
|
615
|
+
await mapRef.current?.removeCircle('circle2');
|
|
616
|
+
await mapRef.current?.removeMarker('marker1');
|
|
617
|
+
};
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
### Zoom Level Limits
|
|
621
|
+
|
|
622
|
+
```tsx
|
|
623
|
+
<MapView
|
|
624
|
+
maxZoom={18}
|
|
625
|
+
minZoom={5}
|
|
626
|
+
initialCameraPosition={{
|
|
627
|
+
target: { latitude: 39.9, longitude: 116.4 },
|
|
628
|
+
zoom: 10,
|
|
629
|
+
}}
|
|
630
|
+
/>
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Heading Updates (iOS)
|
|
634
|
+
|
|
635
|
+
```tsx
|
|
636
|
+
import { startUpdatingHeading, stopUpdatingHeading } from 'expo-gaode-map';
|
|
637
|
+
|
|
638
|
+
// Start heading updates
|
|
639
|
+
startUpdatingHeading();
|
|
640
|
+
|
|
641
|
+
// Stop heading updates
|
|
642
|
+
stopUpdatingHeading();
|