I'm using google-maps-react library for maps in my react application, and I'm adding search address functionality in my application and facing CORS error.
Error:
Autocomplete.js
import React, { Component } from 'react';
import styled from 'styled-components';
const Wrapper = styled.div`
position: relative;
align-items: center;
justify-content: center;
width: 100%;
padding: 20px;
text-align:center;
`;
class AutoComplete extends Component {
constructor(props) {
super(props);
this.clearSearchBox = this.clearSearchBox.bind(this);
}
componentDidMount({ map, mapApi } = this.props) {
const options = {
// restrict your search to a specific type of result
types: ['address'],
// restrict your search to a specific country, or an array of countries
// componentRestrictions: { country: ['gb', 'us'] },
};
this.autoComplete = new mapApi.places.Autocomplete(
this.searchInput,
options,
);
this.autoComplete.addListener('place_changed', this.onPlaceChanged);
this.autoComplete.bindTo('bounds', map);
}
componentWillUnmount({ mapApi } = this.props) {
mapApi.event.clearInstanceListeners(this.searchInput);
}
onPlaceChanged = ({ map, addplace } = this.props) => {
const place = this.autoComplete.getPlace();
if (!place.geometry) return;
if (place.geometry.viewport) {
map.fitBounds(place.geometry.viewport);
} else {
map.setCenter(place.geometry.location);
map.setZoom(17);
}
addplace(place);
this.searchInput.blur();
};
clearSearchBox() {
this.searchInput.value = '';
}
render() {
return (
<Wrapper>
<input
className="search-input"
ref={(ref) => {
this.searchInput = ref;
}}
type="text"
onFocus={this.clearSearchBox}
placeholder="Enter a location"
/>
</Wrapper>
);
}
}
export default AutoComplete;
Google Map Component:
const MyGoogleMap = () => {
const [apiReady, setApiReady] = useState(false);
const [map, setMap] = useState(null);
const [mapApi, setMapApi] = useState(null);
const [address, setAddress] = useState();
const [zoom, setZoom] = useState();
const [center, setCenter] = useState([]);
const [lat, setLat] = useState();
const [lng, setLng] = useState();
const [places, setPlaces] = useState();
const [draggable, setDraggable] = useState();
const setCurrentLocation = () => {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition(position => {
console.log('position.coords: ', position.coords.longitude);
console.log("[position.coords.latitude, position.coords.longitude]: ", [position.coords.latitude, position.coords.longitude])
setCenter([position.coords.latitude, position.coords.longitude]);
setLat(position.coords.latitude);
setLng(position.coords.longitude);
});
}
};
useEffect(() => {
setCurrentLocation();
}, []);
const handleApiLoaded = (map, maps) => {
console.log('map, maps: ', map, maps);
// use map and maps objects
if (map && maps) {
setApiReady(true);
setMap(map);
setMapApi(maps);
}
};
const _generateAddress = () => {
const geocoder = new mapApi.Geocoder();
geocoder.geocode({ location: { lat: lat, lng: lng } }, (results, status) => {
console.log(results);
console.log(status);
if (status === 'OK') {
if (results[0]) {
setZoom(12);
setAddress(results[0].formatted_address);
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
});
};
const onMarkerInteraction = (childKey, childProps, mouse) => {
setDraggable(true);
setLat(mouse.lat);
setLng(mouse.lng);
};
const onMarkerInteractionMouseUp = (childKey, childProps, mouse) => {
setDraggable(true);
_generateAddress();
};
const _onChange = ({ center, zoom }) => {
setZoom(zoom);
setCenter(center);
};
const _onClick = value => {
setLat(value.lat);
setLng(value.lng);
};
const addPlace = place => {
setPlaces([place]);
setLat(place.geometry.location.lat());
setLng(place.geometry.location.lng());
_generateAddress();
};
return (
<div style={{ height: '442px', width: '100%' }}>
{apiReady && <Autocomplete map={map} mapApi={mapApi} addplace={addPlace} />}
<GoogleMapReact
zoom={4}
center={center}
bootstrapURLKeys={{
key: 'API_KEY',
libraries: ['places', 'geometry'],
}}
yesIWantToUseGoogleMapApiInternals
onGoogleApiLoaded={({ map, maps }) =>
handleApiLoaded(map, maps)
}
></GoogleMapReact>
</div>
);
}
I'm following this article to integrate Google Maps with search
Related
I want to switch back and forth between two React components as shown below.
Every time the Photo Component is invoked and the user has taken a picture. I want the Game component to mount again (thus passing condition counter state variable as props, to be changed) before the photo component unmounts.
React is yelling at me for Warning: Can't perform a React state update on an unmounted component.
I understand the problem that my Photo component unmounts first before the state has updated.
I have looked into several similar questions and try to adopt their solutions e.g. having an additional variable to check if the component is mounted or not and few others.
Seems like none helped me.
Game.js
import React, { useState, useEffect } from 'react';
import { TextInput, Platform, Text, View, StyleSheet, Button, Alert } from 'react-native';
import Constants from 'expo-constants';
import * as Location from 'expo-location';
import * as Permissions from 'expo-permissions';
import MapView, { Marker, Circle, Polyline } from 'react-native-maps';
import Header from '../Components/Header';
import Footer from '../Components/Footer';
import PhotoHandler from './Photo';
import * as geolib from 'geolib';
import { event } from 'react-native-reanimated';
export default function GameArea() {
const [isFetching, setIsFetching] = useState(false);
const [generateTargets, setGenerateTargets] = useState(true);
const [targets, setTargets] = useState([]);
const [distances, setDistances] = useState([]);
const [index, setIndex] = useState(null);
const [gesture, setGesture] = useState({});
const [draggingMap, setdraggingMap] = useState(false);
const [numofTargets, setNumofTargets] = useState(null);
const [conditionCounter, setConditionCounter] = useState(false);
const [photoTaken, setPhotoTaken] = useState(false);
const [pickedLocation, setPickedLocation] = useState({
latitude: 123,
longitude: 123
});
const [errorMsg, setErrorMsg] = useState(null);
const [region, setRegion] = useState({
latitude: 123,
longitude: 123,
latitudeDelta: 0.001,
longitudeDelta: 0.001
});
const [mapBounds, setMapBounds] = useState({
n: null,
s: null,
e: null,
w: null
});
const [mapRef, updateMapRef] = useState(null);
useEffect(() => {
const verifyPermissions = async () => {
const result = await Permissions.askAsync(Permissions.LOCATION);
if (result.status !== 'granted') {
Alert.alert(
'Insufficient permissions!',
'You need to grant location permissions to use this app.',
[{ text: 'Okay' }]
);
return false;
}
return true;
};
(async () => {
const hasPermission = await verifyPermissions();
if (!hasPermission) {
return;
}
try {
setIsFetching(true);
const location = await Location.getCurrentPositionAsync({
});
setPickedLocation({
latitude: location.coords.latitude,
longitude: location.coords.longitude
});
setRegion({
latitude: location.coords.latitude,
longitude: location.coords.longitude,
latitudeDelta: 0.003,
longitudeDelta: 0.003
});
} catch (err) {
Alert.alert(
'Could not fetch location!',
'Please try again.',
[{ text: 'Okay' }]
);
}
setIsFetching(false);
})();
}, []);
const getBoundaries = () => {
if (mapRef === null) {
return;
}
mapRef
.getMapBoundaries()
.then((res) => {
setMapBounds({
n: res.northEast.latitude,
e: res.northEast.longitude,
s: res.southWest.latitude,
w: res.southWest.longitude
});
})
.catch((err) => console.log(err));
};
const onRegionChangeComplete = (region) => {
if (draggingMap === true && targets.length == 0) {
getBoundaries();
setRegion({
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta,
longitudeDelta: region.longitudeDelta
})
setdraggingMap(false);
}
else {
return;
}
}
function handleChange(newValue) {
setConditionCounter(newValue);
}
const ondraggingMap = () => {
setdraggingMap(true);
}
async function generatedTargets() {
var url = `https://xxx.yy.zz`;
if (numofTargets == null) {
Alert.alert(
"Missing",
"Enter Number of targets first",
[
{ text: "OK" }
]
);
}
else {
try {
let response = await fetch(
url,
);
let responseJson = await response.json();
console.log("Targets generated");
console.log(responseJson);
setTargets(responseJson);
setGenerateTargets(false);
} catch (error) {
Alert.alert(
"Failed: Network Error",
"Try again",
[
{ text: "OK" }
]
);
console.error(error);
}
}
}
useEffect(() => {
console.log('inside useEffect hook for measuring targets distances');
var lat1 = pickedLocation.latitude;
var lng1 = pickedLocation.longitude;
console.log("targets distance");
const R = 6371;
let targets_distance = [];
for (i = 0; i < targets.length; i++) {
const φ1 = lat1 * Math.PI / 180;
const φ2 = targets[i].lat * Math.PI / 180;
const Δφ = (targets[i].lat - lat1) * Math.PI / 180;
const Δλ = (targets[i].lng - lng1) * Math.PI / 180;
const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
Math.cos(φ1) * Math.cos(φ2) *
Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
const distance = R * c * 1000;
console.log('distance is'+ distance);
if(distance <=20){
setConditionCounter(true); //switch to photo component
targets.splice(i, 1);
console.log("targets after removal");
console.log(targets);
}
else{
targets_distance.push(distance);
}
}
console.log(targets_distance);
var index = 0;
var value = targets_distance[0];
for (var i = 1; i < targets_distance.length; i++) {
if (targets_distance[i] < value) {
value = targets_distance[i];
index = i;
}
}
console.log("smallest element is " + value + " at index " + index);
setIndex(index);
setDistances(targets_distance);
}, [pickedLocation]);
useEffect(() => {
console.log("Inside UseEffect watch position");
(async () => {
try {
await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.BestForNavigation,
distanceInterval: 10,
timeInterval: 3000,
},
(loc) => {
setPickedLocation({
latitude: loc.coords.latitude,
longitude: loc.coords.longitude
});
}
);
} catch (e) {
Alert.alert("Error");
}
})();
}, []);
const setNumberOfTargets = (numofTargets) =>{
setNumofTargets(numofTargets);
}
return (
<View style={styles.container}>
<Header title="Crowdsourcing" />
{conditionCounter?
<PhotoHandler conditionCounter={setConditionCounter} onChange={handleChange}/>:
<MapView style={styles.mapContainer}
region={{
latitude: region.latitude,
longitude: region.longitude,
latitudeDelta: region.latitudeDelta,
longitudeDelta: region.longitudeDelta
}}
showsUserLocation={true}
followUserLocation={true}
ref={(ref) => updateMapRef(ref)}
onPanDrag={ondraggingMap}
onRegionChangeComplete={onRegionChangeComplete}
>
{
targets.map((target, index) => (
<MapView.Marker
key={index}
coordinate={{
latitude: target.lat,
longitude: target.lng
}}
title={target.name}
/>
))}
</MapView>}
<Footer
Targets = {generateTargets}
numberOfTargets = {numofTargets}
setNumTargets = {setNumberOfTargets}
osmTargets = {generatedTargets}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
flexDirection: 'column',
width: '100%',
height: '100%'
},
mapContainer: {
width: '100%',
height: 500,
marginBottom: 20
},
footer: {
width: '100%',
height: 70,
backgroundColor: '#FF7F50',
alignItems: 'center',
justifyContent: 'center'
},
buttonContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-evenly'
}
});
Photo.js
import React, { useState, useEffect } from 'react';
import { StyleSheet, Text, View, Alert, Button, Image } from 'react-native';
import * as ImagePicker from 'expo-image-picker';
import * as Permissions from 'expo-permissions';
import * as Location from 'expo-location';
export default function PhotoHandler(props) {
const [pickedLocation, setPickedLocation] = useState({
latitude: null,
longitude: null
});
const [pickedImage, setPickedImage] = useState(null);
const verifyPermissions = async () =>{
const result = await Permissions.askAsync(Permissions.CAMERA_ROLL);
if(result.status != 'granted'){
Alert.alert(
'Insufficient Permissions!',
'You need to grant camera permissions to use this app',
[{text: 'Okay'}]
);
return false;
}
return true;
};
const imagetakenHandler = async () =>{
const hasPermission = await verifyPermissions();
if(!hasPermission){
return;
}
const image = await ImagePicker.launchCameraAsync({
mediaTypes: ImagePicker.MediaTypeOptions.Images,
quality: 1,
aspect: [1, 1],
base64: true,
exif: true
});
setPickedImage(image.base64);
};
useEffect(() => {
const requestOptions = {
method: 'POST',
headers: 'Content-type: application/x-www-form-urlencoded',
};
if(pickedImage != null){
props.onChange(false); // to switch back to Game component
let response = fetch(`https://xxx.yy.zz`, requestOptions)
.then(response => response.json())
.then(console.log('SUCCESS: '+response))
.catch(e => {
console.log(e);
});
}
}, [pickedImage])
return (
<View style={styles.container}>
{
Alert.alert(
"Success",
"Take photo",
[
{text: "Open Camera",
onPress: imagetakenHandler
}
]
)
}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 30
}
});
I'm trying to add the transform property to my create component. Then inside the transform, I call a function to get the lat and long based on the address parameters.
But before the geodecoder is done the dataprovider is already sending data to the API.
Is there anyway so that I can let the transform know the data is al set to be send?
Also kinda new to React. So, maybe I'm missing something obvious. Here's my code so far:
import * as React from "react";
import {Create, ReferenceArrayInput, SelectArrayInput, SimpleForm, TextInput} from 'react-admin';
import Geocode from "react-geocode";
Geocode.setApiKey("MY_API_KEY");
Geocode.setRegion("nl");
export const StoreCreate = props => {
function getLatLong(data) {
const addressObject = data.address;
if ((addressObject.postal_code.length >= 6 &&
addressObject.postal_code.length <= 7) &&
addressObject.street_name.length > 0 &&
addressObject.housenumber.length > 0
) {
const regex = /^[1-9][0-9]{3}[\s]?[A-Za-z]{2}$/i;
if (regex.test(addressObject.postal_code)) {
const address = addressObject.street_name + ' ' + addressObject.housenumber + ' ' + addressObject.postal_code;
Geocode.fromAddress(address).then(
response => {
const {lat, lng} = response.results[0].geometry.location;
console.log({
address: {
...addressObject, ...{
lat: lat,
lng: lng
}
}
});
return {
address: {
...addressObject, ...{
lat: lat,
lng: lng
}
}
};
},
error => {
console.error(error);
}
);
}
}
}
const transform = (data) => ({
...data,
...getLatLong(data)
});
return (
<Create {...props} transform={transform}>
<SimpleForm>
<TextInput source="name"/>
<TextInput source="email" type="email"/>
<TextInput source="address.street_name"/>
<TextInput source="address.housenumber"/>
<TextInput source="address.postal_code"/>
<TextInput source="address.city"/>
<ReferenceArrayInput source="store_types" reference="store-types">
<SelectArrayInput source="name"/>
</ReferenceArrayInput>
<TextInput source="address.lat"/>
<TextInput source="address.lng"/>
</SimpleForm>
</Create>
);
};
First, the getLatLong function does not return the promise.
function getLatLong(data) {
const addressObject = data.address;
if ((addressObject.postal_code.length >= 6 &&
addressObject.postal_code.length <= 7) &&
addressObject.street_name.length > 0 &&
addressObject.housenumber.length > 0
) {
const regex = /^[1-9][0-9]{3}[\s]?[A-Za-z]{2}$/i;
if (regex.test(addressObject.postal_code)) {
const address = addressObject.street_name + ' ' + addressObject.housenumber + ' ' + addressObject.postal_code;
return Geocode.fromAddress(address).then(
response => {
const {lat, lng} = response.results[0].geometry.location;
console.log({
address: {
...addressObject, ...{
lat: lat,
lng: lng
}
}
});
return {
address: {
...addressObject, ...{
lat: lat,
lng: lng
}
}
};
},
error => {
console.error(error);
throw error;
}
);
}
}
return {};
}
Then, you should use it correctly in your tranform function:
const transform = (data) => getLatLong(data).then((longData) => ({
...data,
...longData
}));
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 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,