Reputation: 184
In summary:
I want to know if it is possible to combine the marker clusterer package with the angularjs-google-maps
package 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:
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.
I've also used the following code to null
the 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
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