1import { ProxyNativeModule, requireNativeModule } from 'expo-modules-core'; 2import React from 'react'; 3import { Platform, findNodeHandle } from 'react-native'; 4 5import { CameraMove } from './Common.types'; 6import { DefaultNativeExpoMapViewProps, ExpoMapState, ExpoMapViewProps } from './Map.types'; 7import { 8 NativeExpoAppleMapsView, 9 NativeExpoAppleMapsModule, 10 NativeExpoGoogleMapsView, 11 NativeExpoGoogleMapsModule, 12} from './NativeExpoMapView'; 13import * as Utils from './Utils'; 14 15export { Marker } from './Marker'; 16export { Polygon } from './Polygon'; 17export { Polyline } from './Polyline'; 18export { Circle } from './Circle'; 19export { Cluster } from './Cluster'; 20export { KML } from './KML'; 21export { GeoJson } from './GeoJson'; 22export { Overlay } from './Overlay'; 23export { ExpoMapRef } from './Map.types'; 24export { POICategoryType } from './Map.types'; 25export { Heatmap } from './Heatmap'; 26export * from './Events'; 27 28const defaultNativeExpoMapViewProps: DefaultNativeExpoMapViewProps = { 29 mapType: 'normal', 30 showZoomControls: true, 31 showCompass: true, 32 showMapToolbar: true, 33 showMyLocationButton: true, 34 showLevelPicker: true, 35 enableRotateGestures: false, 36 enableScrollGestures: true, 37 enableTiltGestures: false, 38 enableZoomGestures: true, 39 initialCameraPosition: { 40 target: { 41 latitude: 51.51, 42 longitude: 0.13, 43 }, 44 zoom: 4, 45 animate: true, 46 duration: 1000, 47 }, 48 enableTraffic: false, 49 enablePOISearching: false, 50 enablePOIs: false, 51 enablePOIFilter: [], 52 createPOISearchRequest: '', 53 clickablePOIs: true, 54}; 55 56/** 57 * Main map component of Expo Maps library. 58 * 59 * See {@link ExpoMapViewProps} to learn more about props. 60 */ 61export class ExpoMap extends React.Component<ExpoMapViewProps> { 62 state: ExpoMapState = { 63 markers: [], 64 polygons: [], 65 polylines: [], 66 circles: [], 67 clusters: [], 68 kmls: [], 69 geojsons: [], 70 overlays: [], 71 heatmaps: [], 72 }; 73 _ismounted = false; 74 mapView = React.createRef<ExpoMap>(); 75 76 getSearchCompletions(queryFragment: string) { 77 const nodeHandle = findNodeHandle(this.mapView.current); 78 let module: ProxyNativeModule; 79 if (Platform.OS === 'ios' && this.props.provider === 'apple') { 80 module = NativeExpoAppleMapsModule; 81 } else { 82 module = NativeExpoGoogleMapsModule; 83 } 84 85 module 86 .getSearchCompletions(nodeHandle, queryFragment) 87 .then((response: [string]) => { 88 console.log(response); 89 }) 90 .catch((error: Error) => { 91 console.log('Error with message: ' + error.message); 92 }); 93 } 94 95 async moveCamera(cameraMove: CameraMove) { 96 const nodeHandle = findNodeHandle(this.mapView.current); 97 let module: ProxyNativeModule; 98 if (Platform.OS === 'ios' && this.props.provider === 'apple') { 99 module = requireNativeModule('ExpoAppleMaps'); 100 } else { 101 module = requireNativeModule('ExpoGoogleMaps'); 102 } 103 return module.moveCamera(nodeHandle, cameraMove); 104 } 105 106 componentDidMount() { 107 this.mapChildren(); 108 this._ismounted = true; 109 } 110 111 componentWillUnmount() { 112 this._ismounted = false; 113 } 114 115 componentDidUpdate(_, prevState: ExpoMapState) { 116 if (Object.is(this.state, prevState)) { 117 this.mapChildren(); 118 } 119 } 120 121 private async mapChildren() { 122 const childrenArray = React.Children.map(this.props.children, async (child) => { 123 if (!Utils.isSimpleType(child)) { 124 if (Utils.isMarker(child)) { 125 return Utils.buildMarkerObject(child); 126 } else if (Utils.isPolygon(child)) { 127 return Utils.buildPolygonObject(child); 128 } else if (Utils.isPolyline(child)) { 129 return Utils.buildPolylineObject(child); 130 } else if (Utils.isCircle(child)) { 131 return Utils.buildCircleObject(child); 132 } else if (Utils.isKML(child)) { 133 return Utils.buildKMLObject(child); 134 } else if (Utils.isGeoJson(child)) { 135 return Utils.buildGeoJsonObject(child); 136 } else if (Utils.isCluster(child)) { 137 return Utils.buildClusterObject(child); 138 } else if (Utils.isOverlay(child)) { 139 return Utils.buildOverlayObject(child); 140 } else if (Utils.isHeatmap(child)) { 141 return Utils.buildHeatmapObject(child); 142 } 143 Utils.warnIfChildIsIncompatible(child); 144 return null; 145 } 146 Utils.warnIfChildIsIncompatible(child); 147 return null; 148 }); 149 150 if (childrenArray !== undefined) { 151 // TODO(@lukmccall): remove any cast 152 const propObjects = await Promise.all(childrenArray as any); 153 if (this._ismounted) { 154 this.setState({ 155 markers: propObjects.filter((elem) => elem?.type === 'marker'), 156 polygons: propObjects.filter((elem) => elem?.type === 'polygon'), 157 polylines: propObjects.filter((elem) => elem?.type === 'polyline'), 158 circles: propObjects.filter((elem) => elem?.type === 'circle'), 159 clusters: propObjects.filter((elem) => elem?.type === 'cluster'), 160 kmls: propObjects.filter((elem) => elem?.type === 'kml'), 161 geojsons: propObjects.filter((elem) => elem?.type === 'geojson'), 162 overlays: propObjects.filter((elem) => elem?.type === 'overlay'), 163 heatmaps: propObjects.filter((elem) => elem?.type === 'heatmap'), 164 }); 165 } 166 } 167 } 168 169 render() { 170 if (Platform.OS === 'ios' && this.props.provider === 'apple') { 171 if (parseInt(Platform.Version, 10) < 13 && this.state.geojsons.length > 0) { 172 console.warn( 173 "Versions of iOS < 13 doesn't support GeoJSON features for Apple Maps. Adding of GeoJSON for these versions will be omitted." 174 ); 175 } 176 if (parseInt(Platform.Version, 10) < 13) { 177 console.warn( 178 "Versions of iOS < 13 doesn't support Points Of Interest Filters and their display modifications for Apple Maps. Adding POI filters for these versions will be omitted." 179 ); 180 } 181 return ( 182 <NativeExpoAppleMapsView 183 {...defaultNativeExpoMapViewProps} 184 {...this.props} 185 markers={this.state.markers} 186 polygons={this.state.polygons} 187 polylines={this.state.polylines} 188 circles={this.state.circles} 189 clusters={this.state.clusters} 190 kmls={this.state.kmls} 191 geojsons={this.state.geojsons} 192 ref={this.mapView} 193 /> 194 ); 195 } 196 197 let googleMapsJsonStyleString = this.props.googleMapsJsonStyleString 198 ? this.props.googleMapsJsonStyleString 199 : ''; 200 if (this.props.enablePOIs === false) { 201 if (this.props.googleMapsJsonStyleString) { 202 console.warn( 203 "Expo Maps enablePOIs prop isn't effective when custom Google Maps map style is active. Please adjust your style manually to disable the POIs. https://developers.google.com/maps/documentation/ios-sdk/poi" 204 ); 205 } else { 206 googleMapsJsonStyleString = JSON.stringify([ 207 { 208 featureType: 'poi', 209 stylers: [{ visibility: 'off' }], 210 }, 211 ]); 212 } 213 } 214 215 return ( 216 <NativeExpoGoogleMapsView 217 {...defaultNativeExpoMapViewProps} 218 {...this.props} 219 googleMapsJsonStyleString={googleMapsJsonStyleString} 220 markers={this.state.markers} 221 polygons={this.state.polygons} 222 polylines={this.state.polylines} 223 circles={this.state.circles} 224 clusters={this.state.clusters} 225 kmls={this.state.kmls} 226 geojsons={this.state.geojsons} 227 ref={this.mapView} 228 overlays={this.state.overlays} 229 heatmaps={this.state.heatmaps} 230 /> 231 ); 232 } 233} 234