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.
Related
I am teaching myself React whilst working on a project which uses the react-google-maps package to produce a map with directions from A to B. The map itself works fine, but I've now tried to print the corresponding route directions in html via the return method but cannot get it these instructions to print out.
From my research via Google and StackOverflow I think my issue may either be:
The scope of the 'this' keyword when trying to access my instructionList in the return method. In which case - what would I need to type to access my instructionList array of <li> items?
I've also tried
<ol>{DirectionsService.route.state.instructionList}</ol> and <ol> and {DirectionsComponent.DirectionsService.route.state.instructionList}</ol> which also didn't work
That when the page is loaded, the api response hasn't necessarily been received and thus my instructionList is null and cannot be rendered. In which case - how should this be handled?
Something else I'm unaware of in my syntax (I'm very much a beginner to react, and the react-google-maps package!)
In my code, I've defined an array called instructionList in the state which contains instructions for getting from A to B
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: { ...result },
markers: true
});
this.setState({
instructions: this.state.directions.routes[0].legs[0].steps
});
this.setState({
instructionList: this.state.instructions.map(instruction => {
return <li>instruction.instructions</li>;
})
});
}
I'm then trying to access this array in the class return method - but the error message is saying that instructionList is not defined.
return (
<div>
<DirectionsComponent/>
<div className="route instructions">
<h1>{title}</h1>
<ol>{this.state.instructionList}</ol>
</div>
<NotificationContainer />
</div>
Below is a fuller piece of code if that makes it easier to identify the issue.
class MyMapComponent extends React.Component {
constructor(props) {
super(props);
}
render() {
const {
startLat,
startLng,
finishLat,
finishLng,
transportMode,
title
} = this.props;
const DirectionsComponent = compose(
withProps({
googleMapURL:
"https://maps.googleapis.com/maps/api/js?key=APIKEYGOESHERE", //removed=&callback=initMap
loadingElement: <div style={{ height: `400px` }} />,
containerElement: <div style={{ width: `100%` }} />,
mapElement: <div style={{ height: `400px`, width: `400px` }} />
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route(
{
origin: new google.maps.LatLng(startLat, startLng),
destination: new google.maps.LatLng(finishLat, finishLng),
travelMode: google.maps.TravelMode[transportMode],
provideRouteAlternatives: true
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: { ...result },
markers: true
});
this.setState({
instructions: this.state.directions.routes[0].legs[0].steps
});
this.setState({
instructionList: this.state.instructions.map(instruction => {
return <li>instruction.instructions</li>;
})
});
} else {
console.error(
`There was an error fetching directions for the specified journey ${result}`
);
NotificationManager.error(
"Journey cannot be retrieved, please try again",
"Error",
20000
);
}
}
);
}
})
)(props => (
<GoogleMap defaultZoom={3}>
{props.directions && (
<DirectionsRenderer
directions={props.directions}
suppressMarkers={props.markers}
/>
)}
</GoogleMap>
));
return (
<div>
<DirectionsComponent />
<div className="route instructions">
<h1>{title}</h1>
<ol>{this.state.instructionList}</ol>
</div>
<NotificationContainer />
</div>
);
}
}
export default MyMapComponent;
Error message is currently TypeError: Cannot read property 'instructionList' of null
I have played around with the code and researched quite a bit but I'm going round in circles. I'm sure the solution is a quick one but I'm struggling to find it with my limited knowledge of React/react-google-maps so I'm very appreciative of anyone who is able to help :)
You haven't init your component's state. So you can't access a property of state. You need to init it in constructor.
constructor(props){
super(props);
this.state = { instructionList: [] };
}
Updated
You need to define onChangeInstructionList to change MyMapComponent's instructionList inside DirectionsComponent. You also need to move DirectionsComponent to componentDidMount of MyMapComponent to avoid infinite loop because of state changes.
class MyMapComponent {
constructor(props){
super(props);
this.state = {
instructionList: [],
};
this.onChangeInstructionList = this.onChangeInstructionList.bind(this);
}
componentDidMount() {
const {startLat, startLng, finishLat, finishLng, transportMode} = this.props;
const DirectionsComponent = compose(
withProps({
googleMapURL: "https://maps.googleapis.com/maps/api/js?key=APIKEYGOESHERE",//removed=&callback=initMap
loadingElement: <div style={{ height: `400px` }} />,
containerElement: <div style={{ width: `100%` }} />,
mapElement: <div style={{height: `400px`, width: `400px` }} />,
onChangeInstructionList: this.onChangeInstructionList,
}),
withScriptjs,
withGoogleMap,
lifecycle({
componentDidMount() {
const DirectionsService = new google.maps.DirectionsService();
DirectionsService.route({
origin: new google.maps.LatLng(startLat, startLng),
destination: new google.maps.LatLng(finishLat, finishLng),
travelMode: google.maps.TravelMode[transportMode],
provideRouteAlternatives: true
}, (result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: {...result},
markers: true
})
this.setState({instructions: this.state.directions.routes[0].legs[0].steps});
this.props.onChangeInstructionList(this.state.instructions.map(instruction => {
return (<li>instruction.instructions</li>);
}));
} else {
console.error(`There was an error fetching directions for the specified journey ${result}`);
NotificationManager.error("Journey cannot be retrieved, please try again", "Error", 20000);
}
});
}
})
)(props =>
<GoogleMap
defaultZoom={3}
>
{props.directions && <DirectionsRenderer directions={props.directions} suppressMarkers={props.markers}/>}
</GoogleMap>
);
this.setState({
DirectionsComponent,
})
}
onChangeInstructionList(newList) {
this.setState({
instructionList: newList,
});
}
render() {
const {title} = this.props;
const { DirectionsComponent, instructionList } = this.state;
return (
<div>
<DirectionsComponent/>
<div className="route instructions">
<h1>{title}</h1>
<ol>{instructionList}</ol>
</div>
<NotificationContainer />
</div>
)
}
}
export default MyMapComponent
What I'm trying to do is send in an array of addresses to a component, use google's geocoder to convert those addresses into lat / long coordinates, and then plat those places on a google map with markers using the google maps api react wrapper. I followed this tutorial pretty closely (https://dev.to/jessicabetts/how-to-use-google-maps-api-and-react-js-26c2) with the biggest difference being that I worked in geocoder. Because geocoder is asynchronous, I can't get the map to re-render with the newly converted coordinates after the promise is resolved. Below is the code I have right now:
import React, { Component } from 'react';
import {Map, InfoWindow, Marker, GoogleApiWrapper} from 'google-maps-react';
const mapStyles = {
width: '100%',
height: '300px'
};
let geocoder;
let addressData = [{location: "146 Pierrepont St, Brooklyn, NY, USA"}, {location: "153 Remsen St, Brooklyn, NY, USA"}];
export class MapContainer extends Component {
constructor (props) {
super(props);
this.onMarkerClick = this.onMarkerClick.bind(this);
this.displayMarkers = this.displayMarkers.bind(this);
this.state = {
lat: 40.6946768,
lng: -73.99161700000002,
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
places: [],
stores: [{latitude: 47.49855629475769, longitude: -122.14184416996333},
{latitude: 47.359423, longitude: -122.021071},
{latitude: 47.2052192687988, longitude: -121.988426208496},
{latitude: 47.6307081, longitude: -122.1434325},
{latitude: 47.3084488, longitude: -122.2140121},
{latitude: 47.5524695, longitude: -122.0425407}]
}
}
componentDidMount () {
this.plotPoints()
}
plotPoints () {
let locations = this.getPoints(geocoder)
let places = new Array()
Promise.all(locations)
.then(function(returnVals) {
returnVals.forEach(function(latLng) {
let place = {latitude: latLng[0], longitude: latLng[1]}
places.push(place)
})
})
this.setState (() => {
return {
places: places
}
});
}
getPoints(geocoder) {
let locationData = [];
for (let i = 0; i < addressData.length; i++) {
locationData.push(this.findLatLang(addressData[i].location, geocoder))
}
return locationData // array of promises
}
findLatLang(address, geocoder) {
return new Promise(function(resolve, reject) {
geocoder.geocode({
'address': address
}, function(results, status) {
if (status === 'OK') {
console.log(results);
resolve([results[0].geometry.location.lat(), results[0].geometry.location.lng()]);
} else {
reject(new Error('Couldnt\'t find the location ' + address));
}
})
})
}
displayMarkers (stores) {
return stores.map((place, index) => {
return <Marker key={index} id={index} position={{
lat: place.latitude,
lng: place.longitude
}}
onClick={() => console.log("You clicked me!")} />
})
}
onMarkerClick (props, marker, e) {
this.setState({
selectedPlace: props,
activeMarker: marker,
showingInfoWindow: true
});
};
render() {
geocoder = new this.props.google.maps.Geocoder();
return (
<div className="container place-map">
<div className="row">
<div className="col-md-12">
<Map
google={this.props.google}
zoom={14}
style={mapStyles}
initialCenter={{
lat: this.state.lat,
lng: this.state.lng
}}
>
{this.displayMarkers(this.state.stores)}
{this.displayMarkers(this.state.places)}
<InfoWindow
marker={this.state.activeMarker}
visible={this.state.showingInfoWindow}
>
<div>Your Location Here!</div>
</InfoWindow>
</Map>
</div>
</div>
</div>
);
}
}
export default GoogleApiWrapper({
apiKey: 'AIzaSyCOJDrZ_DXmHzbzSXv74mULU3aMu3rNrQc'
})(MapContainer);
The array of "stores" renders markers on the map since there are coordinates available at the initial render of the map - but the coordinates that get pushed onto the "places" array never render. If I put a log statement of the "places" into render() I can see that I am getting back valid coordinates from geocoder.
Help! Been banging my head on this for forever (as you can tell by the current sloppy state of the code).
You need to move the setState for places into the Promise.all callback.
You are calling it when the array is still empty and before the promises have resolved
Promise.all(locations)
.then((returnVals) =>{
returnVals.forEach((latLng) => {
let place = {
latitude: latLng[0],
longitude: latLng[1]
}
places.push(place)
})
// places now populated
this.setState(() => {
return {
places: places
}
});
});
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>
);
}
}
I have geolocation running in my app. In Xiaomi device in which i'm working on works fine. I tried on 2 other devices, and got Location request timed out error. Here's the code i have:
import React, { Component } from 'react'
import { View, AsyncStorage, Dimensions, PermissionsAndroid } from 'react-native';
const { width, height } = Dimensions.get('window')
const ASPECT_RATIO = width / height
const LATITUDE_DELTA = 0.0922
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO
class App extends Component {
constructor(props) {
super(props)
this.state = {
initialPosition: {
latitude: null,
longitude: null,
latitudeDelta: null,
longitudeDelta: null
}
}
}
requestLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{
'title': 'Location Permission',
'message': 'This App needs access to your location ' +
'so we can know where you are.'
}
)
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
console.log("You can use locations ")
navigator.geolocation.getCurrentPosition((position) => {
let lat = parseFloat(position.coords.latitude)
let long = parseFloat(position.coords.longitude)
let initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA
}
this.setState({ initialPosition: initialRegion })
this.setState({ markerPosition: initialRegion })
},
(error) => alert(JSON.stringify(error)),
{ enableHighAccuracy: false, timeout: 5000, maximumAge: 10000 }
)
} else {
console.log("Location permission denied")
}
} catch (err) {
console.warn(err)
}
}
componentDidMount() {
this.requestLocationPermission()
}
}
export default App
Some additional information:
React Native version: 0.57.8
Platform: Android
Dev tools: Android SDK v27
i have tried switching enableHighAccuracy to false but that didn't help. The error i get is like this:
I have a React app which uses Google Maps API. I am using Foursquare API also, to fetch data about venues. Currently i am fetching about venues near Nashville, TN, keywords "yoga" and "coffee". I want to use the user's current location, and Nashville as a fallback in case they do not allow.
i've got this from MDN:
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
function success(pos) {
var crd = pos.coords;
console.log('Your current position is:');
console.log(`Latitude : ${crd.latitude}`);
console.log(`Longitude: ${crd.longitude}`);
console.log(`More or less ${crd.accuracy} meters.`);
}
function error(err) {
console.warn(`ERROR(${err.code}): ${err.message}`);
}
navigator.geolocation.getCurrentPosition(success, error, options);
and am looking for help implementing this in my code. How do i start with replacing the near: "Nashville, TN", below with the geolocation code? This is my app.js:
import React, { Component } from 'react';
import './App.css';
import SquareAPI from './API/';
import Map from './component/Map';
import SideBar from './component/Sidebar';
class App extends Component {
constructor(){
super();
this.state = {
venues: [],
markers: [],
center: [],
zoom: 14,
updateSuperState: obj => {
this.setState(obj);
}
};
}
closeAllMarkers = () => {
const markers = this.state.markers.map(marker => {
marker.isOpen = false;
return marker;
});
this.setState({ markers: Object.assign(this.state.markers, markers) });
};
handleMarkerClick = marker => {
this.closeAllMarkers();
marker.isOpen = true;
this.setState({ markers: Object.assign(this.state.markers, marker) });
const venue =this.state.venues.find(venue => venue.id === marker.id);
SquareAPI.getVenueDetails(marker.id).then(res => {
const newVenue = Object.assign(venue, res.response.venue);
this.setState({ venues: Object.assign(this.state.venues, newVenue) })
console.log(newVenue);
});
};
handleListItemClick = venue =>{
const marker = this.state.markers.find(marker => marker.id === venue.id)
this.handleMarkerClick(marker)
}
componentDidMount(){
SquareAPI.search({
near:"Nashville, TN",
query: "yoga",
limit: 10
}).then(results => {
const { venues } = results.response;
const { center } = results.response.geocode.feature.geometry;
const markers = venues.map(venue => {
return {
lat: venue.location.lat,
lng: venue.location.lng,
isOpen: false,
isVisible: true,
id: venue.id
};
})
this.setState({ venues, center, markers });
}).catch(error =>{
console.log("Error: " + error)
})
}
render() {
return (
<div className="App">
<SideBar {...this.state} handleListItemClick={this.handleListItemClick}/>
<Map {...this.state}
handleMarkerClick={this.handleMarkerClick}/>
</div>
);
}
}
export default App;
and my Map.js - i may also need to do it at line 10, defaultCenter=...
/* global google */
import React, { Component } from 'react';
import { withScriptjs, withGoogleMap, GoogleMap, Marker, InfoWindow } from 'react-google-maps';
const MyMapComponent = withScriptjs(
withGoogleMap(props => (
<GoogleMap
defaultZoom={8}
zoom={props.zoom}
defaultCenter={{ lat: -36.186, lng: -87.066 }}
// defaultCenter={
// }
center={{
lat: parseFloat(props.center.lat),
lng: parseFloat(props.center.lng)
}}
>
{props.markers &&
props.markers.filter(marker => marker.isVisible).map((marker, idx, arr) => {
const venueInfo = props.venues.find(venue => venue.id === marker.id);
return (
<Marker
key={idx}
position={{ lat: marker.lat, lng: marker.lng }}
onClick={() => props.handleMarkerClick(marker)}
animation={arr.length === 1
? google.maps.Animation.BOUNCE
: google.maps.Animation.DROP}
>
{marker.isOpen &&
venueInfo.bestPhoto && (
<InfoWindow>
<React.Fragment>
<img src={`${venueInfo.bestPhoto.prefix}300x300${venueInfo.bestPhoto.suffix}`} alt={venueInfo.name} />
<p>{venueInfo.name}</p>
</React.Fragment>
</InfoWindow>
)}
</Marker>
);
})}
</GoogleMap>
))
);
export default class Map extends Component {
render() {
return (
<MyMapComponent
{...this.props}
isMarkerShown
googleMapURL="https://maps.googleapis.com/maps/api/js?key=API_REMOVED"
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%`, width: `65%` }} />}
mapElement={<div style={{ height: `100%`}} />}
/>
);
}
}
thanks!
Use the browsers geolocation.
There is an example in the docs.
In terms of React, you would set locations to state, (add a field), pass them to the Map component via prop.
Something like this
class Anything extends Component{
state = {
location : ''
} //no need for constructor no more, these are called class fields.
getPosition= ()=> {
console.log(navigator.gelocation)
//look at example in the docs and then
this.setState(response from navigator)
}
render(){
return (
<Map {...this.state}> // as you are spreading you are good here, access to
// geolocation via this.props.location in map
// component
)
}
}
https://developers.google.com/maps/documentation/javascript/geolocation