react-map-gl load map on the location of the user - reactjs

Using react 18 and react-map-gl 7.0.11, im not able to load the map on the location of the user.
I am able to get this button on the screen using the GeoLocateControl component
https://i.stack.imgur.com/fBy1y.png
<Map
{...viewState}
reuseMaps
mapStyle="mapbox://styles/mapbox/streets-v9"
mapboxAccessToken={process.env.REACT_APP_MAPBOX_KEY}
onMove={(evt) => setViewState(evt.viewState)}
>
<GeolocateControl
positionOptions={{
enableHighAccuracy: true,
}}
trackUserLocation={true}
onGeolocate={(pos) => {
setViewState({
...viewState,
longitude: pos.coords.longitude,
latitude: pos.coords.latitude,
});
}}
/>
</Map>
Is there a way, to trigger the button click to move the map and the blue dot to the current location of the user on mount of the component (map), without setting up event listeners? I tried the method of declaring a ref, and using useCallback to trigger the ref, but it did not work.
Any suggestions?

as per https://blog.logrocket.com/using-mapbox-gl-js-react/: you can do something like:
function MyMap() {
const [viewport, setViewport] = useState({});
useEffect(() => {
navigator.geolocation.getCurrentPosition((pos) => {
setViewport({
...viewport,
latitude: pos.coords.latitude,
longitude: pos.coords.longitude,
zoom: 3.5,
});
});
}, []);
return (
<div>
{viewport.latitude && viewport.longitude && (
<div>
<h1>Your Location:</h1>
<Map
mapboxAccessToken="MAPBOX_TOKEN"
initialViewState={viewport}
mapStyle="mapbox://styles/mapbox/streets-v11"
>
<Marker
longitude={viewport.longitude}
latitude={viewport.latitude}
/>
</Map>
</div>
)}
</div>
);
}

Related

google map shows random location as center, even when center is specified

I'm using the package google-maps-react to show gmaps in my react app. Everything works fine, except for the fact that the map initially shows a random location as the center, even when I've specified co-ordinates for the center and initial center. How can I fix this so that the map centers on the co-ordinates I've specified initially?
const MapContainer = React.memo((props) => {
const [map, setMap] = useState();
const [center, setCenter] = useState({ lat: 59.913, lng: 10.752 });
var bounds = new props.google.maps.LatLngBounds();
const setMapObj = (mapProps, map) => {
setMap(map);
};
useDeepCompareEffect(() => {
if (props.markers.length === 1) {
setCenter(props.markers[0]);
}
for (var i = 0; i < props.markers.length; i++) {
bounds.extend(
new props.google.maps.LatLng(props.markers[i].lat, props.markers[i].lng)
);
}
if (map) {
map.fitBounds(bounds);
}
}, [props.markers, map]);
return (
<div className={props.className}>
{/* TODO -> Store api key in .env file */}
{console.log(center)}
<Map
google={props.google}
style={{ borderRadius: "10px" }}
center={center}
initialCenter={center}
onReady={setMapObj}
zoom={4}
bounds={bounds}
>
{props.markers.map((item, index) => {
if (item.lat && item.lng)
return (
<Marker key={index} position={{ lat: item.lat, lng: item.lng }} />
);
})}
</Map>
</div>
);
});
Your code is trying to set a center AND fit a particular bounds. The latter will cause the center to change in order to fit all of the markers in the viewport.

Google Maps fitBounds not centering and showing markers in google-maps-react

I'm using google-maps-react in my react app to use GMaps. This is the code I got:
function MapContainer(props) {
var bounds = new props.google.maps.LatLngBounds();
const [markers, setMarkers] = useState([
{
lat: 55.378,
lng: 3.436
},
{
lat: 37.0902,
lng: 95.712
}
]);
const adjustMap = (mapProps, map) => {
const { google } = mapProps;
markers.forEach(marker => {
bounds.extend(new google.maps.LatLng(marker.lat, marker.lng));
});
map.fitBounds(bounds);
}
return (
<>
<div className={props.className}>
<Map
google={props.google}
style={{ borderRadius: '10px'}}
zoom={7}
onReady={adjustMap}
bounds={bounds}
initialCenter={{ lat: 47.444, lng: -122.176}}
>
{
markers.map((item) => {
return (
<Marker position={{lat: item.lat, lng: item.lng}}/>
)
})
}
</Map>
</div>
<button onClick={(e) => { e.preventDefault(); const newMap = [...markers, {lat: 59.913, lng: 10.752}]; setMarkers(newMap)}}>Add marker</button>
</>
);
}
So initially, the map is centered properly, as it shows the two markers. But when I click on the button to add another marker, it just shows a random area where none of the markers are visible.
I referred to what tidyline suggested in this thread modified it slightly to fix the errors, but that didn't work. - https://github.com/fullstackreact/google-maps-react/issues/63
How can I fix this to show all the markers when other markers are added?
There are two issues going on:
onReady={adjustMap} will only execute once and not after adding more markers
you click handler sets a hard coded location for the marker and the bounds of the map is not updated with this new marker. maybe call adjustMap from the handler?

How can implement react-google-maps DirectionsRenderer in my project

I am building a simple project that if a user clicks a button it should show directions from the user's location to the destination which I mark. Here is my code.
I have just implemented some parts but I did not understand how to implement DirectionsRenderer and could not learn from articles, docs. Now I have no idea what to do. Can anyone explain me in a easier way? Thank you!
GoogleMap,
withGoogleMap,
withScriptjs,
DirectionsRenderer,
} from 'react-google-maps';
import Marker from 'react-google-maps/lib/components/Marker';
function Map() {
return (
<div className='App'>
<GoogleMap
defaultZoom={10}
defaultCenter={{ lat: 51.507351, lng: -0.127758 }}
>
<Marker position={{ lat: 51.4666, lng: -0.213 }} />
</GoogleMap>
</div>
);
}
const DirectionsService = new window.google.maps.DirectionsService();
DirectionsService.route({
origin: new window.google.maps.LatLng(41.85073, -87.65126),
destination: new window.google.maps.LatLng(41.85258, -87.65141),
travelMode: window.google.maps.TravelMode.DRIVING,
});
const WrappedMap = withScriptjs(withGoogleMap(Map));
function App() {
return (
<div style={{ width: '100vw', height: '100vh' }}>
<WrappedMap
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=${process.env.REACT_APP_MAP_KEY}`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `100%` }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
export default App;
Here are the steps I made to achieve your use-case:
Use the browser's HTML5 Geolocation feature to get the user's current position inside the componentDidMount.
Then use state variables to hold the value of the current location. You also put the value of the user current location state to center parameter of your GoogleMap object so that the map will show the area of the map where the user is. This will be used as the origion of your Directions service.
3.Also use another state to hold the user current location. This is to be used as state to put a marker in the user current location.
Call a function inside the onClick parameter of your GoogleMap object. Then use another state variable that will hold the value of the clicked coordinates. This will be used as the destination of the Directions Service.
Put a button on top of your map div and call a function inside its OnClick parameter.The function will call the GoogleMaps directionsService and will calculate a route between your origin and destination. If there's a route, you use another state variable to hold the result of the Directions service.
Use the DirectionsRenderer object inside the GoogleMap object to display the polyline of the route.
Here's the sample code (Note: Make sure to put your API key in the index.js for the sample to work) and the code snippet below:
/*global google*/
import React, { Component } from 'react';
import Button from 'react-bootstrap/Button';
import {
withGoogleMap,
Marker,
GoogleMap,
DirectionsRenderer
} from 'react-google-maps';
class Map extends Component {
state = {
directions: null,
markerPos: null,
centerMarker: null,
currentLocation: null
};
componentDidMount = () => {
navigator?.geolocation.getCurrentPosition(
({ coords: { latitude: lat, longitude: lng } }) => {
const pos = { lat, lng };
this.setState({ currentLocation: pos, centerMarker: pos });
}
);
};
onMapClick = e => {
this.setState({ markerPos: e.latLng });
};
getDirections = () => {
const directionsService = new google.maps.DirectionsService();
const origin = this.state.currentLocation;
const destination = this.state.markerPos;
if (origin !== null && destination !== null) {
directionsService.route(
{
origin: origin,
destination: destination,
travelMode: google.maps.TravelMode.DRIVING
},
(result, status) => {
if (status === google.maps.DirectionsStatus.OK) {
this.setState({
directions: result
});
} else {
console.error(`error fetching directions ${result}`);
}
}
);
} else {
console.log('Please mark your destination in the map first!');
}
};
render() {
const GoogleMapExample = withGoogleMap(props => (
<GoogleMap
defaultCenter={{ lat: 40.756795, lng: -73.954298 }}
defaultZoom={13}
center={this.state.currentLocation}
onClick={this.onMapClick}
>
{this.state.centerMarker !== null && (
<Marker position={this.state.centerMarker} label={'userloc'} />
)}
{this.state.markerPos !== null && (
<Marker position={this.state.markerPos} />
)}
{this.state.directions !== null && (
<DirectionsRenderer
directions={this.state.directions}
defaultOptions={{
suppressMarkers: true
}}
/>
)}
</GoogleMap>
));
return (
<div>
<Button onClick={this.getDirections}>Get Direction</Button>
<GoogleMapExample
containerElement={<div style={{ height: `500px`, width: '500px' }} />}
mapElement={<div style={{ height: `100%` }} />}
/>
</div>
);
}
}
export default Map;

React - hovering over dynamically generated component calls render method

My app contains a google map that displays markers based on coordinates from an API. Upon the loading the app, an API request is made and a few coordinates are grabbed to be displayed on the screen. The API data is mapped to a <Marker /> component.
Problem: Upon hovering over any Marker, the render method gets called again and the Markers quickly flash. Why?
Map.js
state = {
center: { lat, lng },
zoom: defaultZoom,
showingInfoWindow: false,
selectedMarker: ""
};
onMarkerHover = (e, marker) => {
this.setState({
selectedMarker: marker.recordid,
showingInfoWindow: true
});
this.onSendMarkerInfoToParent();
};
render() {
const data = this.props.searchedResponse;
console.log("rerendering"); // printed in console on every hover event
const Markers = props =>
data
? data.data.records.map(marker => {
return (
<Marker
position={{ lat: marker.fields.geom.coordinates[1], lng: marker.fields.geom.coordinates[0] }}
key={marker.recordid}
onClick={e => this.onMarkerClick(e, marker)}
onMouseOver={e => this.onMarkerHover(e, marker)}
>
{this.state.showingInfoWindow && this.state.selectedMarker === marker.recordid && (
<InfoWindow
className="info-window"
position={{ lat: marker.fields.geom.coordinates[1], lng: marker.fields.geom.coordinates[0] }}
>
<div>{rateTimeCalc(marker)}</div>
</InfoWindow>
)}
</Marker>
);
})
: null;
return (
<div className="map-container">
<LoadScript googleMapsApiKey={process.env.REACT_APP_GOOGLE_KEY}>
<GoogleMap id="map" center={this.state.center} zoom={this.state.zoom}>
<Markers />
</GoogleMap>
</LoadScript>
</div>
);
}
App.js
onSendMarkerInfoToParent = e => {
console.log("parent", e);
};
This is because of Lifecycle of React app.
You have mapped the onHover method in your code that set the state.
onMouseOver={e => this.onMarkerHover(e, marker)}
onMarkerHover = (e, marker) => {
this.setState({
selectedMarker: marker.recordid,
showingInfoWindow: true
});
this.onSendMarkerInfoToParent();
};
This setState causes re-render.
In React, whenever you set the state of a component. The render method will be executed automatically.
You can read this article for reference: https://medium.com/react-ecosystem/react-components-lifecycle-ce09239010df.
Solution for google-maps-react and Markers re-rendering:
1.Create Wrapper PureComonent for all Markers.
2.Maintain one InfoWindow for all Markers.
Working example:
https://codesandbox.io/s/falling-wave-kjcee

How can i get the location of the user - Using at React Map GL

I'm using React Map GL, and I want to put the latitude and longitude of the user
Returning the component like this:
export default geolocated({
positionOptions: {
enableHighAccuracy: false
},
userDecisionTimeout: 5000
})(Layout);
I'm tried to use react-geolocated but it's returning null coords.
I want to get these informations when the component mounts, that's the code:
componentDidMount() {
console.log(this.props);
if (navigator.geolocation) {
const viewport = {
...this.state.viewport,
latitude: this.props.coords.latitude,
longitude: this.props.coords.longitude
};
this.setState({ viewport });
}
}
The map component:
<MapGL
{...viewport}
//onClick={this.handleMapClick}
mapStyle="mapbox://styles/mapbox/streets-v9"
mapboxApiAccessToken={TOKEN}
perspectiveEnabled
onViewportChange={viewport => this.setState({ viewport })}
>
<div className="geo" style={geolocateStyle}>
{" "}
<GeolocateControl
positionOptions={{ enableHighAccuracy: true }}
trackUserLocation={true}
/>
</div>
<div className="nav" style={navStyle}>
<NavigationControl
onViewportChange={viewport => this.setState({ viewport })}
/>
</div>
</MapGL>
I'm getting the props like this:
{coords: null, isGeolocationAvailable: true, isGeolocationEnabled: true, positionError: null}
I don't know how to achieve this using the Geolocate control...
but an alternative would be to just use the user's device location & plain javascript to fetch the current location...
I.E. Something like this:
useEffect(() => {
navigator.geolocation.getCurrentPosition(pos => {
setViewport({
...viewport,
latitude: pos.coords.latitude,
longitude: pos.coords.longitude
});
});
}, []);
Hope this helps someone!

Resources