'use strict';
angular.module('cluster',[]).factory('DynamicMarkerCluster', function () {
var createClusterIcon = function (cluster) {
var childCount = cluster.getChildCount();
var childMarkers = cluster.getAllChildMarkers();
var childCount = 0;
childMarkers.forEach(function (marker) {
if (marker.count) {
childCount += marker.count;
} else {
childCount += 1;
}
});
var c = 'marker-cluster marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 100) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({
html: '
' + childCount + '
',
className: c,
iconSize: new L.Point(40, 40)
});
};
var createLeafIcon = function (childCount) {
var c = 'marker-cluster marker-cluster-';
if (childCount < 10) {
c += 'small';
} else if (childCount < 100) {
c += 'medium';
} else {
c += 'large';
}
return new L.DivIcon({
html: '' + childCount + '
',
className: c,
iconSize: new L.Point(40, 40)
});
};
var expandMarkersStub = function (keys) {
var result = [];
return {
then: function (onSuccess, onFail) {
onSuccess(result);
}
};
};
function DynamicMarkerCluster(options) {
this.updating = false;
this.updateTimeout = null;
this.layer = new L.MarkerClusterGroup({
disableClusteringAtZoom: 16,
removeOutsideVisibleBounds: true,
singleMarkerMode: false,
iconCreateFunction: createClusterIcon,
maxClusterRadius: 55
});
this.expandMarkers = options.expandMarkers || expandMarkersStub;
this.createClusterIcon = options.createClusterIcon || createClusterIcon;
this.createLeafIcon = options.createClusterIcon || createClusterIcon;
this.update = {
add: [],
remove: [],
}
}
DynamicMarkerCluster.prototype.getLayer = function () {
return this.layer;
};
DynamicMarkerCluster.prototype.onBoundsChange = function (bounds) {
if (this.updateTimeout) {
clearTimeout(this.updateTimeout);
this.updateTimeout = null;
}
var layer = this.layer;
var self = this;
this.updateTimeout = setTimeout(function () {
if (layer && layer._map) {
self.updateCluster(bounds, layer._map.getZoom());
}
self.updateTimeout = null;
}, 500);
};
DynamicMarkerCluster.prototype.applyChanges = function (changes) {
var expand = changes.expand || [];
var add = changes.add || [];
var remove = changes.remove || [];
var clear = changes.clear;
var layer = this.layer;
this.updating = true;
var self = this;
if (clear) {
layer.clearLayers();
}
if (expand) {
// async - wait till updates are loaded for atomic update
this.expandMarkers(expand).then(function (expanded) {
layer.removeLayers(remove);
add.forEach(function(marker) { layer.addLayer(marker)});
//layer.addLayers(add);
//layer.addLayers(expanded);
expanded.forEach(function(marker) { layer.addLayer(marker)});
self.updating = false;
});
} else if (add || remove) {
layer.addLayers(add);
layer.removeLayers(remove);
this.updating = false;
}
};
DynamicMarkerCluster.prototype.updateCluster = function (bounds, zoom) {
if (this.updating) {
// console.log("skipping update");
return;
}
var n = 0;
var expand = [];
var remove = [];
this.layer.eachLayer(function (marker) {
if (marker.count) {
if (marker.geoid && bounds.intersects(marker.bounds) && zoom > 8) {
n++;
expand.push(marker.geoid);
remove.push(marker);
}
}
});
this.applyChanges({expand: expand, remove: remove});
};
DynamicMarkerCluster.prototype.rebuildMapCluster = function (mapCounts, bounds, zoom) {
var layer = this.layer;
if (this.updating || !layer._map) {
// console.log("skipping rebuild");
return;
}
var needsExpanding = [];
var needsAdding = [];
if (mapCounts) {
mapCounts.forEach(function (count) {
if ((bounds.intersects(count.bounds) && zoom > 7) || count.count == 1) {
needsExpanding.push(count.geoid);
} else {
var marker = L.marker([count.lat, count.lng], {icon: createLeafIcon(count.count)});
marker.count = count.count;
marker.geoid = count.geoid;
marker.bounds = count.bounds;
needsAdding.push(marker);
}
});
}
this.applyChanges({add: needsAdding, clear: true, expand: needsExpanding});
};
return DynamicMarkerCluster;
}
);