Leaflet plugin only working when geolocation is enabled - javascript

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.

Related

Google Maps React Wrapper - Marker Cluster create #googlemaps/react-wrapper

I am using the Google ReactJS library to add Maps to my React web app and the #googlemaps/react-wrapper library to cluster markers. But I am not able to make marker clustering on the wrapper. Please, if anyone has any idea, help to solve the problem.
The component code is present below:
import React, { useEffect, useRef } from "react";
import { Wrapper, Status } from "#googlemaps/react-wrapper";
export default function MapContainer({ center, zoomLevel, markerList, icon }) {
const googleMapRef = useRef(null);
let googleMap = null;
useEffect(() => {
try {
let bounds = new window.google.maps.LatLngBounds();
const initGoogleMap = () => {
return new window.google.maps.Map(googleMapRef.current, {
center: center,
zoom: zoomLevel,
styles: [{ stylers: [{ saturation: -100 }] }]
});
}
const createMarker = (markerObj) => new window.google.maps.Marker({
position: { lat: parseFloat(markerObj.lat), lng: parseFloat(markerObj.lng) },
map: googleMap,
icon: {
url: icon,
scaledSize: new window.google.maps.Size(80, 80)
},
});
googleMap = initGoogleMap();
markerList.map(x => {
const marker = createMarker(x);
bounds.extend(marker.position);
});
if (markerList.length === 0) {
initGoogleMap();
bounds = new window.google.maps.LatLngBounds();
}
googleMap.fitBounds(bounds);
}
catch (e) {
console.log("maps", e)
}
})
const render = (status) => {
if (status === Status.LOADING) return "Loading...";
if (status === Status.FAILURE) return "Error";
return null;
};
return (
<div>
<div>
<Wrapper apiKey={TOKEN} render={render}>
{/* <GISTrackingMap zoomLevel={props.zoomLevel} mapFilterByVal={props.mapFilterByVal} center={props.center} gisTrackingData={props.gisTrackingData} /> */}
<div ref={googleMapRef} style={{ width: "100%", height: "78vh" }} />
</Wrapper>
</div>
</div>
)
}
To create a clusterer you have to have an array of markers. So I would suggest moving bounds.extend(marker.position); out of the markerList mapping and save the result:
const markers = markerList.map(x => {
createMarker(x);
});
And then just create a clusterer:
new MarkerClusterer({ googleMapRef, markers });
UPD
Looks like the MarkerClusterer doesn't work with ref, so the code above should be changed as follows:
new MarkerClusterer({ googleMap, markers });
Don't forget to install the library and add the import:
yarn add #googlemaps/markerclusterer
import { MarkerClusterer } from '#googlemaps/markerclusterer';

Info Window not showing on marker click in react-google-maps

I'm trying to get the info window of the specific markers to open when the marker is clicked. The map and markers are all appearing correctly and when I hover over a marker it shows the correct title however when I click a marker nothing happens. It's definitely recognizing that the marker was clicked as it logs
the message in the console but it just doesn't show the info window. Any help would be greatly appreciated as I've been trying to solve this problem all day.
Thanks
Map.js
import React, { Component } from "react";
import { Map, GoogleApiWrapper, Marker, InfoWindow } from "google-maps-react";
import axios from "axios";
import shortid from "shortid";
class MyMapComponent extends Component {
constructor(props) {
super(props);
this.state = {
fields: [],
location: {
lat: 51.5074,
lng: 0.1278,
},
showingInfoWindow: false,
activeMarker: {},
selectedPlace: {},
taxis: [
{
companyName: "",
address1: "",
address2: "",
town: "",
postcode: "",
phoneNumber: "",
email: "",
coords: {
lat: "",
lng: "",
},
distance: "",
},
],
};
}
async componentDidMount() {
const { lat, lng } = await this.getcurrentLocation();
this.setState((prev) => ({
fields: {
...prev.fields,
location: {
lat,
lng,
},
},
currentLocation: {
lat,
lng,
},
}));
}
getcurrentLocation() {
this.getNearbyTaxis();
if (navigator && navigator.geolocation) {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition((pos) => {
const coords = pos.coords;
resolve({
lat: 51.5074,
lng: 0.1278,
/*
lat: coords.latitude,
lng: coords.longitude,
*/
});
console.log(coords.latitude, coords.longitude);
this.setState({
location: {
lat: 51.5074,
lng: 0.1278,
},
});
});
});
}
return {
lat: 0,
lng: 0,
};
}
addMarker = (location, map) => {
this.setState((prev) => ({
fields: {
...prev.fields,
location,
},
}));
map.panTo(location);
};
getNearbyTaxis = () => {
axios
.get(
`https://api.tfl.gov.uk/Cabwise/search?lat=${this.state.location.lat}&lon=${this.state.location.lng}`
)
.then((res) => {
res.data.Operators.OperatorList.map((taxi) => {
this.setState({
taxis: this.state.taxis.concat({
companyName: taxi.TradingName,
address1: taxi.AddressLine1,
address2: taxi.AddressLine2,
town: taxi.Town,
postcode: taxi.Postcode,
phoneNumber: taxi.BookingsPhoneNumber,
email: taxi.BookingsEmail,
coords: {
lat: taxi.Latitude,
lng: taxi.Longitude,
},
distance: taxi.Distance,
}),
});
});
})
.then(console.log(this.state.taxis));
};
handleToggle = () => {
console.log("marker clicked");
this.setState({
isOpen: true,
});
console.log(this.state.isOpen);
};
render() {
return (
<>
<button onClick={this.getNearbyTaxis}>Get Taxis</button>
<Map
google={this.props.google}
style={{
width: "100%",
height: "100%",
}}
initialCenter={this.state.fields.location}
center={this.state.fields.location}
zoom={14}
//onClick={this.onMapClicked}
key={shortid.generate()}
>
{this.state.taxis.length &&
this.state.taxis.map((taxi) => {
return (
<Marker
title={taxi.companyName}
name={taxi.companyName}
position={taxi.coords}
onClick={this.handleToggle} // marker ID is the key here.
key={shortid.generate()}
>
<InfoWindow
key={shortid.generate()}
visible={this.state.isOpen}
onCloseClick={this.handleToggle}
>
<div key={shortid.generate()}>
<h1 key={shortid.generate()}> hji </h1>
</div>
</InfoWindow>
</Marker>
);
})}
<Marker position={this.state.fields.location} />
</Map>
</>
);
}
}
export default GoogleApiWrapper({
apiKey: "MY_API_KEY",
})(MyMapComponent);
TaxiList.js
import React, { useState } from "react";
import { Map, GoogleApiWrapper, InfoWindow, Marker } from "google-maps-react";
import MyMapComponent from "./Map";
function TaxiList(props) {
return (
<div>
<MyMapComponent />
</div>
);
}
export default TaxiList;
In the class constructor the initial assignment of this.state = {} is missing property isOpen.
Since you pass that boolean to InfoWindow, is the component's visible prop supposed to do what you think it does? If yes, then the code should kind of work now. If no, you might want to change your code.
Aditionally, every Marker will use the same variable, which means that every related InfoWindow will show up. To solve this you might want to make use of activeMarker you have lying around there in the initial assignment of this.state = {}.
Edit: The InfoWindow won't show because it's nested. Either change Marker component, or move it outside. Documentation here: https://reactjs.org/docs/render-props.html

React Native Navigation v1 crashing in statTabBasedApp

When I navigate my app in startTabBasedApp (My 3 tabs) 1st screen to Render is the EventMap.js this is where my App crash. I just do not know why it crashing I tried to put console.log in all part of my codes but no error appeared.
So the main problem here is in the EventMap.js, because when I tried to remove the EventMap.js in my startTabBasedApp (Main Tabs) then uninstall the app, run react-native run-android, open the App navigate the Tabs (2) it works fine.
What I'm trying to do in my App is when user open the it and navigate it to the EventMap.js I want to get user's location instantly, just like in the Grab App.
How can I achieve this without crashing the App?
Here are my codes for EventMap.js
class EventMap extends Component {
constructor(props) {
super(props);
this.state = {
focusedLocation: {
latitude: 0,
longitude: 0,
latitudeDelta: 0.01,
longitudeDelta: Dimensions.get('window').width / Dimensions.get('window').height * 0.01
},
locationChosen: false,
markerPosition: {
latitude: 0,
longitude: 0
}
}
}
componentDidMount() {
this.didMountGetUserLocation();
};
//This is getting the location and exactly/automatically when they open
didMountGetUserLocation = () => {
navigator.geolocation.getCurrentPosition(pos => {
var lat = parseFloat(pos.coords.latitude)
var long = parseFloat(pos.coords.longitude)
var initialRegion = {
latitude: lat,
longitude: long,
latitudeDelta: 0.01,
longitudeDelta: Dimensions.get('window').width / Dimensions.get('window').height *0.01
};
this.setState({focusedLocation: initialRegion})
this.setState({locationChosen: true})
this.setState({markerPosition: initialRegion})
},
err => {
console.log(err);
});
};
pickLocationHandler = (event) => {
const coords = event.nativeEvent.coordinate;
//For animation of map
this.map.animateToRegion({
...this.state.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
});
this.setState(prevState => {
return {
focusedLocation: {
...prevState.focusedLocation,
latitude: coords.latitude,
longitude: coords.longitude
},
locationChosen: true
};
});
};
getLocationHandler = () => {
navigator.geolocation.getCurrentPosition(pos => {
const coordsEvent = {
nativeEvent: {
coordinate: {
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
}
}
};
this.pickLocationHandler(coordsEvent);
},
err => {
console.log(err);
alert("Fetching failed");
})
};
render() {
let marker = null;
if(this.state.locationChosen) {
marker = <MapView.Marker coordinate={this.state.markerPosition}/>
}
return(
<View style={{zIndex: -1}}>
<TouchableOpacity onPress={this.getLocationHandler} style={styles.iconContainer}>
<Icon name="md-locate" size={30} color="blue"/>
</TouchableOpacity>
<MapView
style={styles.map}
initialRegion={this.state.focusedLocation}
onPress={this.pickLocationHandler}
showsUserLocation={true}
ref={ref => this.map = ref} //For animating map movement
>
{marker}
</MapView>
</View>
);
}
}

React move marker without re-rendering map in react-google-maps

I am using react-google-maps to display a marker from lat/long data. This works fine but the entire component is rendering every time the marker data is updated. Is there a way to only update the marker? My code is below. The data comes in fast so I only let it update every 10 seconds. I am updating the marker and center at the same time.
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as actions from '../actions';
import { compose, withProps } from 'recompose';
import { withScriptjs, withGoogleMap, GoogleMap, Marker } from 'react-google-maps';
import globalStyles from '../App.css';
import styles from '../styles/ViewMap.module.scss';
class ViewMap extends Component {
constructor() {
super();
this.state = {
lat: 0,
long: 0,
updateMap: true
};
}
shouldComponentUpdate(nextProps, nextState) {
if(this.state.updateMap){
return true;
}
else {
return false;
}
}
componentWillUpdate() {
if (this.state.updateMap) {
for (var key in this.props.vehicleAggregateData.vehiclesData) {
if (key === this.props.vin) {
let latitude = this.props.vehicleAggregateData.vehiclesData[key].position[0];
let longitude = this.props.vehicleAggregateData.vehiclesData[key].position[1];
var self = this;
window.setTimeout(function(){
self.setState({ updateMap: true, lat: latitude, long: longitude });
}, 10000);
self.setState({ updateMap: false});
}
}
}
else{
return false;
}
}
render() {
const MyMapComponent = compose(
withProps({
googleMapURL:
'https://maps.googleapis.com/maps/api/js?key=AIzaSyBitA9VEk3r2trmEW-xXTh_8YDpcIfBwy4&v=3.exp&libraries=geometry,drawing,places',
loadingElement: <div className={globalStyles.paperCard} />,
containerElement: <div style={{ height: '100%' }} />,
mapElement: <div style={{ height: '100%' }} />
// key:"AIzaSyBitA9VEk3r2trmEW-xXTh_8YDpcIfBwy4"
}),
withScriptjs,
withGoogleMap
)(props => (
<GoogleMap defaultZoom={18} defaultCenter={{ lat: parseFloat(this.state.lat), lng: parseFloat(this.state.long) }}>
{props.isMarkerShown && <Marker position={{ lat: parseFloat(this.state.lat), lng: parseFloat(this.state.long) }} />}
</GoogleMap>
));
return (
<div className={styles.map + " " + globalStyles.cardShadow}>
<MyMapComponent isMarkerShown />
</div>
);
}
}
function mapStateToProps(state) {
return {
vehicleAggregateData: state.vehicleData
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch),
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ViewMap);

Google is not defined using react google maps?

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,

Categories