Russell
Russell

Reputation: 184

Combining Marker Clusterer and google maps API within Angularjs framework

In summary:

I want to know if it is possible to combine the marker clusterer package with the angularjs-google-mapspackage and if there are any solutions to the problem.

Detail:

I'm replacing the angular-google-maps package as it is no longer maintained. The recommended alternative is the angularjs-google-maps which is what I used in the following code:

HTML:

 <ng-map></ng-map>

Angularjs:

var lpool= {lat: 53.4084, lng: -2.9916};
var self = this;
var self.customers = [];//data objects received elsewhere

        //retrieve map object
        NgMap.getMap().then(function(map) {
            self.mapObject = map;

            //Liverpool geolocation
            var options = {
                zoom:12,
                center:lpool
            };

            //options passed to map
            self.mapObject.setOptions(options);

                createMarkers(); // Start the chain

        });

 function createMarkers() {
        // marker icons
        var iconUrlBlue = 'modules/driver/images/gm-marker-blue.png';
        var iconUrlPink = 'modules/driver/images/gm-marker-pink.png';

        // min/max values for nudging markers who are on the same spot
        var min = 0.999999;
        var max = 1.000001;
        var markers = [];

        self.customers.forEach(function(customer) {
            // create info window instance
            var infoWindow = new google.maps.InfoWindow(),
                    latitude = customer.location[1] * (Math.random() * (max - min) + min),
                longitude = customer.location[0] * (Math.random() * (max - min) + min);

            //create marker instance
            var googleMarker = new google.maps.Marker({
            position:{
                lat:latitude,
              lng:longitude
            },
            map:self.mapObject,
        icon:iconUrlPink,
          });

            function clickMarker() {
                //wrapped in apply function so Angular makes list changes
                $scope.$apply(function(){
                customer.isChecked = !customer.isChecked;
                googleMarker.setIcon( customer.isChecked ? iconUrlBlue : iconUrlPink);
                 });
                }

            function showWindow(){
                infoWindow.setOptions({
                    content:'<h4><strong>' + customer._id + '</strong> ' + customer.address + '</h4>',
                    position:{lat:latitude, lng:longitude},
                    pixelOffset: new google.maps.Size(0, -33)
                    });
                infoWindow.open(self.mapObject);
                }

            function hideWindow(){
                    infoWindow.close();
                }

            //apply previous functions to the marker
            googleMarker.addListener('click', clickMarker);
            googleMarker.addListener('mouseover', showWindow);
            googleMarker.addListener('mouseout', hideWindow);

            markers.push(googleMarker);
        });

        //create marker cluster instance
     var markerCluster = new MarkerClusterer(self.mapObject, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});


    }

The problem is that the markers are duplicated whenever I change page and reload the map page. Here is the problem as indicated by the owner in this issue of the angularjs-google-maps package:

https://github.com/allenhwkim/angularjs-google-maps/issues/575

I followed his instructions to use the marker directive, and to stop duplicates appearing on the map by removing the new google.maps.Marker method. Here is the code:

HTML:

        <ng-map id="googleMap">
                    <marker ng-repeat="marker in driverCtrl.markers"
                                    position="{{marker.pos}}"
                                    on-click="marker.clickMarker()"
                                    icon="{{marker.icon}}"
                                    on-mouseover="marker.showWindow(marker)"
                                    on-mouseout="marker.hideWindow()"></marker>
                </ng-map>

Angularjs:

 var lpool= {lat: 53.4084, lng: -2.9916};
var self = this;
var self.customers = [];//data objects received elsewhere

        //retrieve map object
        NgMap.getMap('googleMap').then(function(map) {
            self.mapObject = map;

            //Liverpool geolocation
            var options = {
                zoom:12,
                center:lpool
            };

            //options passed to map
            self.mapObject.setOptions(options);

                createMarkers(); // Start the chain

        });

    // 3. Configure google markers for each customer
    function createMarkers() {
        // marker icons
        var iconUrlBlue = 'modules/driver/images/gm-marker-blue.png';
        var iconUrlPink = 'modules/driver/images/gm-marker-pink.png';

        var infoWindow = new google.maps.InfoWindow();

        // min/max values for nudging markers who are on the same spot
        var min = 0.999999;
        var max = 1.000001;
        var markers = [];

        self.customers.forEach(function(customer) {
            // create info window instance

                var latitude = customer.location[1] * (Math.random() * (max - min) + min),
                longitude = customer.location[0] * (Math.random() * (max - min) + min);

            //create marker instance
            var googleMarker = {
            pos:[
                latitude,
              longitude
            ],
            position:{
                lat:latitude,
                lng:longitude
            },
            map:self.mapObject,
        icon:iconUrlPink,
            clickMarker : function() {
                    customer.isChecked = !customer.isChecked;
                  googleMarker.icon = customer.isChecked ? iconUrlBlue :   iconUrlPink;
             },
            showWindow : function(){
                infoWindow.setOptions({
                     content:'<h4><strong>' + customer._id + '</strong> ' + customer.address + '</h4>',
                     position:{lat:latitude, lng:longitude},
                     pixelOffset: new google.maps.Size(0, -33)
                     });
                 infoWindow.open(self.mapObject);
             },
            hideWindow : function(){
                    infoWindow.close();
                }
        };
            markers.push(googleMarker);
        });
        self.markers = markers;

        //create marker cluster instance
     var markerCluster = new MarkerClusterer(self.mapObject, self.markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});


    }

This is the marker clusterer library:

https://googlemaps.github.io/js-marker-clusterer/docs/examples.html (I cannot post more than 2 links as I have less than 10 reputation points!)

The problem in this instance is the marker clusterer needs this method new google.maps.Marker invoked on the marker objects to work. It appears I can't use the angularjs-google-maps package with the marker-clusterer package. Has anyone come across this problem and does anyone have a solution?

Other solutions I've tried:

  1. Create variables in a factory function to maintain the marker objects and use the marker.setMap(null) method to remove the duplicate when the page is changed and reloaded again.

  2. I've also used the following code to nullthe marker instances before the state transitions (changes page):

        var flag = false;
    
        $scope.$on("$stateChangeStart", function(event, to, toParams) {
           if (flag) {
            flag = false;
           return;
            }
        markers.forEach(function(marker){
           marker.setMap(null);
            });
        markers = [];
       event.preventDefault();
       flag = true;
    
     $state.go(to, toParams);
      });
    

as per the instructions set out here:

https://github.com/angular-ui/ui-router/issues/1158

If anyone has a solution to this problem, it would be greatly appreciated. Thanks.

Upvotes: 0

Views: 1012

Answers (1)

Vadim Gremyachev
Vadim Gremyachev

Reputation: 59368

In order to combine js-marker-clusterer with angularjs-google-maps

1) remove the initialization of markers via marker directive. Instead create an array of markers (item of google.maps.Marker type):

var markers = [];
data.forEach(function(item) {
    var marker = new google.maps.Marker({
        position: new google.maps.LatLng(item.lat, item.lng),
        title: item.name
    });
    markers.push(marker)
});

2) and then initialize markers on map via MarkerClusterer object:

var mc = new MarkerClusterer($scope.map, markers, mcOptions);

Example

angular.module('mapApp', ['ngMap'])
    .controller('mapController', function ($scope, NgMap) {

        NgMap.getMap().then(function (map) {
            $scope.map = map;
            $scope.initMarkerClusterer();
        });

        $scope.cities = [
            { id: 1, name: 'Oslo', pos: [59.923043, 10.752839] },
            { id: 2, name: 'Stockholm', pos: [59.339025, 18.065818] },
            { id: 3, name: 'Copenhagen', pos: [55.675507, 12.574227] },
            { id: 4, name: 'Berlin', pos: [52.521248, 13.399038] },
            { id: 5, name: 'Paris', pos: [48.856127, 2.346525] }
        ];



        $scope.initMarkerClusterer = function () {
            var markers = $scope.cities.map(function (city) {
                return $scope.createMarker(city);
            });
            var mcOptions = { imagePath: 'https://cdn.rawgit.com/googlemaps/js-marker-clusterer/gh-pages/images/m' };
            return new MarkerClusterer($scope.map, markers, mcOptions);
        };


        $scope.createMarker = function (city) {
            var marker = new google.maps.Marker({
                position: new google.maps.LatLng(city.pos[0], city.pos[1]),
                title: city.name
            });
            google.maps.event.addListener(marker, 'click', function () {
                $scope.selectedCity = city;
                $scope.map.showInfoWindow('myInfoWindow', this);
            });
            return marker;
        }

    });
<script src="https://code.angularjs.org/1.4.8/angular.js"></script>
<script src="https://maps.googleapis.com/maps/api/js"></script>
<script src="https://rawgit.com/allenhwkim/angularjs-google-maps/master/build/scripts/ng-map.js"></script>
<script src="https://googlemaps.github.io/js-marker-clusterer/src/markerclusterer.js"></script>
<script type="text/javascript" src="app.js"></script>
<div ng-app="mapApp" ng-controller="mapController">
    <ng-map default-style="true" zoom="3" center="59.339025, 18.065818">
        <info-window id="myInfoWindow">
            <div ng-non-bindable>
                <h4>{{selectedCity.name}}</h4>
            </div>
        </info-window>
    </ng-map>
</div>

Upvotes: 0

Related Questions