I want to move multiple markers on the google map when I get the latitude and longitude from MongoDB. I'm always getting updated latitude and longitude from db, but my markers are not moving, and after refreshing the page, the markers positions are changing, but I need to do it without refreshing the page.
This is my code`
class Maps extends React.Component {
constructor(props){
super(props);
this.state = {
dronePosition: []
};
var _this = this;
const config = {
headers: {
"Authorization" : `Bearer ${localStorage.getItem('token')}`,
}
};
// If I'm using setInterval, the markers are not showing at all. That's why here I call the getAllDrones() function
// setInterval(function(){
axios.get(packages.proxy+'drones/getAllDrones',config)
.then(res => {
//Here I'm always getting updated positions for markers from backend.
_this.state.dronePosition = [];
res.data.forEach( function(element) {
if(element.userId == localStorage.getItem("user_id")){
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/red-dot.png"})
}
else{
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png"})
}
});
_this.getAllDrones();
})
// }, 2000)
}
getAllDrones(){
var _this = this;
const config = {
headers: {
"Authorization" : `Bearer ${localStorage.getItem('token')}`,
}
};
axios.get(packages.proxy+'drones/getAllDrones',config)
.then(res => {
_this.state.dronePosition = [];
res.data.forEach( function(element) {
if(element.userId == localStorage.getItem("user_id")){
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/red-dot.png"})
}
else{
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png"})
}
});
_this.getAllDrones2();
})
}
getAllDrones2(){
var _this = this;
const config = {
headers: {
"Authorization" : `Bearer ${localStorage.getItem('token')}`,
}
};
axios.get(packages.proxy+'drones/getAllDrones',config)
.then(res => {
_this.state.dronePosition = [];
res.data.forEach( function(element) {
if(element.userId == localStorage.getItem("user_id")){
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/red-dot.png"})
}
else{
_this.state.dronePosition.push({id: element._id, latitude: element.latitude, longitude: element.longitude, photo: "http://maps.google.com/mapfiles/ms/icons/blue-dot.png"})
}
});
_this.getAllDrones();
})
}
render(){
var _this = this;
const { google } = this.props;
const icon = {
url: `data:image/jpeg;base64,${binary_data}`,
scaledSize: new google.maps.Size(40, 40),
origin: new google.maps.Point(0,0),
anchor: new google.maps.Point(0, 0)
};
return (
<div>
<Header />
<Map className="map" google={google} initialCenter={userLocation} zoom={15} onClick={this.onMapClicked} >
{_this.state.dronePosition.map(marker => (
<Marker
onClick={_this.MarkerClick.bind(_this, marker.id)}
icon={marker.photo}
position={{ lat: marker.latitude, lng: marker.longitude }}
key={marker.id}
/>
))}
</Map>
</div>
)
}
If you want the markers to update without a refresh of the page you need to add them to the component state. Since I don't have access to your mongo-db I've used a dummy api just for demo purpose.
And when making api-calls they should be used in Lifecycle-method componentDidMount, not in the constructor.
I've left out the if-statement for local storage and element.userID since I don't know what that is and the component since I don't have access to it.
import React from "react";
import axios from "axios";
export default class Maps extends React.Component {
constructor(props) {
super(props);
this.state = {
dronePosition: []
};
}
componentDidMount() {
this.refreshMarkers();
}
refreshMarkers = () => {
// Clear state to prevent duplicates
this.setState({dronePosition: []});
const config = {
headers: {
Authorization: `Bearer ${localStorage.getItem("token")}`
}
};
axios.get("https://swapi.co/api/starships").then(res => {
res.data.results.forEach(element => {
this.setState({
dronePosition: [...this.state.dronePosition, element]
});
});
console.log(this.state.dronePosition);
});
};
render() {
return(
<div>
<div onClick={this.refreshMarkers}>Click on me to refresh markers</div>
render the map here...
</div>
);
}
}
Related
Following is my code segment. Please refer to MapView.Marker, even on giving the coordinates for my current location nothing is displayed, same behavior is observed when i map service locations array and provide latitude,longitude values using it.
When i use mapview marker like this , a single marker is displayed but as for this component i have to display multiple markers on the map with different coordinates i have to go with Mapview.Marker. Please point out what am i missing here.
export default class index extends Component {
constructor(props) {
super(props);
this.state = {
text: "",
source: require("../../Image/User_default.png"),
location: {
latitude: 0,
latitudeDelta: 0,
longitude: 0,
longitudeDelta: 0,
},
serviceLocations: [],
};
}
componentDidMount() {
this._getLocationAsync();
this._getServices();
}
_getServices = () => {
create_service
.getAllServices()
.then((res) => {
this.setState({
serviceLocations: res,
});
})
.catch((error) => {
console.log(error);
});
};
_getLocationAsync = async () => {
let { status } = await Permissions.askAsync(Permissions.LOCATION);
if (status !== "granted") {
Alert.alert("Error", "Permission to access location was denied", [
{ text: "OK" },
]);
} else {
let location = await Location.getCurrentPositionAsync({});
location_service
.setCurrentUserLocation(location)
.then(async (res) => {
// console.log("_getLocationAsync:", res);
})
.catch((err) => console.log(err));
}
};
render() {
const { serviceLocations } = this.state;
return (
<View style={style.container}>
<MapView
style={style.mapStyle}
provider={PROVIDER_GOOGLE}
region={{
latitude: 33.650073,
longitude: 73.153164,
latitudeDelta: 0.0921,
longitudeDelta: 0.0421,
}}
showsUserLocation={true}
/>
<Marker/>
{serviceLocations.length
? serviceLocations.map((serviceLocation,key) => {
return (
<MapView.Marker
coordinate={{
latitude: 33.650073,
longitude: 73.153164,
}}
key={key}
// image={require("../../Image/location-pin.png")}
/>
);
})
: null}
</View>
);
}
}
Sorted the solution myself. Actually what i was doing wrong in this code was that i was using the <Marker> outside the scope of <MapView> which is why markers were not displayed on my maps.
As soon as i corrected the scope issue my problem was solved.
I'm traying to draw a route between two points with react-google-maps but is not working for me. Can u help me with this problem? Here's a example of my react component.
import React from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
} from 'react-google-maps';
import MapDirectionsRenderer from './app_map_directions_render';
const Map = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultCenter={props.defaultCenter}
defaultZoom={props.defaultZoom}
>
{props.places.map((marker, index) => {
const position = {lat: marker.latitude, lng: marker.longitude};
return <Marker key={index} position={position}/>;
})}
<MapDirectionsRenderer places={props.places} travelMode={window.google.maps.TravelMode.DRIVING} />
</GoogleMap>
))
);
const AppMap = props => {
const {places} = props;
const {
loadingElement,
containerElement,
mapElement,
defaultCenter,
defaultZoom
} = props;
return (
<Map
googleMapURL={
'https://maps.googleapis.com/maps/api/js?key=' +
googleMapsApiKey +
'&v=3.exp&libraries=geometry,drawing,places'
}
places={places}
loadingElement={loadingElement || <div style={{height: `100%`}}/>}
containerElement={containerElement || <div style={{height: "80vh"}}/>}
mapElement={mapElement || <div style={{height: `100%`}}/>}
defaultCenter={defaultCenter || {lat: 25.798939, lng: -80.291409}}
defaultZoom={defaultZoom || 11}
/>
);
};
export default AppMap;
And my MapDirectionsRenderer Component
import React, {Component} from 'react';
import { DirectionsRenderer } from "react-google-maps";
export default class MapDirectionsRenderer extends Component {
state = {
directions: null,
error: null
};
componentDidMount() {
const { places, travelMode } = this.props;
const waypoints = places.map(p =>({
location: {lat: p.latitude, lng: p.longitude},
stopover: true
}))
if(waypoints.length >= 2){
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new window.google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
if (status === window.google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
this.setState({ error: result });
}
}
);
}
}
render() {
if (this.state.error) {
return <h1>{this.state.error}</h1>;
}
return <DirectionsRenderer directions={this.state.directions} />;
}
}
To render a route Google Maps API provides Directions Service, in case of react-google-maps library DirectionsRenderer component is available which is a wrapper around DirectionsRenderer class which in turn:
Renders directions obtained from the DirectionsService.
Assuming the data for route is provided in the following format:
const places = [
{latitude: 25.8103146,longitude: -80.1751609},
{latitude: 27.9947147,longitude: -82.5943645},
{latitude: 28.4813018,longitude: -81.4387899},
//...
]
the following component could be introduced to calculate and render directions via react-google-maps library:
class MapDirectionsRenderer extends React.Component {
state = {
directions: null,
error: null
};
componentDidMount() {
const { places, travelMode } = this.props;
const waypoints = places.map(p =>({
location: {lat: p.latitude, lng:p.longitude},
stopover: true
}))
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
this.setState({ error: result });
}
}
);
}
render() {
if (this.state.error) {
return <h1>{this.state.error}</h1>;
}
return <DirectionsRenderer directions={this.state.directions} />;
}
}
Here is a demo
For React 16.8 or above, MapDirectionsRenderer could be implemented (using Hooks) as below:
function MapDirectionsRenderer(props) {
const [directions, setDirections] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
const { places, travelMode } = props;
const waypoints = places.map(p => ({
location: { lat: p.latitude, lng: p.longitude },
stopover: true
}));
const origin = waypoints.shift().location;
const destination = waypoints.pop().location;
const directionsService = new google.maps.DirectionsService();
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: travelMode,
waypoints: waypoints
},
(result, status) => {
console.log(result)
if (status === google.maps.DirectionsStatus.OK) {
setDirections(result);
} else {
setError(result);
}
}
);
});
if (error) {
return <h1>{error}</h1>;
}
return (
directions && (
<DirectionsRenderer directions={directions} />
)
);
}
I want to make two components: App and Map. However when I try to make a new brand Map component and send the data from App to Map component, I cannot.
My App (default) component holds the data as a state. When I try to send this state to the Map component. It holds the data as a prop.
And of course If I don't separate them and write everything in App.js, everything works as I expected (markers shown on the map). But I want to control all states in the parent component.
Am I violating a fundamental React rule? How can I fix that?
App.js
import React, { Component } from "react";
import "./App.css";
import Map from "./Map";
class App extends Component {
constructor(props) {
super(props);
this.state = {
locations: [],
markers: []
};
}
componentDidMount() {
fetch(
"correct_foursquare_api_url"
)
.then(response => response.json())
.then(data =>
data.response.venues.map(place => ({
id: place.id,
name: place.name,
lat: place.location.lat,
lng: place.location.lng
}))
)
.then(locations => {
this.setState({ locations });
});
}
render() {
return (
<div className="App">
<Map locations={this.state.locations} />
</div>
)
}
}
export default App;
Map.js
import React, { Component } from "react";
/* global google */
class Map extends Component {
constructor(props) {
super(props);
this.state = {
locations: [],
markers: []
};
}
componentDidMount() {
this.callMap();
}
callMap() {
window.initMap = this.initMap;
loadJS(
"api_url"
);
}
// Map
initMap = () => {
const { locations, markers } = this.state;
let map = new google.maps.Map(document.getElementById("map"), {
center: { lat: 59.4827293, lng: -83.1405355 },
zoom: 13
});
// Markers
for (var i = 0; i < locations.length; i++) {
var title = locations[i].name;
var position = new google.maps.LatLng(locations[i].lat, locations[i].lng);
var id = locations[i].id;
var marker = new google.maps.Marker({
map: map,
position: position,
title: title,
animation: google.maps.Animation.DROP,
id: id
});
markers.push(marker);
}
};
render() {
return <div id="map" />;
}
}
function loadJS(src) {
var ref = window.document.getElementsByTagName("script")[0];
var script = window.document.createElement("script");
script.src = src;
script.async = true;
ref.parentNode.insertBefore(script, ref);
}
export default Map;
You store your locations in the state of the App component, but you also have locations in state of the Map component that you use on mount.
You could instead wait with rendering the Map component until the locations request is finished, and then use the locations props in the Map component passed down from App instead.
Example
class App extends Component {
constructor(props) {
super(props);
this.state = {
locations: [],
markers: []
};
}
componentDidMount() {
fetch("correct_foursquare_api_url")
.then(response => response.json())
.then(data => {
const locations = data.response.venues.map(place => ({
id: place.id,
name: place.name,
lat: place.location.lat,
lng: place.location.lng
}));
this.setState({ locations });
});
}
render() {
const { locations, markers } = this.state;
if (locations.length === 0) {
return null;
}
return (
<div className="App">
<Map locations={locations} markers={markers} />
</div>
);
}
}
class Map extends Component {
// ...
initMap = () => {
const { locations, markers } = this.props;
// ...
};
// ...
}
I'm new to React and currently trying to learn how to use react-google-maps library. Tried to show a map with users geolocation as the initialCenter of the map.
This is my code:
import React from "react";
import { GoogleApiWrapper, Map } from "google-maps-react";
export class MapContainer extends React.Component {
constructor(props) {
super(props);
this.state = { userLocation: { lat: 32, lng: 32 } };
}
componentWillMount(props) {
this.setState({
userLocation: navigator.geolocation.getCurrentPosition(
this.renderPosition
)
});
}
renderPosition(position) {
return { lat: position.coords.latitude, lng: position.coords.longitude };
}
render() {
return (
<Map
google={this.props.google}
initialCenter={this.state.userLocation}
zoom={10}
/>
);
}
}
export default GoogleApiWrapper({
apiKey: "-----------"
})(MapContainer);
Insted of creating a map with users location I get an initialCenter of my default state values.
How can I fix it? Am I even using the lifecycle function right?
Thank you very much for your help
navigator.geolocation.getCurrentPosition is asynchronous, so you need to use the success callback and set the user location in there.
You could add an additional piece of state named e.g. loading, and only render when the user's geolocation is known.
Example
export class MapContainer extends React.Component {
state = { userLocation: { lat: 32, lng: 32 }, loading: true };
componentDidMount(props) {
navigator.geolocation.getCurrentPosition(
position => {
const { latitude, longitude } = position.coords;
this.setState({
userLocation: { lat: latitude, lng: longitude },
loading: false
});
},
() => {
this.setState({ loading: false });
}
);
}
render() {
const { loading, userLocation } = this.state;
const { google } = this.props;
if (loading) {
return null;
}
return <Map google={google} initialCenter={userLocation} zoom={10} />;
}
}
export default GoogleApiWrapper({
apiKey: "-----------"
})(MapContainer);
I am trying to display the user location on the map using google-maps-react. I followed the fullstack tutorial, but I just can't seem to display the user location. I will display my Map.js Component below. Please help me point out what I am doing wrong. Thank you.
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
class Map extends Component {
constructor(props) {
super(props);
const {lat, lng} = this.props.initialCenter;
this.state = {
currentLocation: {
lat: lat,
lng: lng
}
}
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.google !== this.props.google) {
this.loadMap();
}
if (prevState.currentLocation !== this.state.currentLocation) {
this.recenterMap();
}
}
recenterMap() {
const map = this.map;
const curr = this.state.currentLocation;
const google = this.props.google;
const maps = google.maps;
if (map) {
let center = new maps.LatLng(curr.lat, curr.lng)
map.panTo(center)
}
}
componentDidMount() {
if (this.props.centerAroundCurrentLocation) {
if (navigator && navigator.geolocation) {
navigator.geolocation.getCurrentPosition((pos) => {
const coords = pos.coords;
this.setState({
currentLocation: {
lat: coords.latitude,
lng: coords.longitude
}
})
})
}
}
this.loadMap();
}
loadMap() {
if (this.props && this.props.google) {
// google is available
const {google} = this.props;
const maps = google.maps;
const mapRef = this.refs.map;
const node = ReactDOM.findDOMNode(mapRef);
let {initialCenter, zoom} = this.props;
const {lat, lng} = initialCenter;
const center = new maps.LatLng(lat, lng);
const mapConfig = Object.assign({}, {
center: center,
zoom: zoom
})
this.map = new maps.Map(node, mapConfig);
}
}
render() {
const style = {
width: '100vw',
height: '100vh'
}
return (
<div ref='map' style={style}>
Loading map...
</div>
)
}
}
Map.propTypes = {
google: React.PropTypes.object,
zoom: React.PropTypes.number,
initialCenter: React.PropTypes.object,
centerAroundCurrentLocation: React.PropTypes.bool
}
Map.defaultProps = {
zoom: 13,
// San Francisco, by default
initialCenter: {
lat: 37.774929,
lng: -122.419416
},
centerAroundCurrentLocation: false
}
export default Map