google map react, from onGoogleApiLoaded apiIsLoaded doesn't called - reactjs

Function apiIsLoaded doesn't get called, and map can't be initialized. What's wrong? When inspecting, I can see a div without a google map iframe, just empty space.
I need to apiIsLoaded be called, but cant find any way to do this. Here is the code, any help would be appreciated.
import React from 'react';
import GoogleMapReact from "google-map-react";
const LocationMap = (props) => {
const apiIsLoaded = (map, maps) => {
if (map) {
map.setOptions({ gestureHandling: 'greedy', mapTypeControl: false, minZoom: 2});
}
};
const { address } = props;
const center = address ? { lat: address.lat, lng: address.lon } : { lat: 0, lng: 0 };
return (
<div className="google-maps-wrapper">
<GoogleMapReact
bootstrapURLKeys={{
key: process.env.REACT_APP_MAP_KEY
}}
defaultZoom={address ? 8 : 2}
center={[center.lat, center.lng]}
defaultCenter={[0, 0]}
yesIWantToUseGoogleMapApiInternals={true}
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps)}>
</GoogleMapReact>
</div>
);
};
export default LocationMap;

check your method is calld by GoogleMapReact or not
const apiIsLoaded = (map, maps) => {
console.log('apiIsLoaded')
if (map) {
map.setOptions({ gestureHandling: 'greedy', mapTypeControl: false, minZoom: 2});
}
};
Second thing is you don't need to pass yes to
yesIWantToUseGoogleMapApiInternals={true}
change it to
yesIWantToUseGoogleMapApiInternals

Related

Centralize google map issue

import React, {
useState
} from "react";
import GoogleMapReact from "google-map-react";
function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin[1], pin[0]));
});
return bounds;
};
return ( <
div >
<
div style = {
{
height: "100vh",
width: "100%"
}
} >
<
GoogleMapReact bootstrapURLKeys = {
{
key: AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk
}
}
defaultCenter = {
tallinn.center
}
defaultZoom = {
tallinn.zoom
}
onGoogleApiLoaded = {
({
map,
maps
}) => apiIsLoaded(map, maps, latLgn)
}
yesIWantToUseGoogleMapApiInternals >
{
latLgn.map((item, index) => ( <
div lat = {
item[1]
}
lng = {
item[0]
}
key = {
index
} >
</div>
))
} </GoogleMapReact>
</div>
</div>
);
}
export default Gmap
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have a problem with centralizing the map I am rendering.
although I have a state with the "lat" and "lng" as stated in the docs still, when I run the app with npm start or refreshing the page, it will centre itself somewhere in the ocean.\
PS. I will paste my "map" component only.
import React, { useState } from "react";
import GoogleMapReact from "google-map-react";
export default function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin[1], pin[0]));
});
return bounds;
};
return (
<div>
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{key: AIzaSyCkUOdZ5y7hMm0yrcCQoCvLwzdM6M8s5qk }}
defaultCenter={tallinn.center}
defaultZoom={tallinn.zoom}
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps, latLgn)}
yesIWantToUseGoogleMapApiInternals
>
{latLgn.map((item, index) => (
<div lat={item[1]} lng={item[0]} key={index}< </div>
))}
</GoogleMapReact>
</div>
</div>
);
}
ps. let me know if I should provide more information
UPDATE
I have created a sample project here.
I checked your code and I noticed a couple of things.
First, in your getMapBounds function, the bounds returned are null. This is because the values of pin[1] and pin[0] are undefined. This might be the reasoon why the map is centering in the middle of the ocean like this as this is the center of the world and coordinates are 0,0. You must use pin.lat and pin.lng instead so that it will correctly populate the value for bounds.
Second, it seems that you would like to put markers on the coordinates of your latLgn. To achieve this, you can follow the AnyReactComponent function as mentioned in the google-map-react docs instead of putting a div directly inside the latLgn.map.
Lastly, inside your latLgn.map, you must not use item[1] and item[0] as they are also both undefined instead use item.lat and item.lng.
Here's the code snippet for my working code:
import React, { useState } from "react";
import "./style.css"
import GoogleMapReact from "google-map-react";
const AnyReactComponent = ({ text }) => <div>{text}</div>;
export default function GMap() {
const [latLgn, setLatLgn] = useState([{
lng: 24.7536,
lat: 59.437
},
{
lng: 24.7303,
lat: 59.4393
},
{
lng: 24.7387,
lat: 59.4497
},
]);
const [tallinn] = useState({
center: { // where i want to be centerd
lng: 24.7536,
lat: 59.437,
},
zoom: 10,
});
// Fit map to its bounds after the api is loaded
const apiIsLoaded = (map, maps, latlgn) => {
// Get bounds by our latlgn
const bounds = getMapBounds(map, maps, latlgn);
// Fit map to bounds
map.fitBounds(bounds);
// Bind the resize listener
bindResizeListener(map, maps, bounds);
};
// Re-center map when resizing the window
const bindResizeListener = (map, maps, bounds) => {
maps.event.addDomListenerOnce(map, "idle", () => {
maps.event.addDomListener(window, "resize", () => {
map.fitBounds(bounds);
});
});
};
// Return map bounds based on list of places
const getMapBounds = (map, maps, pins) => {
const bounds = new maps.LatLngBounds();
pins.forEach((pin) => {
bounds.extend(new maps.LatLng(pin.lat, pin.lng));
console.log(pin[0])
});
return bounds;
};
return (
<div>
<div style={{ height: "100vh", width: "100%" }}>
<GoogleMapReact
bootstrapURLKeys={{key: "YOUR_API_KEY" }}
defaultCenter={tallinn.center}
defaultZoom={tallinn.zoom}
onGoogleApiLoaded={({ map, maps }) => apiIsLoaded(map, maps, latLgn)}
yesIWantToUseGoogleMapApiInternals
>
{latLgn.map((item, index) => (
<AnyReactComponent lat={item.lat} lng={item.lng} key={index} text={"(" +item.lat + "," + item.lng +")"}> </AnyReactComponent>
))}
</GoogleMapReact>
</div>
</div>
);
}
I also noticed your question in the comment section. You can use online ides like stackblitz to provide an sscce of your code and just remove the API key. You can put a note in your question that you need to put API key to see how the code works. You can refer to my working code above.

How to Draw Polyline on mapmyindia using React?

I am just exploring Mapmyindia.
I have gone through the basic location display on the map.
I am not getting how to display polyline on map.
code for location
app.js
import "./App.css";
import SideBar from "./componants/SideBar";
import Map from "mapmyindia-react";
// import MapmyIndia, { MapMarker } from "react-mapmyindia";
function App() {
return (
<div className="App">
Hello
<Map
markers={[
{
position: [21.1588329, 72.7688111],
draggable: true,
zoom: 15,
title: "Marker title",
onClick: (e) => {
console.log("clicked ");
},
onDragend: (e) => {
console.log("dragged");
},
onMouseover: (e) => {
console.log("Mouse over");
},
},
]}
/>
{/* <Map /> */}
<SideBar></SideBar>
</div>
);
}
export default App;
Which result this
Now, Please help with drawing polyline.
I guess you're using the npm package for maps. If you go through the library's code in gitHub, you can see the owner has only added marker functionality. You can simply copy the code from there and add it manually to your project and then add the polyline functionality and then pass the data as props from your app.js file like you're doing for markers.
renderPolylines = () => {
const { polylines = [] } = this.props;
if (!this.map) {
return;
}
polylines.map(m => {
if (m?.position && Array.isArray(m.position)) {
const { position, color, weight, opacity } = m;
let points = [];
position.map(p => {
const { lat, lng } = p;
const center = new L.LatLng(lat, lng);
points.push(
new L.LatLng(center.lat, center.lng))/*array of wgs points*/
})
const polyline = new L.Polyline(points, { color, weight, opacity });
this.map.addLayer(polyline);
this.polylines.push(polyline);
}
});
};
Props for rendering polyline from app.js
polylines = {[
{
position: [
{
lat: 18.5014,
lng: 73.805,
},
{
lat: 18.5414,
lng: 73.855,
},
{
lat: 18.5514,
lng: 73.855,
},
{
lat: 18.5614,
lng: 73.855,
},
],
color: "red",
weight: 4,
opacity: 0.5,
},
]}

Limit the number of clicks on map using React

I am using the Google Maps API and I want to limit the number of markers on the map (limit: 10). I couldn't find anything related to it in the API docs
neither I can find any similar source to solve my problem.
Here is my code:
import React from "react";
import {
GoogleMap,
useLoadScript,
Marker,
} from "#react-google-maps/api";
const mapContainerStyle = {
height: "50vh",
width: "100vw",
};
const options = {
zoomControl: false,
scrollwheel: false,
draggable: false
};
const center = {
lat: 34.155834,
lng: -119.202789,
};
export default function App() {
const { isLoaded, loadError } = useLoadScript({
googleMapsApiKey: "AIzaSyCpaQDSgGTCetTR0uz42RyV80cByaGaYLs",
});
const [markers, setMarkers] = React.useState([]);
const onMapClick =
React.useCallback((e) => {
setMarkers((current) =>
[
...current,
{
lat: e.latLng.lat(),
lng: e.latLng.lng(),
},
]);
}, []);
const mapRef = React.useRef();
const onMapLoad = React.useCallback((map) => {
mapRef.current = map;
}, []);
if (loadError) return "Error";
if (!isLoaded) return "Loading...";
return (
<div>
<GoogleMap
id="map"
mapContainerStyle={mapContainerStyle}
zoom={14}
center={center}
options={options}
onClick={onMapClick}
onLoad={onMapLoad}
>
{markers.map((marker) => (
<Marker
key={`${marker.lat}-${marker.lng}`}
position={{ lat: marker.lat, lng: marker.lng }}
onClick={() => {
console.log("clicked")
}}
/>
))}
</GoogleMap>
</div>
);
}
How do I set the number of clicks up to 10?
You could do something like this. You may also want to call another function before returning current to perform some other update to alert the user they are maxed out on markers.
const onMapClick = React.useCallback((e) => {
setMarkers((current) => {
if (current.length < 10) {
return [
...current,
{
lat: e.latLng.lat(),
lng: e.latLng.lng()
}
];
} else {
return current;
};
});
}, []);

How to setState() from within a nested function in React?

I'm trying to adapt this example from https://github.com/mapbox/mapbox-react-examples/tree/master/basic,
import React from 'react'
import ReactDOM from 'react-dom'
import mapboxgl from 'mapbox-gl'
mapboxgl.accessToken = 'pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4M29iazA2Z2gycXA4N2pmbDZmangifQ.-g_vE53SD2WrJ6tFX7QHmA';
class Application extends React.Component {
constructor(props: Props) {
super(props);
this.state = {
lng: 5,
lat: 34,
zoom: 1.5
};
}
componentDidMount() {
const { lng, lat, zoom } = this.state;
const map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/streets-v9',
center: [lng, lat],
zoom
});
map.on('move', () => {
const { lng, lat } = map.getCenter();
this.setState({
lng: lng.toFixed(4),
lat: lat.toFixed(4),
zoom: map.getZoom().toFixed(2)
});
});
}
render() {
const { lng, lat, zoom } = this.state;
return (
<div>
<div className="inline-block absolute top left mt12 ml12 bg-darken75 color-white z1 py6 px12 round-full txt-s txt-bold">
<div>{`Longitude: ${lng} Latitude: ${lat} Zoom: ${zoom}`}</div>
</div>
<div ref={el => this.mapContainer = el} className="absolute top right left bottom" />
</div>
);
}
}
ReactDOM.render(<Application />, document.getElementById('app'));
to a case in which, rather than displaying the map's center, I would like to display the latitude and longitude of the mouse position.
So far, I've managed to simply log it to the console:
import React from 'react';
import mapboxgl from 'mapbox-gl';
mapboxgl.accessToken = 'pk.eyJ1Ijoia3VydHBlZWsiLCJhIjoiY2p6cnVneWdvMHlzeDNqcWo0dm83ZzZ2eiJ9.yUCSreTRcKs12uT5PTCztg';
export default class Map extends React.Component {
componentDidMount() {
this.map = new mapboxgl.Map({
container: this.mapContainer,
style: 'mapbox://styles/mapbox/outdoors-v11',
center: [-119.5591, 37.715],
zoom: 9
});
this.map.on('load', function(e) {
e.target.on('mousemove', function(e) {
console.log(JSON.stringify(e.point));
console.log(JSON.stringify(e.lngLat.wrap()));
});
});
}
componentWillUnmount() {
this.map.remove();
}
render() {
const style = {
position: 'absolute',
top: 0,
bottom: 0,
width: '100%'
};
return <div style={style} ref={el => this.mapContainer = el} />;
}
}
This writes lines like the following to the console:
{"x":972,"y":272}
{"lng":-118.90266689452113,"lat":37.86205552587528}
However, rather than logging the coordinates to the console, I would like to invoke this.setState() like in the example so that I can render the coordinates in a child component.
The problem is, within the on('mousemove', ...) callback function, this is not the component. I've read about using arrow functions (which are lexically scoped) to work around this, but it seems to me that in this case, I need a 'normal' function(e) in order to capture the event.
How can I setState() with the mouse coordinates in this example?
It's possible to use arrow functions just like any other function
this.map.on('load', e => {
e.target.on('mousemove', e => {
this.setState({}) //correct this
})
})

Center AND zoom by prop in react-google-maps?

I am building a Zillow-like map with real estate property points in page body, and filters in page header.
What I need is to center AND zoom react-google-maps map, when selecting Google place from external Google places filter. Currently I am able to center map on place select, but not zoom in (that is because i need to refetch data on every zoom in/out, so need to control zoom, see code below).
I tried to use fitBounds from here, but ref in my code is always undefined.
I have API that fetches points, based on filters, with params like this:
viewPort: {
northEast: { lat: 57.04834755670983, lng: 24.3255340332031 },
southWest: { lat: 56.83635114109457, lng: 23.93826596679685 }
},
priceMin: 0,
priceMax: 500,
propType: "RENT",
numRooms: 3,
...
In header section there is also address filter input, that has Google places autocomplete. When selected, Google provides info about place:
"geometry" : {
...
"viewport" : {
"northeast" : {
"lat" : 56.95662478029149,
"lng" : 24.2054917802915
},
"southwest" : {
"lat" : 56.9539268197085,
"lng" : 24.2027938197085
}
}
...
Here is my Map component:
import React, { Component } from 'react';
import { compose, withProps, withState, withHandlers } from "recompose";
import {
withGoogleMap,
GoogleMap,
Marker,
} from "react-google-maps";
import { MarkerClusterer } from "react-google-maps/lib/components/addons/MarkerClusterer";
const defaultMapOptions = {
fullscreenControl: false,
streetViewControl: false,
};
const PropertyMap = compose(
withProps({
loadingElement: <div>Loading...</div>,
containerElement: <div style={{ height: "500px" }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withState('zoom', 'onZoomChange', 11),
withHandlers(() => {
const refs = {
map: undefined,
}
return {
onMapMounted: () => ref => {
refs.map = ref
},
onZoomChanged: ({ onZoomChange, onMapBoundsChange }) => () => {
onZoomChange(refs.map.getZoom())
onMapBoundsChange(refs.map.getBounds());
},
onDragEnd: ({ onMapBoundsChange }) => () => {
onMapBoundsChange(refs.map.getBounds());
},
onMarkerClustererClick: () => (markerClusterer) => {
const clickedMarkers = markerClusterer.getMarkers()
console.log(`Current clicked markers length: ${clickedMarkers.length}`)
console.log(clickedMarkers)
}
}
}),
withGoogleMap
)(props =>
<GoogleMap
defaultCenter={props.defaultCenter ? props.defaultCenter : { lat: 56.9425, lng: 24.1319 }}
zoom={props.zoom}
ref={props.onMapMounted}
onZoomChanged={props.onZoomChanged}
onDragEnd={props.onDragEnd}
defaultOptions={defaultMapOptions}
>
<MarkerClusterer
onClick={props.onMarkerClustererClick}
averageCenter
enableRetinaIcons
gridSize={60}
key={props.mapUid}
>
{props.features !== null && props.features.map(feature => {
const coord = { lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] };
return <Marker key={feature.id} position={coord} />;
})}
</MarkerClusterer>
</GoogleMap>
);
export default PropertyMap;
So I think I would like to make it possible to pass zoom param in props, like this:
<PropertyMap
mapUid={this.state.mapUid}
features={this.state.features}
key={this.state.defaultCenter ? `${this.state.defaultCenter.lat}` : '1'}
defaultCenter={this.state.defaultCenter}
defaultZoom={calculateZoomFromViewPort(this.state.viewPort)}
onMapBoundsChange={this.handleMapBoundsChange}
/>
This way I could calculateZoomFromViewPort(this.state.viewPort), but then I need to somehow set it to withState zoom default value.
So what I need is some how pass default zoom value to withState('zoom', 'onZoomChange', 11), (11 is hardcoded, currently). But I am new to recompose package, but is it heavily used in react-google-maps package examples.
If you have other ideas, how to zoom and center map based on viewport & center params from Google place, please share!
fitbounds() is the recommended way to achieve auto zoom. Remember refs always created after first render of the component. I have made changes in your code by adding fitBounds. There might be few syntax errors as I prefer to use react-google-maps like class based utility wrapper. It's easier to manage with redux and component lifecycles. Below is the latest code:
import React, { Component } from 'react';
import { compose, withProps, withState, withHandlers } from "recompose";
import {
withGoogleMap,
GoogleMap,
Marker,
} from "react-google-maps";
import { MarkerClusterer } from "react-google-maps/lib/components/addons/MarkerClusterer";
const defaultMapOptions = {
fullscreenControl: false,
streetViewControl: false,
};
const PropertyMap = compose(
withProps({
loadingElement: <div>Loading...</div>,
containerElement: <div style={{ height: "500px" }} />,
mapElement: <div style={{ height: `100%` }} />,
}),
withState('zoom', 'onZoomChange', 11),
withHandlers(() => {
const refs = {
map: undefined,
}
return {
onMarkersUpdate: ()=> () => {
if(refs.map){
const mapBounds = new google.maps.LatLngBounds();
//just add markers position into mapBounds
markers.forEach((marker)=>{
mapBounds.extend(marker.position)
});
this.refs.map.fitBounds(mapBbounds);
}
},
onZoomChanged: ({ onZoomChange, onMapBoundsChange }) => () => {
onZoomChange(refs.map.getZoom())
onMapBoundsChange(refs.map.getBounds());
},
onDragEnd: ({ onMapBoundsChange }) => () => {
onMapBoundsChange(refs.map.getBounds());
},
onMarkerClustererClick: () => (markerClusterer) => {
const clickedMarkers = markerClusterer.getMarkers()
console.log(`Current clicked markers length: ${clickedMarkers.length}`)
console.log(clickedMarkers)
}
}
}),
withGoogleMap
)(props =>
<GoogleMap
defaultCenter={props.defaultCenter ? props.defaultCenter : { lat: 56.9425, lng: 24.1319 }}
zoom={props.zoom}
ref='map'
onZoomChanged={props.onZoomChanged}
onDragEnd={props.onDragEnd}
defaultOptions={defaultMapOptions}
>
{props.onMarkersUpdate()}
<MarkerClusterer
onClick={props.onMarkerClustererClick}
averageCenter
enableRetinaIcons
gridSize={60}
key={props.mapUid}
>
{props.features !== null && props.features.map(feature => {
const coord = { lng: feature.geometry.coordinates[0], lat: feature.geometry.coordinates[1] };
return <Marker key={feature.id} position={coord} />;
})}
</MarkerClusterer>
</GoogleMap>
);
export default PropertyMap;
Please feel free to react out me. If you make a codesandbox for this. It will be easier to fix syntax errors. And Don't miss out fitbounds(). It makes things easier for us.

Resources