I have read the other questions related to this and tried implementing what the answers were on there.
They recommended adding /*global google */ - did not work
Next was to add const google = window.google; - did not work
I will post the code below and the error.
The error is happening where new google.maps is being called
Map.js:
/* global google */
import { default as React, Component } from 'react';
import raf from 'raf';
import canUseDOM from 'can-use-dom';
const google = window.google;
import { withGoogleMap, GoogleMap, Circle, InfoWindow, Marker } from 'react-google-maps';
import withScriptjs from 'react-google-maps/lib/async/withScriptjs';
const googleMapURL =
'https://maps.googleapis.com/maps/api/js?v=3.27&libraries=places,geometry&key=AIzaSyA7XEFRxE4Lm28tAh44M_568fCLOP_On3k';
const geolocation =
canUseDOM && navigator.geolocation
? navigator.geolocation
: {
getCurrentPosition(success, failure) {
failure("Your browser doesn't support geolocation.");
},
};
const GeolocationExampleGoogleMap = withScriptjs(
withGoogleMap(props =>
<GoogleMap defaultZoom={8} center={props.center}>
{props.center &&
<InfoWindow position={props.center}>
<div>User's Location</div>
</InfoWindow>}
{props.center &&
<Circle
center={props.center}
radius={props.radius}
options={{
fillColor: 'red',
fillOpacity: 0.2,
strokeColor: 'red',
strokeOpacity: 1,
strokeWeight: 1,
}}
/>}
>
{props.markers.map((marker, index) => {
const onClick = () => props.onMarkerClick(marker);
const onCloseClick = () => props.onCloseClick(marker);
return (
<Marker
key={index}
position={marker.position}
title={(index + 1).toString()}
onClick={onClick}
>
{marker.showInfo &&
<InfoWindow onCloseClick={onCloseClick}>
<div>
<strong>
{marker.content}
</strong>
<br />
<em>The contents of this InfoWindow are actually ReactElements.</em>
</div>
</InfoWindow>}
</Marker>
);
})}
</GoogleMap>,
),
);
function generateInitialMarkers() {
const southWest = new google.maps.LatLng(-31.203405, 125.244141);
const northEast = new google.maps.LatLng(-25.363882, 131.044922);
const lngSpan = northEast.lng() - southWest.lng();
const latSpan = northEast.lat() - southWest.lat();
const markers = [];
for (let i = 0; i < 5; i++) {
const position = new google.maps.LatLng(
southWest.lat() + latSpan * Math.random(),
southWest.lng() + lngSpan * Math.random(),
);
markers.push({
position,
content: 'This is the secret message'.split(' ')[i],
showInfo: false,
});
}
return markers;
}
export default class GeolocationExample extends Component {
constructor(props) {
super(props);
super(props);
this.state = {
center: null,
content: null,
radius: 6000,
markers: generateInitialMarkers(),
};
const isUnmounted = false;
handleMarkerClick = this.handleMarkerClick.bind(this);
handleCloseClick = this.handleCloseClick.bind(this);
}
handleMarkerClick(targetMarker) {
this.setState({
markers: this.state.markers.map((marker) => {
if (marker === targetMarker) {
return {
...marker,
showInfo: true,
};
}
return marker;
}),
});
}
handleCloseClick(targetMarker) {
this.setState({
markers: this.state.markers.map((marker) => {
if (marker === targetMarker) {
return {
...marker,
showInfo: false,
};
}
return marker;
}),
});
}
componentDidMount() {
const tick = () => {
if (this.isUnmounted) {
return;
}
this.setState({ radius: Math.max(this.state.radius - 20, 0) });
if (this.state.radius > 200) {
raf(tick);
}
};
geolocation.getCurrentPosition(
(position) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: position.coords.latitude,
lng: position.coords.longitude,
},
content: 'Location found using HTML5.',
});
raf(tick);
},
(reason) => {
if (this.isUnmounted) {
return;
}
this.setState({
center: {
lat: 60,
lng: 105,
},
content: `Error: The Geolocation service failed (${reason}).`,
});
},
);
}
componentWillUnmount() {
this.isUnmounted = true;
}
render() {
return (
<GeolocationExampleGoogleMap
googleMapURL={googleMapURL}
loadingElement={<div style={{ height: '100%' }}>loading...</div>}
containerElement={<div style={{ height: '100%' }} />}
mapElement={<div style={{ height: '100%' }} />}
center={this.state.center}
content={this.state.content}
radius={this.state.radius}
onMarkerClick={this.handleMarkerClick}
onCloseClick={this.handleCloseClick}
markers={this.state.markers}
/>
);
}
}
You are loading async script. When you initialize google constant there is no google object. You can solve this by using 'ref' for map component. Ref callback fired when google object exists. For example:
<GoogleMap defaultZoom={8} center={props.center} ref={()=>{props.onMapLoad}}>...</GoogleMap>
...
constructor(props) {
...
this.onMapLoad = this.onMapLoad.bind(this)
}
...
onMapLoad() {
/*init markers and all objects with "google" here*/
}
...
<GeolocationExampleGoogleMap
onMapLoad={this.onMapLoad}
...
/>
and, of course, do not init google const, use just google object instead,
Related
what im trying to do
place a marker on the map and calculate the distance between the marker and a static location on the map.
if the distance is greater than 1000 metres then i display address not available modal
problem description
Unable to access google inside UseEffect .
I loaded the script properly and followed the documentation
window.google is available in the onMapClick function but not anywhere in my index component or inside the useEffect hook
How do i access it ? Everything else works fine
Unhandled Runtime Error
ReferenceError: google is not defined
import React, { useRef, useState, useEffect, useCallback } from 'react';
import {
GoogleMap,
useLoadScript,
Marker,
InfoWindow,
MarkerClusterer,
} from '#react-google-maps/api';
import '#reach/combobox/styles.css';
import usePlacesAutoComplete, {
getGeocode,
getLatLng,
} from 'use-places-autocomplete';
import getDistanceFromLatLonInKm from '../../utils/getDistanceFromLatLonInKm.js';
import Modal from 'react-bootstrap/Modal';
import Button from 'react-bootstrap/Button';
import { Circle } from '#react-google-maps/api';
const libraries = ['places', 'geometry'];
const mapContainerStyle = {
width: '100%',
height: '40vh',
};
const center = {
lat: 25.33800452203996,
lng: 55.393221974372864,
};
const options = {
zoomControl: true,
};
const circleOptions = {
strokeColor: '#00a3a6',
strokeOpacity: 0.4,
strokeWeight: 2,
fillColor: '#00a3a6',
fillOpacity: 0.1,
clickable: false,
draggable: false,
editable: false,
visible: true,
radius: 1050,
zIndex: 1,
};
const initialMarker = { lat: null, long: null };
export default function index() {
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY,
libraries,
});
const [marker, setMarker] = useState(initialMarker);
const [showModal, setShowModal] = useState(false);
const handleModalClose = () => {
setShowModal(false);
setMarker(initialMarker);
};
const onMapClick = (event) => {
setMarker({
lat: event.latLng.lat(),
lng: event.latLng.lng(),
});
console.log(window.google); //accessible here
};
const renderMap = () => {
return (
<GoogleMap
mapContainerStyle={mapContainerStyle}
zoom={14}
options={options}
center={center}
onClick={onMapClick}>
<Circle center={center} options={circleOptions} />
{marker && <Marker position={{ lat: marker.lat, lng: marker.lng }} />}
</GoogleMap>
);
};
useEffect(() => {
if (
//cant access google here
google.maps.geometry.spherical.computeDistanceBetween(
new google.maps.LatLng(center.lat, center.lng),
new google.maps.LatLng(marker.lat, marker.lng)
) > 1000
) {
setShowModal(true);
}
}, [marker.lat]);
if (loadError) return 'Error Loading Maps';
if (!isLoaded) return 'Loading Maps';
return (
<>
<div>{renderMap()}</div>
<Modal
show={showModal}
onHide={handleModalClose}
backdrop="static"
keyboard={false}
centered>
<Modal.Header closeButton>
<Modal.Title>Address is out of bounds</Modal.Title>
</Modal.Header>
<Modal.Body>Sorry ! We Dont Deliver Food In Your Area .</Modal.Body>
<Modal.Footer>
<Button onClick={handleModalClose}>Choose New Address</Button>
</Modal.Footer>
</Modal>
</>
);
}
Turn on the internet connection in your computer...it will definitely work
I am having trouble using setStates. I stored an array of markers for my Google Map in my state and I am using a for loop to iterate through each marker in order to change the position state of the marker using Google's Geocode API.
Here is my state:
state = {
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
markers: [
{
name: "Costco Wholesale",
address: "9151 Bridgeport Rd, Richmond, BC V6X 3L9",
position: { lat: 0, lng: 0 },
placeID: 'ChIJWc2NzuF0hlQRDu0NNhdQCjM'
} //just trying to get this one to work first before I add in the others
],
busy: []
};
Here is the function(declared inside the class):
findLatLong(){
for(let i = 0; i < this.state.markers.length; i++){
Geocode.fromAddress(this.state.markers[i].address).then(
response => {
const { lati, lngi } = response.results[0].geometry.location;
this.state.markers[i].position.setState({lat: lati, lng: lngi})
}
);
}
}
As you can see, I am passing the address contained in the same array element into the .fromAddress function and then using setState to set the lat and lng to the returned value.
I later call the function after the map renders but before the markers do:
<Map
google={this.props.google}
zoom={14}
style={mapStyles}
initialCenter={{ lat: 49.166590, lng: -123.133569 }}
>
{this.findLatLong}
{this.state.markers.map((marker, index) => (
<Marker
key={index}
onClick={this.onMarkerClick}
name={marker.name}
position={marker.position}
/>
))}
However marker's position state is not changing and is instead remaining as the filler values I passed during the initial state declaration.
Full code if it helps:
import React, { Component } from 'react';
import { Map, GoogleApiWrapper, InfoWindow, Marker } from 'google-maps-react';
import Geocode from 'react-geocode';
const key = '';
Geocode.setApiKey(key);
const mapStyles = {
width: '100%',
height: '100%'
};
export class MapContainer extends Component {
state = {
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
markers: [
{
name: "Costco Wholesale",
address: "9151 Bridgeport Rd, Richmond, BC V6X 3L9",
position: { lat: 0, lng: 0 },
placeID: 'ChIJWc2NzuF0hlQRDu0NNhdQCjM'
}
],
busy: []
};
findLatLong(){
for(let i = 0; i < this.state.markers.length; i++){
Geocode.fromAddress(this.state.markers[i].address).then(
response => {
const { lati, lngi } = response.results[0].geometry.location;
this.state.markers[i].position.setState({lat: lati, lng: lngi})
}
);
}
}
componentDidMount() {
this.getList();
}
getList = () => {
fetch('/api/getList')
.then(res => res.json())
.then(percent => this.setState({ busy: percent }))
}
onMarkerClick = (props, marker, e) =>
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
onClose = props => {
if (this.state.showingInfoWindow) {
this.setState({
showingInfoWindow: false,
activeMarker: null
});
}
};
render() {
return (
<Map
google={this.props.google}
zoom={14}
style={mapStyles}
initialCenter={{ lat: 49.166590, lng: -123.133569 }}
>
{this.findLatLong}
{this.state.markers.map((marker, index) => (
<Marker
key={index}
onClick={this.onMarkerClick}
name={marker.name}
position={marker.position}
/>
))}
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
onClose={this.onClose}
>
<div>
<h4>{this.state.selectedPlace.name}</h4>
<h4>{this.state.busy}</h4>
</div>
</InfoWindow>
</Map>
);
}
}
Thank you in advance!
Attempt to fix #1
.then(
response => {
const { lati, lngi } = response.results[0].geometry.location;
this.setState(oldState => {
const newMarkers = [oldState.markers];
const modifiedMarker = newMarkers[i];
modifiedMarker.lat = lati;
modifiedMarker.lng = lngi;
return {oldState, markers: [newMarkers]};
//How do i implement the modifiedMarkers?
})
UPDATE
Actually it is better if you mutate the state just once and not inside the loop
findLatLong(){
const newMarkers = [...this.state.markers]
for(let i = 0; i < this.state.markers.length; i++){
Geocode.fromAddress(this.state.markers[i].address).then(
response => {
const { lati, lngi } = response.results[0].geometry.location;
newMarkers[i].position.lat = lati;
newMarkers[i].position.lng = lngi;
}
);
}
this.setState(oldState => {
return { ...oldState, markers: [...newMakers] };
});
}
That's no how you mutate the state, it should be something like this:
this.setState(oldState => {
const newMakers = [...oldState.makers];
const modifiedElement = newMakers[i];
modifiedElement.lat = lati;
modifiedElement.lng = lngi;
return { ...oldState, makers: [...newMakers] };
});
I have the following map, as you should on the map I have a polygon and markers which are the main points of the polygon, below I have the main points of the polygon printed, i.e. the markers.
I give the user the possibility to delete points of the polygon, as you can see below in the image by clicking on the X, the point is eliminated.
The problem is this, when I click on the X, the point is eliminated as a marker, but it seems to remain as a fixed point of the polygon, which it really shouldn't do, the polygon should change its shape, based on the eliminated point.
I am not able to understand where I am wrong.
Can you give me some help.
Link: codesandbox
Index:
import React from "react";
import ReactDOM from "react-dom";
import Map from "./Map";
import "./styles.css";
//import { makeStyles } from "#material-ui/core/styles";
import ExpansionPanel from "#material-ui/core/ExpansionPanel";
import ExpansionPanelSummary from "#material-ui/core/ExpansionPanelSummary";
import ExpansionPanelDetails from "#material-ui/core/ExpansionPanelDetails";
import Typography from "#material-ui/core/Typography";
import ExpandMoreIcon from "#material-ui/icons/ExpandMore";
import ClearIcon from "#material-ui/icons/Clear";
import TextField from "#material-ui/core/TextField";
const API_KEY = "MY_API_KEY";
/*const useStyles = makeStyles(theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
}));*/
const useStyles = theme => ({
root: {
width: "100%"
},
heading: {
fontSize: theme.typography.pxToRem(15),
fontWeight: theme.typography.fontWeightRegular
}
});
const center = {
lat: 38.9065495,
lng: -77.0518192
};
//const classes = useStyles();
//className={classes.heading}
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
paths: [
{ lat: 38.97330905858943, lng: -77.10469090410157 },
{ lat: 38.9209748864926, lng: -76.9083102888672 },
{ lat: 38.82689001319151, lng: -76.92204319902345 },
{ lat: 38.82261046915962, lng: -77.0181735701172 },
{ lat: 38.90174038629909, lng: -77.14314305253907 }
]
};
}
render() {
const { paths } = this.state;
return (
<div className="App2">
<Map
apiKey={API_KEY}
center={center}
paths={paths}
point={paths => this.setState({ paths })}
/>
{paths.map((pos, key) => {
return (
<ExpansionPanel key={key}>
<ExpansionPanelSummary
expandIcon={<ExpandMoreIcon />}
aria-controls="panel1a-content"
id="panel1a-header"
>
<ClearIcon
style={{ color: "#dc004e" }}
onClick={() => {
paths.splice(key, 1);
console.log(paths);
this.setState({ paths: paths });
}}
/>
<Typography>Point #{key}</Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
<TextField
fullWidth
key={"lat" + key}
label="Latitude"
type="text"
value={pos.lat}
disabled={true}
/>
<TextField
fullWidth
key={"lng" + key}
label="Longitude"
type="text"
value={pos.lng}
disabled={true}
/>
</ExpansionPanelDetails>
</ExpansionPanel>
);
})}
</div>
);
}
}
//export default withStyles(useStyles)(App);
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Map:
import React, { useState, useRef, useCallback } from "react";
import {
LoadScript,
GoogleMap,
DrawingManager,
Polygon,
Marker
} from "#react-google-maps/api";
import "./styles.css";
const libraries = ["drawing"];
const options = {
drawingControl: true,
drawingControlOptions: {
drawingModes: ["polygon"]
},
polygonOptions: {
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2,
clickable: true,
editable: true,
draggable: true,
zIndex: 1
}
};
class LoadScriptOnlyIfNeeded extends LoadScript {
componentDidMount() {
const cleaningUp = true;
const isBrowser = typeof document !== "undefined"; // require('#react-google-maps/api/src/utils/isbrowser')
const isAlreadyLoaded =
window.google &&
window.google.maps &&
document.querySelector("body.first-hit-completed"); // AJAX page loading system is adding this class the first time the app is loaded
if (!isAlreadyLoaded && isBrowser) {
// #ts-ignore
if (window.google && !cleaningUp) {
console.error("google api is already presented");
return;
}
this.isCleaningUp().then(this.injectScript);
}
if (isAlreadyLoaded) {
this.setState({ loaded: true });
}
}
}
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState(paths);
const [state, setState] = useState({
drawingMode: "polygon"
});
const noDraw = () => {
setState(function set(prevState) {
return Object.assign({}, prevState, {
drawingMode: "maker"
});
});
};
const onPolygonComplete = React.useCallback(
function onPolygonComplete(poly) {
const polyArray = poly.getPath().getArray();
let paths = [];
polyArray.forEach(function(path) {
paths.push({ lat: path.lat(), lng: path.lng() });
});
setPath(paths);
console.log("onPolygonComplete", paths);
point(paths);
noDraw();
poly.setMap(null);
},
[point]
);
/*const onLoad = React.useCallback(function onLoad(map) {
//console.log(map);
}, []);
const onDrawingManagerLoad = React.useCallback(function onDrawingManagerLoad(
drawingManager
) {
// console.log(drawingManager);
},
[]);*/
// Define refs for Polygon instance and listeners
const polygonRef = useRef(null);
const listenersRef = useRef([]);
// Call setPath with new edited path
const onEdit = useCallback(() => {
if (polygonRef.current) {
const nextPath = polygonRef.current
.getPath()
.getArray()
.map(latLng => {
return { lat: latLng.lat(), lng: latLng.lng() };
});
setPath(nextPath);
point(nextPath);
}
}, [setPath, point]);
// Bind refs to current Polygon and listeners
const onLoad = useCallback(
polygon => {
polygonRef.current = polygon;
const path = polygon.getPath();
listenersRef.current.push(
path.addListener("set_at", onEdit),
path.addListener("insert_at", onEdit),
path.addListener("remove_at", onEdit)
);
},
[onEdit]
);
// Clean up refs
const onUnmount = useCallback(() => {
listenersRef.current.forEach(lis => lis.remove());
polygonRef.current = null;
}, []);
console.log(path);
return (
<div className="App">
<LoadScriptOnlyIfNeeded
id="script-loader"
googleMapsApiKey={apiKey}
libraries={libraries}
language="it"
region="us"
>
<GoogleMap
mapContainerClassName="App-map"
center={center}
zoom={10}
version="weekly"
//onLoad={onLoad}
>
{path.length === 0 ? (
<DrawingManager
drawingMode={state.drawingMode}
options={options}
onPolygonComplete={onPolygonComplete}
//onLoad={onDrawingManagerLoad}
editable
draggable
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
/>
) : (
<Polygon
options={{
fillColor: `#2196F3`,
strokeColor: `#2196F3`,
fillOpacity: 0.5,
strokeWeight: 2
}}
// Make the Polygon editable / draggable
editable
draggable
path={path}
// Event used when manipulating and adding points
onMouseUp={onEdit}
// Event used when dragging the whole Polygon
onDragEnd={onEdit}
onLoad={onLoad}
onUnmount={onUnmount}
/>
)}
{path.map((pos, key) => {
return <Marker key={key} label={"" + key} position={pos} />;
})}
</GoogleMap>
</LoadScriptOnlyIfNeeded>
</div>
);
}
Add useEffect to Map component to update paths for each render
export default function Map({ apiKey, center, paths = [], point }) {
const [path, setPath] = useState();
const [state, setState] = useState({
drawingMode: "polygon"
});
useEffect(() => {
setPath(paths);
}, [paths]);
.
.
.
and use filter instead of splice
In React you should never mutate the state directly.
onClick={() => {
this.setState({
paths: this.state.paths.filter((_, i) => i !== key)
});
or use splice like below
onClick={() => {
const paths = this.state.paths;
this.setState({
paths: [...paths.slice(0,key), ...paths.slice(key+1)]})
}}
codesandbox
During the Cancel Event, Call the function with the index of that array in the function parameter .
removetheArray = value => {
const { paths } = this.state;
this.setState({
paths: paths.splice(value, 1)
});
};
Function name is the removetheArray and pass the index as the value, the array as removed and map is updated incase map is not updated you have to init the map.
When I use Polyline it gives me this error:
attempt to invoke interface method 'java.util.iterator java.util.list.iterator()' on a null object
At first, I thought I made something wrong or I mis-used google-map API so I tried with some hard-coded coords (as shown in my code below) but without any progress.
The code will run fine if I remove the Polyline part.
I googled hoping to find any solution but without any success also.
some info my about dev platform:
expo version 2.19.1
yarn version 1.16.0
Node version 10.15.2
Testing on a physical device with android PI installed.
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Permissions, MapView } from 'expo'
// import Polyline from '#mapbox/polyline'
import { Polyline } from 'react-native-maps'
const locations = require('./locations.json')
export default class App extends React.Component {
state = {
latitude: null,
longitude: null,
locations: locations
}
componentDidMount() {
this.GetLocations()
}
GetLocations = async () => {
const { status } = await Permissions.getAsync(Permissions.LOCATION)
if (status !== 'granted') {
const response = await Permissions.askAsync(Permissions.LOCATION)
}
navigator.geolocation.getCurrentPosition(
({ coords: { latitude, longitude }}) => this.setState({ latitude, longitude}, this.mergeCoords),
(err) => console.log(`Error: ${err}`)
)
const { locations: [sampleLocation] } = this.state
this.setState({
desLatitude: sampleLocation.coords.latitude,
desLongitude: sampleLocation.coords.longitude,
}, this.mergeCoords)
}
mergeCoords = () => {
const { latitude, longitude, desLatitude, desLongitude } = this.state
const hasStartAndEnd = ( latitude !== null && desLatitude !== null )
// if the line have start and end
if (hasStartAndEnd) {
const concatStart = `${latitude},${longitude}`
const concatEnd = `${desLatitude},${desLongitude}`
this.getDirections(concatStart, concatEnd)
}
}
async getDirections(startLoc, desLoc) {
try {
// const res = await fetch(`https://maps.googleapis.com/maps/api/directions/json?key=MY_API_KEY&origin=${startLoc}&destination=${desLoc}`)
// const resJson = await res.json()
// const points = Polyline.decode(resJson.routes[0].overview_polyline.points)
// const coords = points.map( point => {
// return {
// latitude: point[0],
// longitude: point[1]
// }
// })
const coords = {
latitude: 31.262353,
longitude: 29.989506,
}
console.log("point, coords: ", coord)
this.setState({coords})
} catch(err) {
console.log('Error: ', err)
}
}
render() {
const { latitude, longitude, coords } = this.state
if (latitude) {
return (
<MapView
style={{ flex: 1 }}
showsUserLocation
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
<MapView.Polyline
strokeWidth={2}
strokeColor="red"
coordinates={coords}
/>
</MapView>
)
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>We need your permissions !!</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
//use condional rendering
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Permissions, MapView } from 'expo'
// import Polyline from '#mapbox/polyline'
import { Polyline } from 'react-native-maps'
const locations = require('./locations.json')
export default class App extends React.Component {
state = {
latitude: null,
longitude: null,
locations: locations,
direct : false
}
componentDidMount() {
this.GetLocations()
}
GetLocations = async () => {
const { status } = await Permissions.getAsync(Permissions.LOCATION)
if (status !== 'granted') {
const response = await Permissions.askAsync(Permissions.LOCATION)
}
navigator.geolocation.getCurrentPosition(
({ coords: { latitude, longitude }}) => this.setState({ latitude, longitude},
this.mergeCoords),
(err) => console.log(`Error: ${err}`)
)
const { locations: [sampleLocation] } = this.state
this.setState({
desLatitude: sampleLocation.coords.latitude,
desLongitude: sampleLocation.coords.longitude,
}, this.mergeCoords)
}
mergeCoords = () => {
const { latitude, longitude, desLatitude, desLongitude } = this.state
const hasStartAndEnd = ( latitude !== null && desLatitude !== null )
// if the line have start and end
if (hasStartAndEnd) {
const concatStart = `${latitude},${longitude}`
const concatEnd = `${desLatitude},${desLongitude}`
this.getDirections(concatStart, concatEnd)
}
}
async getDirections(startLoc, desLoc) {
try {
// const res = await fetch(`https://maps.googleapis.com/maps/api/directions/json?
key=MY_API_KEY&origin=${startLoc}&destination=${desLoc}`)
// const resJson = await res.json()
// const points = Polyline.decode(resJson.routes[0].overview_polyline.points)
// const coords = points.map( point => {
// return {
// latitude: point[0],
// longitude: point[1]
// }
// })
const coords = {
latitude: 31.262353,
longitude: 29.989506,
}
console.log("point, coords: ", coord)
this.setState({coords, direct:true})
} catch(err) {
console.log('Error: ', err)
}
}
render() {
const { latitude, longitude, coords } = this.state
if (latitude) {
return (
<MapView
style={{ flex: 1 }}
showsUserLocation
initialRegion={{
latitude,
longitude,
latitudeDelta: 0.0922,
longitudeDelta: 0.0421,
}}
>
{this.state.direct &&
<MapView.Polyline
strokeWidth={2}
strokeColor="red"
coordinates={coords}
/>}
</MapView>
)
}
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center'}}>
<Text>We need your permissions !!</Text>
</View>
)
}
}
I am using leaflet with react-leaflet.
OverpassLayer is working when geolocation is enabled. When Geolocation is blocked because I'm on localhost, the app isn't even entering the OverpassLayer component.
App.js
import OverpassLayer from './OverpassLayer'
class App extends React.Component {
state = {
zoom: 16,
position: {
lat: 51.505,
lng: -0.09,
},
mapKey: Math.random(),
overpassLayerKey: Math.random()
}
componentDidMount () {
//center map on user's current position
this.handleGeolocation()
this.refreshOverpassLayer()
}
handleGeolocation = () => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
(position) => {
this.setState({
position: {
lat: position.coords.latitude,
lng: position.coords.longitude,
},
mapKey: Math.random()
})
}, (err) => {
console.log("Geolocation did not work: " + err)
}
)
} else {
console.log("Geolocation did not work. Navigator.geolocation falsy")
}
}
refreshOverpassLayer = () => {
this.setState({
overpassLayerKey: Math.random()
})
}
render () {
return (
<Map
style={{height: "100vh", width: "100vw"}}
zoom={this.state.zoom}
center={[this.state.position.lat, this.state.position.lng]}
key={this.state.mapKey}
ref='map'
>
<TileLayer
url='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'
attribution='© OpenStreetMap contributors'
/>
<OverpassLayer
key={this.state.overpassLayerKey}
/>
</Map>
)
}
}
OverpassLayer.js
import {LayerGroup} from 'react-leaflet'
import L from 'leaflet'
import OverPassLayer from 'leaflet-overpass-layer'
export default class OverpassLayer extends LayerGroup {
componentWillReceiveProps(nextProps) {
console.log(nextProps.key)
console.log('OverpassLayer receiving props')
const query = '('
+ 'node["amenity"]({{bbox}});'
+ 'way["amenity"]({{bbox}});'
+ 'relation["amenity"]({{bbox}});'
+ ');'
+ 'out body;'
+ '>;'
+ 'out skel qt;'
const opl = new L.OverPassLayer({
'query': query,
'endPoint': 'https://overpass-api.de/api/',
})
nextProps.map.addLayer(opl)
}
}
The issue is componentWillReceiveProps doesn't fire on the first render. I had to addLayer in the constructor as well.