Is google clustering possible with this library?
I want to know whether goole clustering is possible with this library
+1
Use this: https://www.npmjs.com/package/node-js-marker-clusterer Works for me.
import MarkerClusterer from 'node-js-marker-clusterer';
const mc = new MarkerClusterer(
this.map, // from new google.maps.Map();
markers, // from const markers = locations.map()
{
styles: [{
width: 40,
height: 40,
url: '/assets/icon-markercluster.png',
textColor: 'white',
}],
},
);
@joriscolours do you have a sample?
@scyrizales I used it here: https://www.vanlanschot.nl/contact/kantoren
@joriscolours Hi. I have a question. Could you please show us a code snippet, how did you use this Cluster with this plugin. Thank you.
Something like this.
The 'parent' component:
/**
* This component requires the 3rd party HOC to add the <script> tag dynamically and pass the google (google.maps) object to the subcomponent.
*/
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { GoogleApiWrapper } from 'google-maps-react';
import GoogleMap from 'Feature/Locations/code/Patterns/Molecules/GoogleMap';
class GoogleMapContainer extends Component {
render() {
return (
<GoogleMap
google={this.props.google}
{...this.props}
/>
);
}
}
GoogleMapContainer.propTypes = {
google: PropTypes.objectOf(PropTypes.any),
settings: PropTypes.objectOf(PropTypes.any),
};
GoogleMapContainer.defaultProps = {
google: null,
settings: null,
};
export default GoogleApiWrapper(props => ({
apiKey: props.settings.apiKey,
}
))(GoogleMapContainer);
The child component, <GoogleMap>.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import MarkerClusterer from 'node-js-marker-clusterer';
class GoogleMap extends Component {
componentDidMount() {
this.loadMap();
}
componentDidUpdate(prevProps) {
if (prevProps.google !== this.props.google || prevProps.locations !== this.props.locations) {
this.loadMap();
}
}
loadMap() {
if (this.props && this.props.google) {
const { google } = this.props;
const node = this.mapRef;
const mapConfig = Object.assign({}, {
center: { lat: this.props.settings.defaultLatitude, lng: this.props.settings.defaultLongitude },
zoom: this.props.settings.zoom,
mapTypeControl: false,
streetViewControl: false,
gestureHandling: 'cooperative',
});
this.map = new google.maps.Map(node, mapConfig);
const infowindow = new google.maps.InfoWindow({
content: this.props.labels.loading,
});
const markers = this.props.locations.map((location) => {
const marker = new google.maps.Marker({
position: { lat: location.lat, lng: location.lng },
map: this.map,
content: `<div class="c-maps__callout">
...
</div>`,
icon: '/static/assets/icon-mapmarker.png',
});
if (location.isOpen) {
setTimeout(() => {
infowindow.setContent(marker.content);
infowindow.open(this.map, marker);
}, 1000);
}
google.maps.event.addListener(marker, 'click', () => {
infowindow.setContent(marker.content);
infowindow.open(this.map, marker);
});
return marker;
});
return new MarkerClusterer(
this.map,
markers,
{
styles: [{
width: 40,
height: 40,
url: '/static/assets/icon-markercluster1.png',
textColor: 'white',
}],
},
);
}
return {};
}
render() {
return (
<div
className="c-maps"
ref={(e) => {
this.mapRef = e;
}}
/>
);
}
}
By the way @joriscolours, I was able to find it from your page, just a little unminifying action :D To be clear with anyone, in order to use MarkerClusterer, you need to stop using the Marker component exposed by this google-maps-react
I found a simple solution to the clustering. First, I'm using the google markerclusterer.js file you can download from google here: https://developers.google.com/maps/documentation/javascript/marker-clustering
Then, I observed that the Maps component passes props to child components, including maps, and google. Both of these are needed to create the clusters. From there, I just created a "MarkerCluster" component. Full source is here
import React, {useEffect} from 'react'
import PropTypes from 'prop-types'
import MarkerClusterer from './markerclusterer'
const evtNames = [
'click',
'dblclick',
'dragend',
'mousedown',
'mouseout',
'mouseover',
'mouseup',
'recenter',
]
const markerCluster = (props) => {
const {map, google, markers} = props
const handleEvent = ({event, marker, entry}) => {
if (props[event]) {
props[event]({
props: props,
marker: marker,
event: event,
entry: entry
})
}
}
// This hook works like ComponentWillMount
// The hook isn't really needed, this whole thing worked without it,
// I added the hook so that I could implement a cleanup function
useEffect(() => {
if (map && markers) {
const mapMarkers = markers.map((marker) => {
const entry = new google.maps.Marker({
position: {
lat: marker.position.lat,
lng: marker.position.lng
},
map: map,
name: marker.name
})
evtNames.forEach(e => {
entry.addListener(e, () => handleEvent({
event: e,
marker: marker,
entry: entry
}))
})
return entry
})
const clusterer = new MarkerClusterer(map, mapMarkers, {imagePath: '/static/images/maps/m'})
// Cleanup function. Note, this is only returned if we create the markers
return () => {
//console.log('Cleaning up markers')
clusterer.clearMarkers()
}
}
}, [map, google, markers])
// Do we need to render anything??
return (null)
}
markerCluster.propTypes = {
map: PropTypes.object,
google: PropTypes.object,
markers: PropTypes.arrayOf(PropTypes.shape({
position: PropTypes.shape({
lat: PropTypes.number.isRequired,
lng: PropTypes.number.isRequired,
}).isRequired,
name: PropTypes.string.isRequired,
})),
}
export default markerCluster
Then in your map implemtation, something like this:
<Map google={google}>
<MarkerCluster
markers={markers}
click={this.onMarkerClick}
mouseover={this.onMouseOver}
mouseout={this.onMouseOut}
/>
</Map>
I used the same technique found in the Marker component to map handlers to the markers that are created, and I pass enough information back so handlers can do what is needed. I probably pass too much... but, it works.
This could probably be added to the library pretty easily ;-)
@kutenai nice. thanks for the solution.
@kutenai i tried the same but I am getting an error
Uncaught TypeError: _markerclusterer__WEBPACK_IMPORTED_MODULE_2___default.a is not a constructor at markercluster.js:57
Hi @kutenai,
The link for the markerclusterer.js from https://developers.google.com/maps/documentation/javascript/marker-clustering isn't available at this point in time. I have used this file instead Archive which I assume is the same file you were talking about. However, I am getting an error that tells me that in the markerclusterer.js file that google is undefined. Is it because the file I have found is not the same as the one you have used? or is there an underlying problem elsewhere?
You need to instantiate the google map. You can't do this from your compiled code, since node does not expose global variables, so I add the logic to my template. Here is my initialization code
<script type="text/javascript">
function createMap(location) {
var mapOptions = {
center: location['center'],
zoom: location.zoom_level
};
return new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
}
function initMap() {
var location = {{ location|safe }};
var map = createMap(location);
if ('polymap'.indexOf(location['type']) !== -1) {
createPolyMap(map, location);
} else {
createCircleMap(map, location);
}
}
function createPolyMap(map, options) {
var polyPath = new google.maps.Polygon({
paths: options['paths'],
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35
});
polyPath.setMap(map);
return polyPath;
}
function createCircleMap(map, options) {
var radius = options['radius'];
return new google.maps.Circle({
strokeColor: '#FF0000',
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: '#FF0000',
fillOpacity: 0.35,
center: options['center'],
map: map,
radius: radius
});
}
function createMarker(map, latLong, title, desc) {
return new google.maps.Marker({
position: latLong,
map: map,
title: title,
desc: desc
});
}
</script>
<script async defer src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAP_API_KEY }}&callback=initMap" type="text/javascript"></script>
You will need to secure an API key, which can cost you money, but this is generally a few cents, or a few dollars until you get to high traffic. Hopefully, by then you won't care.
You need to instantiate the google map. You can't do this from your compiled code, since node does not expose global variables, so I add the logic to my template. Here is my initialization code
<script type="text/javascript"> function createMap(location) { var mapOptions = { center: location['center'], zoom: location.zoom_level }; return new google.maps.Map(document.getElementById("map-canvas"), mapOptions); } function initMap() { var location = {{ location|safe }}; var map = createMap(location); if ('polymap'.indexOf(location['type']) !== -1) { createPolyMap(map, location); } else { createCircleMap(map, location); } } function createPolyMap(map, options) { var polyPath = new google.maps.Polygon({ paths: options['paths'], strokeColor: '#FF0000', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#FF0000', fillOpacity: 0.35 }); polyPath.setMap(map); return polyPath; } function createCircleMap(map, options) { var radius = options['radius']; return new google.maps.Circle({ strokeColor: '#FF0000', strokeOpacity: 0.8, strokeWeight: 2, fillColor: '#FF0000', fillOpacity: 0.35, center: options['center'], map: map, radius: radius }); } function createMarker(map, latLong, title, desc) { return new google.maps.Marker({ position: latLong, map: map, title: title, desc: desc }); } </script> <script async defer src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAP_API_KEY }}&callback=initMap" type="text/javascript"></script>You will need to secure an API key, which can cost you money, but this is generally a few cents, or a few dollars until you get to high traffic. Hopefully, by then you won't care.
Hi @kutenai , may I know where did you put the initialization code? Where is this "template" located? Thank you!
Is it possible to cluster with Marker component?
Hi @kutenai I implemented google-maps-react and its working fine but I need to implement clustering please let me know what is the best way to implement it. Thanks
@kutenai Is it possible to cluster with Marker component?