react-native-cn-maps 0.1.0

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 (42) hide show
  1. package/CnMaps.podspec +21 -0
  2. package/LICENSE +20 -0
  3. package/README.md +89 -0
  4. package/android/build.gradle +68 -0
  5. package/android/src/main/AndroidManifest.xml +7 -0
  6. package/android/src/main/java/com/cnmaps/MapView.kt +368 -0
  7. package/android/src/main/java/com/cnmaps/MapViewManager.kt +112 -0
  8. package/android/src/main/java/com/cnmaps/MapsPackage.kt +17 -0
  9. package/ios/RNMapsMapView.h +14 -0
  10. package/ios/RNMapsMapView.mm +291 -0
  11. package/lib/module/MapMarker.js +8 -0
  12. package/lib/module/MapMarker.js.map +1 -0
  13. package/lib/module/MapView.js +111 -0
  14. package/lib/module/MapView.js.map +1 -0
  15. package/lib/module/MapViewNativeComponent.ts +79 -0
  16. package/lib/module/coordinate.js +72 -0
  17. package/lib/module/coordinate.js.map +1 -0
  18. package/lib/module/index.js +5 -0
  19. package/lib/module/index.js.map +1 -0
  20. package/lib/module/package.json +1 -0
  21. package/lib/module/types.js +4 -0
  22. package/lib/module/types.js.map +1 -0
  23. package/lib/typescript/package.json +1 -0
  24. package/lib/typescript/src/MapMarker.d.ts +8 -0
  25. package/lib/typescript/src/MapMarker.d.ts.map +1 -0
  26. package/lib/typescript/src/MapView.d.ts +145 -0
  27. package/lib/typescript/src/MapView.d.ts.map +1 -0
  28. package/lib/typescript/src/MapViewNativeComponent.d.ts +55 -0
  29. package/lib/typescript/src/MapViewNativeComponent.d.ts.map +1 -0
  30. package/lib/typescript/src/coordinate.d.ts +8 -0
  31. package/lib/typescript/src/coordinate.d.ts.map +1 -0
  32. package/lib/typescript/src/index.d.ts +5 -0
  33. package/lib/typescript/src/index.d.ts.map +1 -0
  34. package/lib/typescript/src/types.d.ts +50 -0
  35. package/lib/typescript/src/types.d.ts.map +1 -0
  36. package/package.json +182 -0
  37. package/src/MapMarker.tsx +14 -0
  38. package/src/MapView.tsx +201 -0
  39. package/src/MapViewNativeComponent.ts +79 -0
  40. package/src/coordinate.ts +123 -0
  41. package/src/index.tsx +15 -0
  42. package/src/types.ts +59 -0
@@ -0,0 +1,201 @@
1
+ import React from 'react';
2
+ import type { NativeSyntheticEvent } from 'react-native';
3
+ import NativeMapView, { Commands } from './MapViewNativeComponent';
4
+ import {
5
+ fromProviderCoordinate,
6
+ fromProviderRegion,
7
+ toProviderCoordinate,
8
+ toProviderRegion,
9
+ } from './coordinate';
10
+ import type {
11
+ NativeMarker,
12
+ NativeMarkerPressEvent,
13
+ NativeRegionChangeEvent,
14
+ } from './MapViewNativeComponent';
15
+ import type {
16
+ CoordinateSystem,
17
+ MapProvider,
18
+ MapViewHandle,
19
+ MapViewProps,
20
+ MarkerPressEvent,
21
+ MarkerProps,
22
+ RegionChangeEvent,
23
+ } from './types';
24
+
25
+ const SUPPORTED_PROVIDER: MapProvider = 'amap';
26
+ const DEFAULT_COORDINATE_SYSTEM: CoordinateSystem = 'gcj02';
27
+
28
+ function isMarkerElement(
29
+ child: React.ReactNode
30
+ ): child is React.ReactElement<MarkerProps> {
31
+ return (
32
+ React.isValidElement(child) &&
33
+ Boolean((child.type as { __MAP_MARKER?: boolean }).__MAP_MARKER)
34
+ );
35
+ }
36
+
37
+ function markerColorToString(color: MarkerProps['pinColor']) {
38
+ if (typeof color === 'string') {
39
+ return color;
40
+ }
41
+
42
+ return undefined;
43
+ }
44
+
45
+ export const MapView = React.forwardRef<MapViewHandle, MapViewProps>(
46
+ function MapView(
47
+ {
48
+ provider = SUPPORTED_PROVIDER,
49
+ coordinateSystem = DEFAULT_COORDINATE_SYSTEM,
50
+ initialRegion,
51
+ region,
52
+ children,
53
+ onRegionChange,
54
+ onRegionChangeComplete,
55
+ ...rest
56
+ },
57
+ ref
58
+ ) {
59
+ const nativeRef =
60
+ React.useRef<React.ElementRef<typeof NativeMapView>>(null);
61
+ const markerHandlers = React.useRef<Record<string, MarkerProps['onPress']>>(
62
+ {}
63
+ );
64
+
65
+ if (__DEV__ && provider !== SUPPORTED_PROVIDER) {
66
+ console.warn(
67
+ `[react-native-cn-maps] provider="${provider}" is reserved but not implemented yet. Falling back to "amap".`
68
+ );
69
+ }
70
+
71
+ const markers = React.useMemo(() => {
72
+ const nextHandlers: Record<string, MarkerProps['onPress']> = {};
73
+ const nativeMarkers: NativeMarker[] = [];
74
+
75
+ React.Children.forEach(children, (child, index) => {
76
+ if (!isMarkerElement(child)) {
77
+ if (__DEV__ && child != null) {
78
+ console.warn(
79
+ '[react-native-cn-maps] Only <Marker /> children are rendered in the current MapView milestone.'
80
+ );
81
+ }
82
+ return;
83
+ }
84
+
85
+ const identifier = child.props.identifier ?? String(index);
86
+ const coordinate = toProviderCoordinate(
87
+ child.props.coordinate,
88
+ coordinateSystem
89
+ );
90
+
91
+ if (child.props.onPress) {
92
+ nextHandlers[identifier] = child.props.onPress;
93
+ }
94
+
95
+ nativeMarkers.push({
96
+ identifier,
97
+ latitude: coordinate.latitude,
98
+ longitude: coordinate.longitude,
99
+ title: child.props.title,
100
+ description: child.props.description,
101
+ pinColor: markerColorToString(child.props.pinColor),
102
+ draggable: child.props.draggable,
103
+ });
104
+ });
105
+
106
+ markerHandlers.current = nextHandlers;
107
+ return nativeMarkers;
108
+ }, [children, coordinateSystem]);
109
+
110
+ React.useImperativeHandle(
111
+ ref,
112
+ () => ({
113
+ animateToRegion(nextRegion, duration = 500) {
114
+ const providerRegion = toProviderRegion(nextRegion, coordinateSystem);
115
+
116
+ if (providerRegion && nativeRef.current) {
117
+ Commands.animateToRegion(
118
+ nativeRef.current,
119
+ providerRegion.latitude,
120
+ providerRegion.longitude,
121
+ providerRegion.latitudeDelta,
122
+ providerRegion.longitudeDelta,
123
+ duration
124
+ );
125
+ }
126
+ },
127
+ }),
128
+ [coordinateSystem]
129
+ );
130
+
131
+ const handleRegionChange = React.useCallback(
132
+ (event: NativeSyntheticEvent<NativeRegionChangeEvent>) => {
133
+ onRegionChange?.({
134
+ nativeEvent: {
135
+ ...event.nativeEvent,
136
+ region: fromProviderRegion(
137
+ event.nativeEvent.region,
138
+ coordinateSystem
139
+ ),
140
+ },
141
+ } satisfies RegionChangeEvent);
142
+ },
143
+ [coordinateSystem, onRegionChange]
144
+ );
145
+
146
+ const handleRegionChangeComplete = React.useCallback(
147
+ (event: NativeSyntheticEvent<NativeRegionChangeEvent>) => {
148
+ onRegionChangeComplete?.({
149
+ nativeEvent: {
150
+ ...event.nativeEvent,
151
+ region: fromProviderRegion(
152
+ event.nativeEvent.region,
153
+ coordinateSystem
154
+ ),
155
+ },
156
+ } satisfies RegionChangeEvent);
157
+ },
158
+ [coordinateSystem, onRegionChangeComplete]
159
+ );
160
+
161
+ const handleMarkerPress = React.useCallback(
162
+ (event: NativeSyntheticEvent<NativeMarkerPressEvent>) => {
163
+ const handler = markerHandlers.current[event.nativeEvent.identifier];
164
+
165
+ if (!handler) {
166
+ return;
167
+ }
168
+
169
+ const coordinate = fromProviderCoordinate(
170
+ event.nativeEvent.coordinate,
171
+ coordinateSystem
172
+ );
173
+
174
+ handler({
175
+ nativeEvent: {
176
+ identifier: event.nativeEvent.identifier,
177
+ coordinate,
178
+ },
179
+ } satisfies MarkerPressEvent);
180
+ },
181
+ [coordinateSystem]
182
+ );
183
+
184
+ return (
185
+ <NativeMapView
186
+ {...rest}
187
+ ref={nativeRef}
188
+ provider={SUPPORTED_PROVIDER}
189
+ coordinateSystem={coordinateSystem}
190
+ initialRegion={toProviderRegion(initialRegion, coordinateSystem)}
191
+ region={toProviderRegion(region, coordinateSystem)}
192
+ markers={markers}
193
+ onRegionChange={onRegionChange ? handleRegionChange : undefined}
194
+ onRegionChangeComplete={
195
+ onRegionChangeComplete ? handleRegionChangeComplete : undefined
196
+ }
197
+ onMarkerPress={handleMarkerPress}
198
+ />
199
+ );
200
+ }
201
+ );
@@ -0,0 +1,79 @@
1
+ import {
2
+ codegenNativeCommands,
3
+ codegenNativeComponent,
4
+ type CodegenTypes,
5
+ type HostComponent,
6
+ type ViewProps,
7
+ } from 'react-native';
8
+
9
+ export type NativeRegion = Readonly<{
10
+ latitude: CodegenTypes.Double;
11
+ longitude: CodegenTypes.Double;
12
+ latitudeDelta: CodegenTypes.Double;
13
+ longitudeDelta: CodegenTypes.Double;
14
+ }>;
15
+
16
+ export type NativeMarker = Readonly<{
17
+ identifier: string;
18
+ latitude: CodegenTypes.Double;
19
+ longitude: CodegenTypes.Double;
20
+ title?: string;
21
+ description?: string;
22
+ pinColor?: string;
23
+ draggable?: CodegenTypes.WithDefault<boolean, false>;
24
+ }>;
25
+
26
+ export type NativeRegionChangeEvent = Readonly<{
27
+ region: Readonly<{
28
+ latitude: CodegenTypes.Double;
29
+ longitude: CodegenTypes.Double;
30
+ latitudeDelta: CodegenTypes.Double;
31
+ longitudeDelta: CodegenTypes.Double;
32
+ }>;
33
+ isGesture?: boolean;
34
+ }>;
35
+
36
+ export type NativeMarkerPressEvent = Readonly<{
37
+ identifier: string;
38
+ coordinate: Readonly<{
39
+ latitude: CodegenTypes.Double;
40
+ longitude: CodegenTypes.Double;
41
+ }>;
42
+ }>;
43
+
44
+ export interface NativeProps extends ViewProps {
45
+ provider?: CodegenTypes.WithDefault<string, 'amap'>;
46
+ coordinateSystem?: CodegenTypes.WithDefault<string, 'gcj02'>;
47
+ initialRegion?: NativeRegion;
48
+ region?: NativeRegion;
49
+ markers?: ReadonlyArray<NativeMarker>;
50
+ showsUserLocation?: CodegenTypes.WithDefault<boolean, false>;
51
+ zoomEnabled?: CodegenTypes.WithDefault<boolean, true>;
52
+ scrollEnabled?: CodegenTypes.WithDefault<boolean, true>;
53
+ rotateEnabled?: CodegenTypes.WithDefault<boolean, true>;
54
+ pitchEnabled?: CodegenTypes.WithDefault<boolean, true>;
55
+ onRegionChange?: CodegenTypes.DirectEventHandler<NativeRegionChangeEvent>;
56
+ onRegionChangeComplete?: CodegenTypes.DirectEventHandler<NativeRegionChangeEvent>;
57
+ onMarkerPress?: CodegenTypes.DirectEventHandler<NativeMarkerPressEvent>;
58
+ }
59
+
60
+ type ComponentType = HostComponent<NativeProps>;
61
+
62
+ interface NativeCommands {
63
+ animateToRegion: (
64
+ viewRef: React.ElementRef<ComponentType>,
65
+ latitude: CodegenTypes.Double,
66
+ longitude: CodegenTypes.Double,
67
+ latitudeDelta: CodegenTypes.Double,
68
+ longitudeDelta: CodegenTypes.Double,
69
+ duration: CodegenTypes.Int32
70
+ ) => void;
71
+ }
72
+
73
+ export const Commands: NativeCommands = codegenNativeCommands<NativeCommands>({
74
+ supportedCommands: ['animateToRegion'],
75
+ });
76
+
77
+ export default codegenNativeComponent<NativeProps>(
78
+ 'RNMapsMapView'
79
+ ) as ComponentType;
@@ -0,0 +1,123 @@
1
+ import type { CoordinateSystem, LatLng, Region } from './types';
2
+
3
+ const A = 6378245.0;
4
+ const EE = 0.00669342162296594323;
5
+ const PI = Math.PI;
6
+
7
+ function isOutsideChina(latitude: number, longitude: number) {
8
+ return (
9
+ longitude < 72.004 ||
10
+ longitude > 137.8347 ||
11
+ latitude < 0.8293 ||
12
+ latitude > 55.8271
13
+ );
14
+ }
15
+
16
+ function transformLatitude(x: number, y: number) {
17
+ let ret =
18
+ -100.0 +
19
+ 2.0 * x +
20
+ 3.0 * y +
21
+ 0.2 * y * y +
22
+ 0.1 * x * y +
23
+ 0.2 * Math.sqrt(Math.abs(x));
24
+ ret +=
25
+ ((20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0) /
26
+ 3.0;
27
+ ret +=
28
+ ((20.0 * Math.sin(y * PI) + 40.0 * Math.sin((y / 3.0) * PI)) * 2.0) / 3.0;
29
+ ret +=
30
+ ((160.0 * Math.sin((y / 12.0) * PI) + 320 * Math.sin((y * PI) / 30.0)) *
31
+ 2.0) /
32
+ 3.0;
33
+ return ret;
34
+ }
35
+
36
+ function transformLongitude(x: number, y: number) {
37
+ let ret =
38
+ 300.0 +
39
+ x +
40
+ 2.0 * y +
41
+ 0.1 * x * x +
42
+ 0.1 * x * y +
43
+ 0.1 * Math.sqrt(Math.abs(x));
44
+ ret +=
45
+ ((20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0) /
46
+ 3.0;
47
+ ret +=
48
+ ((20.0 * Math.sin(x * PI) + 40.0 * Math.sin((x / 3.0) * PI)) * 2.0) / 3.0;
49
+ ret +=
50
+ ((150.0 * Math.sin((x / 12.0) * PI) + 300.0 * Math.sin((x / 30.0) * PI)) *
51
+ 2.0) /
52
+ 3.0;
53
+ return ret;
54
+ }
55
+
56
+ export function wgs84ToGcj02(coordinate: LatLng): LatLng {
57
+ const { latitude, longitude } = coordinate;
58
+
59
+ if (isOutsideChina(latitude, longitude)) {
60
+ return coordinate;
61
+ }
62
+
63
+ let dLat = transformLatitude(longitude - 105.0, latitude - 35.0);
64
+ let dLon = transformLongitude(longitude - 105.0, latitude - 35.0);
65
+ const radLat = (latitude / 180.0) * PI;
66
+ let magic = Math.sin(radLat);
67
+ magic = 1 - EE * magic * magic;
68
+ const sqrtMagic = Math.sqrt(magic);
69
+ dLat = (dLat * 180.0) / (((A * (1 - EE)) / (magic * sqrtMagic)) * PI);
70
+ dLon = (dLon * 180.0) / ((A / sqrtMagic) * Math.cos(radLat) * PI);
71
+
72
+ return {
73
+ latitude: latitude + dLat,
74
+ longitude: longitude + dLon,
75
+ };
76
+ }
77
+
78
+ export function gcj02ToWgs84(coordinate: LatLng): LatLng {
79
+ const converted = wgs84ToGcj02(coordinate);
80
+
81
+ return {
82
+ latitude: coordinate.latitude * 2 - converted.latitude,
83
+ longitude: coordinate.longitude * 2 - converted.longitude,
84
+ };
85
+ }
86
+
87
+ export function toProviderCoordinate(
88
+ coordinate: LatLng,
89
+ coordinateSystem: CoordinateSystem
90
+ ): LatLng {
91
+ return coordinateSystem === 'wgs84' ? wgs84ToGcj02(coordinate) : coordinate;
92
+ }
93
+
94
+ export function fromProviderCoordinate(
95
+ coordinate: LatLng,
96
+ coordinateSystem: CoordinateSystem
97
+ ): LatLng {
98
+ return coordinateSystem === 'wgs84' ? gcj02ToWgs84(coordinate) : coordinate;
99
+ }
100
+
101
+ export function toProviderRegion(
102
+ region: Region | undefined,
103
+ coordinateSystem: CoordinateSystem
104
+ ): Region | undefined {
105
+ if (!region) {
106
+ return undefined;
107
+ }
108
+
109
+ return {
110
+ ...region,
111
+ ...toProviderCoordinate(region, coordinateSystem),
112
+ };
113
+ }
114
+
115
+ export function fromProviderRegion(
116
+ region: Region,
117
+ coordinateSystem: CoordinateSystem
118
+ ): Region {
119
+ return {
120
+ ...region,
121
+ ...fromProviderCoordinate(region, coordinateSystem),
122
+ };
123
+ }
package/src/index.tsx ADDED
@@ -0,0 +1,15 @@
1
+ export { MapView as default, MapView } from './MapView';
2
+ export { default as Marker, Marker as MapMarker } from './MapMarker';
3
+ export type { MapMarkerProps } from './MapMarker';
4
+ export type {
5
+ CoordinateSystem,
6
+ LatLng,
7
+ MapEvent,
8
+ MapProvider,
9
+ MapViewHandle,
10
+ MapViewProps,
11
+ MarkerPressEvent,
12
+ MarkerProps,
13
+ Region,
14
+ RegionChangeEvent,
15
+ } from './types';
package/src/types.ts ADDED
@@ -0,0 +1,59 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { ColorValue, ViewProps } from 'react-native';
3
+
4
+ export type MapProvider = 'amap' | 'baidu' | 'tencent';
5
+
6
+ export type CoordinateSystem = 'gcj02' | 'wgs84';
7
+
8
+ export type LatLng = {
9
+ latitude: number;
10
+ longitude: number;
11
+ };
12
+
13
+ export type Region = LatLng & {
14
+ latitudeDelta: number;
15
+ longitudeDelta: number;
16
+ };
17
+
18
+ export type MapEvent<T = Record<string, unknown>> = {
19
+ nativeEvent: T;
20
+ };
21
+
22
+ export type RegionChangeEvent = MapEvent<{
23
+ region: Region;
24
+ isGesture?: boolean;
25
+ }>;
26
+
27
+ export type MarkerPressEvent = MapEvent<{
28
+ coordinate: LatLng;
29
+ identifier: string;
30
+ }>;
31
+
32
+ export type MapViewProps = ViewProps & {
33
+ provider?: MapProvider;
34
+ coordinateSystem?: CoordinateSystem;
35
+ initialRegion?: Region;
36
+ region?: Region;
37
+ showsUserLocation?: boolean;
38
+ zoomEnabled?: boolean;
39
+ scrollEnabled?: boolean;
40
+ rotateEnabled?: boolean;
41
+ pitchEnabled?: boolean;
42
+ children?: ReactNode;
43
+ onRegionChange?: (event: RegionChangeEvent) => void;
44
+ onRegionChangeComplete?: (event: RegionChangeEvent) => void;
45
+ };
46
+
47
+ export type MarkerProps = ViewProps & {
48
+ coordinate: LatLng;
49
+ identifier?: string;
50
+ title?: string;
51
+ description?: string;
52
+ pinColor?: ColorValue;
53
+ draggable?: boolean;
54
+ onPress?: (event: MarkerPressEvent) => void;
55
+ };
56
+
57
+ export type MapViewHandle = {
58
+ animateToRegion: (region: Region, duration?: number) => void;
59
+ };