Search code examples
javascriptgoogle-maps-api-3markerclusterer

Google Maps Store Locator Library with MarkerClusterer


I'm using the Google Maps Store Locator library, but I want to be able to implement MarkerClusterer alongside it. However, I haven't been able to figure out how to make them work together.

Code:

(function($, window, document){

    var map = null,
        cluster = null;

    function storeSource() {
        $.extend(this, new storeLocator.StaticDataFeed);
        var that = this;

        $.getJSON('linktoAJAXThatReturnsJSON', function(data) {
            that.setStores(that.parse_(data));
            map.fitBounds(centerMap(data));
        });
    }

    storeSource.prototype.parse_ = function(data) {
        var stores = [];

        data.forEach(function(row){
            var
            position = new google.maps.LatLng(row.lat, row.long),
            locality = row.postcode + ', ' + row.city,

            store = new storeLocator.Store(row.id, position, null, {
                    title   : row.name,
                    address : [row.address, locality, row.country].join('<br>'),
                    phone   : row.phone
                });

            stores.push(store);
        });

        return stores;
    };

    // Clusters Markers together
    function makeCluster(data) {
        var markers = [];

        data.forEach(function(row){
            markers.push(row.getMarker());
        });

        cluster = new MarkerClusterer(map, markers, {});
    }

    // Finds viewpoint that accomodates all locations
    function centerMap(data) {
        var bounds = new google.maps.LatLngBounds();

        data.forEach(function(row){
            bounds.extend(new google.maps.LatLng(row.lat, row.long));
        });

        return bounds;
    }

    google.maps.event.addDomListener(window, 'load', function() {
        map = new google.maps.Map(document.getElementById('mappanel'), {
            mapTypeId : google.maps.MapTypeId.ROADMAP
        });
        var data = new storeSource();
        var view = new storeLocator.View(map, data, {
            geolocation: false
        });

        new storeLocator.Panel(document.getElementById('searchpanel'), {
            view: view
        });

        // I think this is the place to try and add the Markers
        // from storeSource. However, debugging shows Markers haven't
        // been created yet. This leads me to believe that it's done
        // internally in the storeLocator library. Not sure what to do
        makeCluster(view.data_.stores_);
    });

    $(document).on('click', '.action', function(e){
        e.preventDefault();
    });
})(window.jQuery, window, document);

Unfortuantely, I don't have a live version.

As explained in the code, the MarkerClusterer requires an array of objects of type google.maps.Marker.

My plan is to reuse the storeLocator.Store objects and retrieve the Marker from it. I try to retrieve them, but debugging shows that they are undefined.

Not sure how to make these 2 libraries work together without having to hack either of them.

EDIT::SOME PROGRESS MADE

I was able to store the Markers the StoreLocator uses into MarkerClusterer by overriding storeLocator.View.createMarker function. However, this leads to another problem: the visibility of the markers are controlled by 2 different libraries: MarkerClusterer wants to hide the Markers when zoomed out, but storeLocator displays all Markers all the time.

Is there a way to make the Markers follow MarkerClusterer's default behavior?

I also have a JFiddle.

EDIT #2::SOLUTION

Huge thanks to P1s4 for the solution! Here is the revised JFiddle.


Solution

  • I'm working on ClusterMarker + Storelocator.

    Here my panel.js.

    Now it's all working: clustermarkers, infowindows, zoom on click in the panel, filter with features both in cluster and panel. Hope this is useful.

         google.maps.event.addDomListener(window, 'load', function () {
        var map =  new google.maps.Map(document.getElementById('map-canvas'), {
            center :  new google.maps.LatLng(43.779982, 11.242564),
            zoom : 4,
            mapTypeId : google.maps.MapTypeId.ROADMAP
        });
        var panelDiv = document.getElementById('panel');
        var data =  new DataSource;
        var view =  new storeLocator.View(map, data, {
            geolocation : true,
            features : data.getFeatures()
        });
    
         // create the markers for storelocator and cluster at same time
        // opacity of storelocator marker 0 and cluster 1. This way you see only 
        // the markers of cluster. setClickable false on marker of cluster 
       // and you click markers of storelocator. This way you use your panel click
       // and storelocator inwfowindow. 
    
        var clusterMarkers = [];
        view.createMarker = function (store) {
            var markerOptions = {
                position : store.getLocation(),
                icon : store.getDetails().icon,
                Opacity : 0,
                title : store.getDetails().title,
                Filter : store.getDetails().filter
            }
            marker =  new google.maps.Marker(markerOptions);
            markercluster =  new google.maps.Marker(markerOptions);
            markercluster.setOpacity(1);
            markercluster.setClickable(false);
            clusters.addMarker(markercluster);
            clusterMarkers.push(markercluster);
            return marker;
        }
    
    //I set maxzoom at 17 and when i open infowindow at 18. This way i have
    //infowindow on the marker and not inside cluster
        clusters =  new MarkerClusterer(map, [], {
            maxZoom : 17
        });
    
    // modded infowindow for storelocator
        var infowindow =  new google.maps.InfoWindow;
        view.getInfoWindow = function (store) {
            if (!store) {
                return infowindow;
            }
            var details = store.getDetails();
            var html = ['<div class="store"><div class="title">', details.title, '</div><div class="address">', details.address, '</div>', '<div class="hours misc">', details.phone, '</div></div>'].join('');
             infowindow.setContent($(html)[0]);
            if (map.getZoom() < "18") map.setZoom(18);
            map.panTo(store.getLocation());
            return infowindow;
        };
    
    // i close infowindow on zoom out.
        google.maps.event.addListener(map, 'zoom_changed', function() {
            infowindow.close();
        });
    
         new storeLocator.Panel(panelDiv, {
            view : view,
            featureFilter : true
        });
    
    // i use features modded with radio button on storelocator. I have a filter based on number (1,2,3,4) and i added features "all" (0) to all stores.
        var features = view.getFeatures().asList();
        $('<div id="filter-radio" />').appendTo('.storelocator-filter');
        $.each(features, function (i, o) {
            list = $('<input type="radio" class="filter" name="filter" value="' + i + '" id="filter' + (o.getDisplayName()) + '"/><label for="filter' + (o.getDisplayName()) + '">' + (o.getDisplayName()) + '</label>').appendTo('#filter-radio').change(function () {
                view.set('featureFilter',  new storeLocator.FeatureSet(features[this.value]));
                view.refreshView();
    
      // call toggle to change the markers on the cluster too
                toggle(this.value);
            });
        }); 
    
      //in mobile view i prefer use a select for features filter
    
        var features_mobile = view.getFeatures().asList();
            $('<div id="filter-select" />').prependTo('#panel');
             list = $('<select class="filter-select"/>')
            .appendTo('#filter-select').
            change(function () {
                view.set('featureFilter',
                new storeLocator.FeatureSet(features[this.selectedIndex]));
                view.refreshView();
                toggle(this.selectedIndex);
            });
        $.each(features, function (i, o) {
    
            list.append(new Option(o.getDisplayName()));
    
        });
        // toggle function for markercluster. I filter markers inside the
        // cluster array with the same value as storelocator
        function toggle(filterc) {
            var markers = [];
            for (var i = 0; i < clusterMarkers.length; i++) {
                if (filterc == '0') {
                    markers.push(clusterMarkers[i]);
                    clusterMarkers[i].setVisible(true);
                }
                else if (clusterMarkers[i].Filter == filterc) {
                    markers.push(clusterMarkers[i]);
                    clusterMarkers[i].setVisible(true);
                }
            }
            if (markers.length) {
                clusters.removeMarkers(clusterMarkers);
                clusters.addMarkers(markers);
            }
        };
    
    
    });