Git Product home page Git Product logo

Comments (16)

kutenai avatar kutenai commented on May 17, 2024 29

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 ;-)

from google-maps-react.

 avatar commented on May 17, 2024 1

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;
        }}
      />
    );
  }
}

from google-maps-react.

kutenai avatar kutenai commented on May 17, 2024 1

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.

from google-maps-react.

artemis-prime avatar artemis-prime commented on May 17, 2024

+1

from google-maps-react.

 avatar commented on May 17, 2024

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',
          }],
        },
      );

from google-maps-react.

scyrizales avatar scyrizales commented on May 17, 2024

@joriscolours do you have a sample?

from google-maps-react.

 avatar commented on May 17, 2024

@scyrizales I used it here: https://www.vanlanschot.nl/contact/kantoren

from google-maps-react.

wamujlb avatar wamujlb commented on May 17, 2024

@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.

from google-maps-react.

scyrizales avatar scyrizales commented on May 17, 2024

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

from google-maps-react.

iakashpatel avatar iakashpatel commented on May 17, 2024

@kutenai nice. thanks for the solution.

from google-maps-react.

kartik4all avatar kartik4all commented on May 17, 2024

@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

from google-maps-react.

ChrisnNg avatar ChrisnNg commented on May 17, 2024

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?

from google-maps-react.

fjregencia avatar fjregencia commented on May 17, 2024

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!

from google-maps-react.

michaelHottomali avatar michaelHottomali commented on May 17, 2024

Is it possible to cluster with Marker component?

from google-maps-react.

loveheenavasu avatar loveheenavasu commented on May 17, 2024

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

from google-maps-react.

loveheenavasu avatar loveheenavasu commented on May 17, 2024

@kutenai Is it possible to cluster with Marker component?

from google-maps-react.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.