From 81e5456759b469813b7dcaa3a5fbef38aae2215a Mon Sep 17 00:00:00 2001 From: danzel Date: Tue, 11 Sep 2012 15:33:22 +1200 Subject: [PATCH] Update Build --- dist/leaflet.markercluster-src.js | 705 +++++++++++++----------------- dist/leaflet.markercluster.js | 2 +- 2 files changed, 299 insertions(+), 408 deletions(-) diff --git a/dist/leaflet.markercluster-src.js b/dist/leaflet.markercluster-src.js index edb01e2e9..f0dacc07f 100644 --- a/dist/leaflet.markercluster-src.js +++ b/dist/leaflet.markercluster-src.js @@ -81,12 +81,21 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ this._unspiderfy(); } - var newCluster = this._topClusterLevel._recursivelyAddLayer(layer, this._topClusterLevel._zoom - 1); + this._addLayer(layer, this._maxZoom); + + //Work out what is visible + var visibleLayer = layer, + currentZoom = this._map.getZoom(); + if (layer.__parent) { + while (visibleLayer.__parent._zoom >= currentZoom) { + visibleLayer = visibleLayer.__parent; + } + } if (this.options.animateAddingMarkers) { - this._animationAddLayer(layer, newCluster); + this._animationAddLayer(layer, visibleLayer); } else { - this._animationAddLayerNonAnimated(layer, newCluster); + this._animationAddLayerNonAnimated(layer, visibleLayer); } return this; }, @@ -97,29 +106,93 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ this._unspiderfyLayer(layer); } - if (!this._topClusterLevel._recursivelyRemoveLayer(layer)) { - //If this happens you are doing something bad - //If you've moved a marker that is in the cluster then that would be why - //console.log('failed to remove'); - var a = 0; + //Remove the marker from clusters + this._removeLayer(layer, true); + + if (layer._icon) { + L.FeatureGroup.prototype.removeLayer.call(this, layer); + } + return this; + }, + + //Remove the given object from the given array + _arraySplice: function (anArray, obj) { + for (var i = anArray.length - 1; i >= 0; i--) { + if (anArray[i] === obj) { + anArray.splice(i, 1); + return; + } + } + }, + + _removeLayer: function (marker, removeFromDistanceGrid) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + map = this._map; + + //Remove the marker from distance clusters it might be in + if (removeFromDistanceGrid) { + for (var z = this._maxZoom; z >= 0; z--) { + if (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) { + break; + } + } } - return this; + //Work our way up the clusters removing them as we go if required + var cluster = marker.__parent, + markers = cluster._markers, + otherMarker; + + //Remove the marker from the immediate parents marker list + this._arraySplice(markers, marker); + + while (cluster) { + cluster._childCount--; + + if (cluster._zoom < 0) { + //Top level, do nothing + break; + } else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required + //We need to push the other marker up to the parent + otherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0]; + + //Update distance grid + gridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom)); + gridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom)); + + //Move otherMarker up to parent + this._arraySplice(cluster.__parent._childClusters, cluster); + cluster.__parent._markers.push(otherMarker); + otherMarker.__parent = cluster.__parent; + + if (cluster._icon) { + //Cluster is currently on the map, need to put the marker on the map instead + L.FeatureGroup.prototype.removeLayer.call(this, cluster); + L.FeatureGroup.prototype.addLayer.call(this, otherMarker); + } + } else { + cluster._recalculateBounds(); + cluster._updateIcon(); + } + + cluster = cluster.__parent; + } }, clearLayers: function () { //Need our own special implementation as the LayerGroup one doesn't work for us - if (this._unspiderfy) { - this._unspiderfy(); - } - //If we aren't on the map yet, just blow away the markers we know of if (!this._map) { this._needsClustering = []; return this; } + if (this._unspiderfy) { + this._unspiderfy(); + } + //Remove all the visible layers for (var i in this._layers) { if (this._layers.hasOwnProperty(i)) { @@ -127,7 +200,7 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ } } - //Reset _topClusterLevel + //Reset _topClusterLevel and the DistanceGrids this._generateInitialClusters(); return this; @@ -151,15 +224,14 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ onAdd: function (map) { L.FeatureGroup.prototype.onAdd.call(this, map); - if (!this._topClusterLevel) { + if (!this._gridClusters) { this._generateInitialClusters(); - } else if (this._needsClustering.length > 0) { - for (var i = this._needsClustering.length - 1; i >= 0; i--) { - this.addLayer(this._needsClustering[i]); - } - this._needsClustering = []; } + for (var i = 0, l = this._needsClustering.length; i < l; i++) { + this._addLayer(this._needsClustering[i], this._maxZoom); + } + this._needsClustering = []; this._map.on('zoomend', this._zoomEnd, this); this._map.on('moveend', this._moveEnd, this); @@ -169,6 +241,16 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ } this._bindEvents(); + + + //Actually add our markers to the map: + + //Remember the current zoom level and bounds + this._zoom = this._map.getZoom(); + this._currentShownBounds = this._getExpandedVisibleBounds(); + + //Make things appear on the map + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds); }, //Overrides FeatureGroup.onRemove @@ -266,12 +348,6 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ } }, - _sqDist: function (p1, p2) { - var dx = p2.x - p1.x, - dy = p2.y - p1.y; - return dx * dx + dy * dy; - }, - _zoomEnd: function () { if (!this._map) { //May have been removed from the map by a zoomEnd handler return; @@ -287,50 +363,99 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ return; } - var newBounds = this._getExpandedVisibleBounds(), - depth = this._zoom - this._topClusterLevel._zoom; + var newBounds = this._getExpandedVisibleBounds(); - this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, depth, newBounds); - this._topClusterLevel._recursivelyAddChildrenToMap(null, depth + 1, newBounds); + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, newBounds); + this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, newBounds); this._currentShownBounds = newBounds; return; }, _generateInitialClusters: function () { - var minZoom = this._map.getMinZoom(), - maxZoom = this._map.getMaxZoom(), - currentZoom = this._map.getZoom(); + var maxZoom = this._map.getMaxZoom(), + radius = this.options.maxClusterRadius; if (this.options.disableClusteringAtZoom) { maxZoom = this.options.disableClusteringAtZoom - 1; } + this._maxZoom = maxZoom; + this._gridClusters = {}; + this._gridUnclustered = {}; - //console.time('cluster'); - this._topClusterLevel = this._clusterToMarkerCluster(this._needsClustering, maxZoom); - this._needsClustering = []; - - //Generate to the top - while (minZoom < this._topClusterLevel._zoom) { - this._topClusterLevel = this._clusterToMarkerCluster(this._topClusterLevel._childClusters.concat(this._topClusterLevel._markers), this._topClusterLevel._zoom - 1); + //Set up DistanceGrids for each zoom + for (var zoom = maxZoom; zoom >= 0; zoom--) { + this._gridClusters[zoom] = new L.DistanceGrid(radius); + this._gridUnclustered[zoom] = new L.DistanceGrid(radius); } - //console.timeEnd('cluster'); - //Remember the current zoom level and bounds - this._zoom = currentZoom; - this._currentShownBounds = this._getExpandedVisibleBounds(); + this._topClusterLevel = new L.MarkerCluster(this, -1); + }, - //Make things appear on the map - this._topClusterLevel._recursivelyAddChildrenToMap(null, currentZoom - minZoom + 1, this._currentShownBounds); + //Zoom: Zoom to start adding at (Pass this._maxZoom to start at the bottom) + _addLayer: function (layer, zoom) { + var gridClusters = this._gridClusters, + gridUnclustered = this._gridUnclustered, + markerPoint, z; + + //Find the lowest zoom level to slot this one in + for (; zoom >= 0; zoom--) { + markerPoint = this._map.project(layer.getLatLng(), zoom); // calculate pixel position + + //Try find a cluster close by + var closest = gridClusters[zoom].getNearObject(markerPoint); + if (closest) { + closest._addChild(layer); + layer.__parent = closest; + return; + } + + //Try find a marker close by to form a new cluster with + closest = gridUnclustered[zoom].getNearObject(markerPoint); + if (closest) { + if (closest.__parent) { + this._removeLayer(closest, false); + } + var parent = closest.__parent || this._topClusterLevel; + + //Create new cluster with these 2 in it + + var newCluster = new L.MarkerCluster(this, zoom, closest, layer); + gridClusters[zoom].addObject(newCluster, this._map.project(newCluster._cLatLng, zoom)); + closest.__parent = newCluster; + layer.__parent = newCluster; + + //First create any new intermediate parent clusters that don't exist + var lastParent = newCluster; + for (z = zoom - 1; z > parent._zoom; z--) { + lastParent = new L.MarkerCluster(this, z, lastParent); + gridClusters[z].addObject(lastParent, this._map.project(closest.getLatLng(), z)); + } + parent._addChild(lastParent); + + //Remove closest from this zoom level and any above that it is in, replace with newCluster + for (z = zoom; z >= 0; z--) { + if (!gridUnclustered[z].removeObject(closest, this._map.project(closest.getLatLng(), z))) { + break; + } + } + + return; + } + + //Didn't manage to cluster in at this zoom, record us as a marker here and continue upwards + gridUnclustered[zoom].addObject(layer, markerPoint); + } + + return; }, //Merge and split any existing clusters that are too big or small _mergeSplitClusters: function () { - if (this._zoom < this._map._zoom) { //Zoom in, split this._animationStart(); //Remove clusters now off screen - this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom - this._topClusterLevel._zoom, this._getExpandedVisibleBounds()); + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, this._getExpandedVisibleBounds()); this._animationZoomIn(this._zoom, this._map._zoom); @@ -342,97 +467,7 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ this._moveEnd(); } }, - - //Takes a list of markers and clusters the new marker in to them - //Will return null or the new MarkerCluster. The clustered in marker is removed from the given array - _clusterOne: function (unclustered, newMarker, markerPoint) { - var marker = unclustered.getNearObject(markerPoint); - - if (marker) { - // create a new cluster with these 2 - unclustered.removeObject(marker); - return new L.MarkerCluster(this, marker, newMarker); - } - - return null; - }, - - //Takes a list of objects that have a 'getLatLng()' function (Marker / MarkerCluster) - //Performs clustering on them (using a greedy algorithm) and returns those clusters. - //markers: List of Markers/MarkerClusters to cluster - //Returns { 'clusters': [new clusters], 'unclustered': [unclustered markers] } - _cluster: function (markers, zoom) { - var radius = this.options.maxClusterRadius, - clusters = new L.DistanceGrid(radius), - unclustered = new L.DistanceGrid(radius), - i, j, marker, markerPoint, cluster, newCluster; - - // go through each point - for (i = markers.length - 1; i >= 0; i--) { - marker = markers[i]; - markerPoint = this._map.project(marker.getLatLng(), zoom); // calculate pixel position - - // try add it to an existing cluster - cluster = clusters.getNearObject(markerPoint); - - if (cluster) { - cluster._addChild(marker); - } else { - // otherwise, look through all of the markers we haven't managed to cluster and see if we should form a cluster with them - newCluster = this._clusterOne(unclustered, marker, markerPoint); - if (newCluster) { - clusters.addObject(newCluster, this._map.project(newCluster.getLatLng(), zoom)); - } else { - // didn't manage to use it - unclustered.addObject(marker, markerPoint); - } - } - } - - var result = [], - group = this; - - // any clusters that did not end up being a child of a new cluster, make them a child of a new cluster - unclustered.eachObject(function (cluster) { - if (cluster instanceof L.MarkerCluster) { - newCluster = new L.MarkerCluster(group, cluster); - newCluster._haveGeneratedChildClusters = true; - - clusters.addObject(newCluster, cluster._dGridPoint); - unclustered.removeObject(cluster); - - return true; - } - return false; - }); - - unclustered.eachObject(function (marker) { - result.push(marker); - }); - - // initialize created clusters - clusters.eachObject(function (cluster) { - cluster._baseInit(); - result.push(cluster); - }); - - return result; - }, - - //Clusters the given markers (with _cluster) and returns the result as a MarkerCluster - _clusterToMarkerCluster: function (markers, zoom) { - var toAdd = this._cluster(markers, zoom), - result = new L.MarkerCluster(this), - i; - - for (i = toAdd.length - 1; i >= 0; i--) { - result._addChild(toAdd[i]); - } - result._zoom = zoom; - result._haveGeneratedChildClusters = true; - return result; - }, - + //Gets the maps visible bounds expanded in each direction by the size of the screen (so the user cannot see an area we do not cover in one pan) _getExpandedVisibleBounds: function () { var map = this._map, @@ -447,11 +482,16 @@ L.MarkerClusterGroup = L.FeatureGroup.extend({ //Shared animation code _animationAddLayerNonAnimated: function (layer, newCluster) { - if (newCluster === true) { + if (newCluster === layer) { L.FeatureGroup.prototype.addLayer.call(this, layer); } else if (newCluster._childCount === 2) { newCluster._addToMap(); - newCluster._recursivelyRemoveChildrenFromMap(newCluster._bounds, this._map.getMaxZoom()); //getMaxZoom will always get all children + + var markers = newCluster.getAllChildMarkers(); + L.FeatureGroup.prototype.removeLayer.call(this, markers[0]); + L.FeatureGroup.prototype.removeLayer.call(this, markers[1]); + } else { + newCluster._updateIcon(); } } }); @@ -463,12 +503,12 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { //Do nothing... }, _animationZoomIn: function (previousZoomLevel, newZoomLevel) { - this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel - this._topClusterLevel._zoom); - this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel - this._topClusterLevel._zoom + 1, this._getExpandedVisibleBounds()); + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); }, _animationZoomOut: function (previousZoomLevel, newZoomLevel) { - this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel - this._topClusterLevel._zoom); - this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel - this._topClusterLevel._zoom + 1, this._getExpandedVisibleBounds()); + this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); }, _animationAddLayer: function (layer, newCluster) { this._animationAddLayerNonAnimated(layer, newCluster); @@ -489,23 +529,21 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { _animationZoomIn: function (previousZoomLevel, newZoomLevel) { var me = this, bounds = this._getExpandedVisibleBounds(), - i, - depthToStartAt = 1 + previousZoomLevel - this._topClusterLevel._zoom, - depthToDescend = newZoomLevel - previousZoomLevel; + i; //Add all children of current clusters to map and remove those clusters from map - this._topClusterLevel._recursively(bounds, depthToStartAt, 0, function (c) { + this._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) { var startPos = c._latlng, markers = c._markers, m; - if (c._isSingleParent() && depthToDescend === 1) { //Immediately add the new child and remove us + if (c._isSingleParent() && previousZoomLevel + 1 === newZoomLevel) { //Immediately add the new child and remove us L.FeatureGroup.prototype.removeLayer.call(me, c); - c._recursivelyAddChildrenToMap(null, depthToDescend, bounds); + c._recursivelyAddChildrenToMap(null, newZoomLevel, bounds); } else { //Fade out old cluster c.setOpacity(0); - c._recursivelyAddChildrenToMap(startPos, depthToDescend, bounds); + c._recursivelyAddChildrenToMap(startPos, newZoomLevel, bounds); } //Remove all markers that aren't visible any more @@ -523,7 +561,7 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { var j, n; //Update opacities - me._topClusterLevel._recursivelyBecomeVisible(bounds, depthToStartAt + depthToDescend); + me._topClusterLevel._recursivelyBecomeVisible(bounds, newZoomLevel); //TODO Maybe? Update markers in _recursivelyBecomeVisible for (j in me._layers) { if (me._layers.hasOwnProperty(j)) { @@ -536,15 +574,15 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { } //update the positions of the just added clusters/markers - me._topClusterLevel._recursively(bounds, depthToStartAt, 0, function (c) { - c._recursivelyRestoreChildPositions(depthToDescend); + me._topClusterLevel._recursively(bounds, previousZoomLevel, newZoomLevel, function (c) { + c._recursivelyRestoreChildPositions(newZoomLevel); }); //Remove the old clusters and close the zoom animation setTimeout(function () { //update the positions of the just added clusters/markers - me._topClusterLevel._recursively(bounds, depthToStartAt, 0, function (c) { + me._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) { L.FeatureGroup.prototype.removeLayer.call(me, c); }); @@ -553,32 +591,29 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { }, _animationZoomOut: function (previousZoomLevel, newZoomLevel) { - var depthToStartAt = 1 + newZoomLevel - this._topClusterLevel._zoom, - depthToAnimateIn = previousZoomLevel - newZoomLevel; - - this._animationZoomOutSingle(this._topClusterLevel, depthToStartAt, depthToAnimateIn); + this._animationZoomOutSingle(this._topClusterLevel, previousZoomLevel, newZoomLevel); //Need to add markers for those that weren't on the map before but are now - this._topClusterLevel._recursivelyAddChildrenToMap(null, depthToStartAt, this._getExpandedVisibleBounds()); + this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds()); }, - _animationZoomOutSingle: function (marker, depthToStartAt, depthToAnimateIn) { + _animationZoomOutSingle: function (marker, previousZoomLevel, newZoomLevel) { var bounds = this._getExpandedVisibleBounds(); //Animate all of the markers in the clusters to move to their cluster center point - marker._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, depthToStartAt, depthToAnimateIn); + marker._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, previousZoomLevel, newZoomLevel); var me = this; //Update the opacity (If we immediately set it they won't animate) this._forceLayout(); - marker._recursivelyBecomeVisible(bounds, depthToStartAt); + marker._recursivelyBecomeVisible(bounds, newZoomLevel); //TODO: Maybe use the transition timing stuff to make this more reliable //When the animations are done, tidy up setTimeout(function () { - marker._recursively(bounds, depthToStartAt, 0, null, function (c) { - c._recursivelyRemoveChildrenFromMap(bounds, depthToAnimateIn - 1); + marker._recursively(bounds, newZoomLevel, 0, function (c) { + c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel); }); me._animationEnd(); }, 250); @@ -587,9 +622,10 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { var me = this; L.FeatureGroup.prototype.addLayer.call(this, layer); - if (newCluster !== true) { + if (newCluster !== layer) { if (newCluster._childCount > 2) { //Was already a cluster + newCluster._updateIcon(); this._forceLayout(); this._animationStart(); @@ -607,7 +643,7 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { this._forceLayout(); me._animationStart(); - me._animationZoomOutSingle(newCluster, 0, this._map.getMaxZoom()); + me._animationZoomOutSingle(newCluster, this._map.getMaxZoom(), this._map.getZoom()); } } }, @@ -624,12 +660,18 @@ L.MarkerClusterGroup.include(!L.DomUtil.TRANSITION ? { L.MarkerCluster = L.Marker.extend({ - initialize: function (group, a, b) { + initialize: function (group, zoom, a, b) { + + L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), { icon: this }); + + this._group = group; + this._zoom = zoom; this._markers = []; this._childClusters = []; this._childCount = 0; + this._iconNeedsUpdate = true; this._bounds = new L.LatLngBounds(); @@ -666,31 +708,54 @@ L.MarkerCluster = L.Marker.extend({ this._group._map.fitBounds(this._bounds); }, - _baseInit: function () { - this._latlng = this._wLatLng; - L.Marker.prototype.initialize.call(this, this._latlng, { icon: this._group.options.iconCreateFunction(this) }); + + _updateIcon: function () { + this._iconNeedsUpdate = true; + if (this._icon) { + this.setIcon(this); + } }, - _addChild: function (new1) { + //Cludge for Icon, we pretend to be an icon for performance + createIcon: function () { + if (this._iconNeedsUpdate) { + this._iconObj = this._group.options.iconCreateFunction(this); + this._iconNeedsUpdate = false; + } + return this._iconObj.createIcon(); + }, + createShadow: function () { + return this._iconObj.createShadow(); + }, + + + _addChild: function (new1, isNotificationFromChild) { + + this._iconNeedsUpdate = true; this._expandBounds(new1); + if (new1 instanceof L.MarkerCluster) { - this._childClusters.push(new1); + if (!isNotificationFromChild) { + this._childClusters.push(new1); + new1.__parent = this; + } this._childCount += new1._childCount; } else { - this._markers.push(new1); + if (!isNotificationFromChild) { + this._markers.push(new1); + } this._childCount++; } - if (this._icon) { - this.setIcon(this._group.options.iconCreateFunction(this)); + if (this.__parent) { + this.__parent._addChild(new1, true); } - }, + //Expand our bounds and tell our parent to _expandBounds: function (marker) { - var addedCount, - addedLatLng = marker._latlng; + addedLatLng = marker._wLatLng || marker._latlng; if (marker instanceof L.MarkerCluster) { this._bounds.extend(marker._bounds); @@ -700,9 +765,9 @@ L.MarkerCluster = L.Marker.extend({ addedCount = 1; } - if (!this._latlng) { + if (!this._cLatLng) { // when clustering, take position of the first point as the cluster center - this._latlng = this._cLatLng = addedLatLng; + this._cLatLng = marker._cLatLng || addedLatLng; } // when showing clusters, take weighted average of all points as cluster center @@ -710,7 +775,7 @@ L.MarkerCluster = L.Marker.extend({ //Calculate weighted latlng for display if (!this._wLatLng) { - this._wLatLng = new L.LatLng(addedLatLng.lat, addedLatLng.lng); + this._latlng = this._wLatLng = new L.LatLng(addedLatLng.lat, addedLatLng.lng); } else { this._wLatLng.lat = (addedLatLng.lat * addedCount + this._wLatLng.lat * this._childCount) / totalCount; this._wLatLng.lng = (addedLatLng.lng * addedCount + this._wLatLng.lng * this._childCount) / totalCount; @@ -725,188 +790,7 @@ L.MarkerCluster = L.Marker.extend({ } L.FeatureGroup.prototype.addLayer.call(this._group, this); }, - - //layer: The layer to try add - //returns: - // true: was able to put this marker in, but don't know its current visible parents position (If returned externally, add this marker at its position) - // false: wasn't able to put this marker in - // a MarkerCluster: the visible parent of the marker - _recursivelyAddLayer: function (layer, zoom) { - var group = this._group, - map = group._map, - maxClusterRadius = group.options.maxClusterRadius, - result = false, - sqDist = this._group._sqDist, - i; - - for (i = this._childClusters.length - 1; i >= 0; i--) { - var c = this._childClusters[i]; - //Recurse into children where their bounds fits the layer or they can just take it - if (c._bounds.contains(layer.getLatLng()) || c._canAcceptPosition(layer.getLatLng(), zoom + 1)) { - result = c._recursivelyAddLayer(layer, zoom + 1); - if (result) { - this._childCount++; - break; - } - } - } - - //Couldn't add it to a child, but it should be part of us (this._zoom -> we are the root node) - if (!result && (this._canAcceptPosition(layer.getLatLng(), zoom) || ('_zoom' in this))) { - - //If we are allowed to cluster at our childs level - if (zoom + 1 !== group.options.disableClusteringAtZoom) { - - //Add to ourself instead - var layerPos = map.project(layer.getLatLng(), zoom + 1); - - //var distanceGrid = new L.DistanceGrid(maxClusterRadius); - for (i = this._markers.length - 1; i >= 0; i--) { - var m = this._markers[i]; - if (sqDist(layerPos, map.project(m.getLatLng(), zoom + 1)) < (maxClusterRadius * maxClusterRadius)) { - result = m; - this._markers.splice(i, 1); - this._childCount--; - break; - } - } - } - //result = distanceGrid.getNearObject(map.project(layer.getLatLng(), zoom + 1)); - - if (result) { - //Create a new cluster for them - result = new L.MarkerCluster(this._group, result, layer); - result._baseInit(); - - //Add our new child - this._addChild(result); - - //We may be above the zoom that these 2 markers would initially cluster at - // so push the new cluster as deep as it can go - var wantedZoom = map.getZoom() - 1, - maxZoom = map.getMaxZoom(), - newResult, - finalResult = (zoom === wantedZoom) ? result : true; - - if (group.options.disableClusteringAtZoom) { - maxZoom = group.options.disableClusteringAtZoom - 2; - } - - while (zoom < maxZoom) { - zoom++; - - //Shouldn't be a cluster at this level - if (sqDist(map.project(layer.getLatLng(), zoom + 1), map.project(result._markers[0].getLatLng(), zoom + 1)) >= (maxClusterRadius * maxClusterRadius)) { - break; - } - - newResult = new L.MarkerCluster(this._group, result._markers[0], layer); - newResult._baseInit(); - result._markers = []; - result._childClusters.push(newResult); - result = newResult; - - if (zoom === wantedZoom) { - finalResult = result; - } - } - result = finalResult; - - } else { - this._addChild(layer); - result = true; - } - } - - if (result) { - if (!('_zoom' in this)) { - this.setIcon(this._group.options.iconCreateFunction(this)); - } - this._recalculateBounds(); - } - if (result === true) { - if (this._icon) { - result = this; - } - } - - return result; - }, - - _canAcceptPosition: function (latlng, zoom) { - if (this._childCount === 0) { - return true; - } - - var clusterRadiusSqrd = this._group.options.maxClusterRadius * this._group.options.maxClusterRadius, - pos = this._group._map.project(this._cLatLng, zoom), - otherpos = this._group._map.project(latlng, zoom); - - return (this._group._sqDist(pos, otherpos) <= clusterRadiusSqrd); - }, - - //Removes the given node from this marker cluster (or its child as required) - //Returns true if it (or a child cluster) removes the marker - _recursivelyRemoveLayer: function (layer) { - var group = this._group, - markers = this._markers, - childClusters = this._childClusters, - i; - - //Check our children - for (i = markers.length - 1; i >= 0; i--) { - if (markers[i] === layer) { - if (markers[i]._icon) { - L.FeatureGroup.prototype.removeLayer.call(group, markers[i]); - } - - markers.splice(i, 1); - this._childCount--; - this._recalculateBounds(); - - if (!('_zoom' in this)) { - this.setIcon(group.options.iconCreateFunction(this)); - } - return true; - } - } - - //Otherwise check our childClusters - for (i = childClusters.length - 1; i >= 0; i--) { - var child = childClusters[i]; - - if (child._bounds.contains(layer._latlng) && child._recursivelyRemoveLayer(layer)) { - this._childCount--; - if (!('_zoom' in this)) { - this.setIcon(group.options.iconCreateFunction(this)); - } - - //if our child cluster is no longer a cluster, remove it and replace with just the marker - if (child._childCount === 1) { - - //If the child is visible, remove it and put the marker on the map - if (child._icon) { - L.FeatureGroup.prototype.removeLayer.call(group, child); - L.FeatureGroup.prototype.addLayer.call(group, child._markers[0]); - } - - //Take ownership of its only marker and bin the cluster - markers.push(child._markers[0]); - childClusters.splice(i, 1); - } - - this._recalculateBounds(); - - if (this._icon && this._childCount > 1) { //No need to update if we are getting removed anyway - this.setIcon(group.options.iconCreateFunction(this)); - } - return true; - } - } - - return false; - }, - + _recursivelyAnimateChildrenIn: function (bounds, center, depth) { this._recursively(bounds, 0, depth - 1, function (c) { @@ -936,16 +820,16 @@ L.MarkerCluster = L.Marker.extend({ ); }, - _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, depthToStartAt, depthToAnimateIn) { - this._recursively(bounds, depthToStartAt, 0, + _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, previousZoomLevel, newZoomLevel) { + this._recursively(bounds, newZoomLevel, 0, function (c) { - c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), depthToAnimateIn); + c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel); //TODO: depthToAnimateIn affects _isSingleParent, if there is a multizoom we may/may not be. //As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate - if (c._isSingleParent() && depthToAnimateIn === 1) { + if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) { c.setOpacity(1); - c._recursivelyRemoveChildrenFromMap(bounds, depthToAnimateIn - 1); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds + c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds } else { c.setOpacity(0); } @@ -955,16 +839,16 @@ L.MarkerCluster = L.Marker.extend({ ); }, - _recursivelyBecomeVisible: function (bounds, depth) { - this._recursively(bounds, 0, depth, null, function (c) { + _recursivelyBecomeVisible: function (bounds, zoomLevel) { + this._recursively(bounds, 0, zoomLevel, null, function (c) { c.setOpacity(1); }); }, - _recursivelyAddChildrenToMap: function (startPos, depth, bounds) { - this._recursively(bounds, 0, depth, - function (c, recursionDepth) { - if (recursionDepth === 0) { + _recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) { + this._recursively(bounds, 0, zoomLevel, + function (c) { + if (zoomLevel === c._zoom) { return; } @@ -988,12 +872,11 @@ L.MarkerCluster = L.Marker.extend({ }, function (c) { c._addToMap(startPos); - } ); }, - _recursivelyRestoreChildPositions: function (depth) { + _recursivelyRestoreChildPositions: function (zoomLevel) { //Fix positions of child markers for (var i = this._markers.length - 1; i >= 0; i--) { var nm = this._markers[i]; @@ -1003,14 +886,14 @@ L.MarkerCluster = L.Marker.extend({ } } - if (depth === 1) { + if (zoomLevel - 1 === this._zoom) { //Reposition child clusters for (var j = this._childClusters.length - 1; j >= 0; j--) { this._childClusters[j]._restorePosition(); } } else { for (var k = this._childClusters.length - 1; k >= 0; k--) { - this._childClusters[k]._recursivelyRestoreChildPositions(depth - 1); + this._childClusters[k]._recursivelyRestoreChildPositions(zoomLevel); } } }, @@ -1023,9 +906,9 @@ L.MarkerCluster = L.Marker.extend({ }, //exceptBounds: If set, don't remove any markers/clusters in it - _recursivelyRemoveChildrenFromMap: function (previousBounds, depth, exceptBounds) { + _recursivelyRemoveChildrenFromMap: function (previousBounds, zoomLevel, exceptBounds) { var m, i; - this._recursively(previousBounds, 0, depth, + this._recursively(previousBounds, -1, zoomLevel - 1, function (c) { //Remove markers at every level for (i = c._markers.length - 1; i >= 0; i--) { @@ -1051,36 +934,37 @@ L.MarkerCluster = L.Marker.extend({ //Run the given functions recursively to this and child clusters // boundsToApplyTo: a L.LatLngBounds representing the bounds of what clusters to recurse in to - // depthToStartAt: the depth to start calling the given functions - // timesToRecurse: how many layers deep to recurse in to after hitting depthToStartAt, bottom level: depthToRunFor == 0 + // zoomLevelToStart: zoom level to start running functions (inclusive) + // zoomLevelToStop: zoom level to stop running functions (inclusive) // runAtEveryLevel: function that takes an L.MarkerCluster as an argument that should be applied on every level // runAtBottomLevel: function that takes an L.MarkerCluster as an argument that should be applied at only the bottom level - _recursively: function (boundsToApplyTo, depthToStartAt, timesToRecurse, runAtEveryLevel, runAtBottomLevel) { + _recursively: function (boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel) { var childClusters = this._childClusters, + zoom = this._zoom, i, c; - if (depthToStartAt > 0) { //Still going down to required depth, just recurse to child clusters + if (zoomLevelToStart > zoom) { //Still going down to required depth, just recurse to child clusters for (i = childClusters.length - 1; i >= 0; i--) { c = childClusters[i]; if (boundsToApplyTo.intersects(c._bounds)) { - c._recursively(boundsToApplyTo, depthToStartAt - 1, timesToRecurse, runAtEveryLevel, runAtBottomLevel); + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); } } } else { //In required depth if (runAtEveryLevel) { - runAtEveryLevel(this, timesToRecurse); + runAtEveryLevel(this); } - if (timesToRecurse === 0 && runAtBottomLevel) { + if (runAtBottomLevel && this._zoom === zoomLevelToStop) { runAtBottomLevel(this); } //TODO: This loop is almost the same as above - if (timesToRecurse > 0) { + if (zoomLevelToStop > zoom) { for (i = childClusters.length - 1; i >= 0; i--) { c = childClusters[i]; if (boundsToApplyTo.intersects(c._bounds)) { - c._recursively(boundsToApplyTo, depthToStartAt, timesToRecurse - 1, runAtEveryLevel, runAtBottomLevel); + c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel); } } } @@ -1122,6 +1006,7 @@ L.DistanceGrid = function (cellSize) { this._cellSize = cellSize; this._sqCellSize = cellSize * cellSize; this._grid = {}; + this._objectPoint = { }; }; L.DistanceGrid.prototype = { @@ -1131,10 +1016,10 @@ L.DistanceGrid.prototype = { y = this._getCoord(point.y), grid = this._grid, row = grid[y] = grid[y] || {}, - cell = row[x] = row[x] || []; + cell = row[x] = row[x] || [], + stamp = L.Util.stamp(obj); - obj._dGridCell = cell; - obj._dGridPoint = point; + this._objectPoint[stamp] = point; cell.push(obj); }, @@ -1144,25 +1029,30 @@ L.DistanceGrid.prototype = { this.addObject(obj, point); }, - removeObject: function (obj) { - var oldCell = obj._dGridCell, - point = obj._dGridPoint, - i, len, x, y; + //Returns true if the object was found + removeObject: function (obj, point) { + var x = this._getCoord(point.x), + y = this._getCoord(point.y), + grid = this._grid, + row = grid[y] = grid[y] || {}, + cell = row[x] = row[x] || [], + i, len; - for (i = 0, len = oldCell.length; i < len; i++) { - if (oldCell[i] === obj) { + delete this._objectPoint[L.Util.stamp(obj)]; - oldCell.splice(i, 1); + for (i = 0, len = cell.length; i < len; i++) { + if (cell[i] === obj) { + + cell.splice(i, 1); if (len === 1) { - x = this._getCoord(point.x); - y = this._getCoord(point.y); - delete this._grid[y][x]; + delete row[x]; } - break; + return true; } } + }, eachObject: function (fn, context) { @@ -1194,8 +1084,9 @@ L.DistanceGrid.prototype = { var x = this._getCoord(point.x), y = this._getCoord(point.y), i, j, k, row, cell, len, obj, dist, + objectPoint = this._objectPoint, closestDistSq = this._sqCellSize, - closest = null; + closest = null; for (i = y - 1; i <= y + 1; i++) { row = this._grid[i]; @@ -1207,7 +1098,7 @@ L.DistanceGrid.prototype = { for (k = 0, len = cell.length; k < len; k++) { obj = cell[k]; - dist = this._sqDist(obj._dGridPoint, point); + dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point); if (dist < closestDistSq) { closestDistSq = dist; closest = obj; diff --git a/dist/leaflet.markercluster.js b/dist/leaflet.markercluster.js index c25db86ce..0cf3fc2df 100644 --- a/dist/leaflet.markercluster.js +++ b/dist/leaflet.markercluster.js @@ -3,4 +3,4 @@ Leaflet.markercluster is an open-source JavaScript library for Marker Clustering on leaflet powered maps. https://github.com/danzel/Leaflet.markercluster */ -(function(e,t){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,skipDuplicateAddTesting:!1,animateAddingMarkers:!1},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),L.FeatureGroup.prototype.initialize.call(this,[]),this._inZoomAnimation=0,this._needsClustering=[],this._currentShownBounds=null},addLayer:function(e){if(e instanceof L.LayerGroup){for(var t in e._layers)e._layers.hasOwnProperty(t)&&this.addLayer(e._layers[t]);return this}this.options.singleMarkerMode&&(e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}}));if(!this._map)return this._needsClustering.push(e),this;if(!this.options.skipDuplicateAddTesting&&this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy();var n=this._topClusterLevel._recursivelyAddLayer(e,this._topClusterLevel._zoom-1);return this.options.animateAddingMarkers?this._animationAddLayer(e,n):this._animationAddLayerNonAnimated(e,n),this},removeLayer:function(e){this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e));if(!this._topClusterLevel._recursivelyRemoveLayer(e))var t=0;return this},clearLayers:function(){this._unspiderfy&&this._unspiderfy();if(!this._map)return this._needsClustering=[],this;for(var e in this._layers)this._layers.hasOwnProperty(e)&&L.FeatureGroup.prototype.removeLayer.call(this,this._layers[e]);return this._generateInitialClusters(),this},hasLayer:function(e){var t=!1;return this._topClusterLevel._recursively(new L.LatLngBounds([e.getLatLng()]),0,this._map.getMaxZoom()+1,function(n){for(var r=n._markers.length-1;r>=0&&!t;r--)n._markers[r]===e&&(t=!0)},null),t},onAdd:function(e){L.FeatureGroup.prototype.onAdd.call(this,e);if(!this._topClusterLevel)this._generateInitialClusters();else if(this._needsClustering.length>0){for(var t=this._needsClustering.length-1;t>=0;t--)this.addLayer(this._needsClustering[t]);this._needsClustering=[]}this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents()},onRemove:function(e){this._map.off("zoomend",this._zoomEnd,this),this._map.off("moveend",this._moveEnd,this),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),L.FeatureGroup.prototype.onRemove.call(this,e)},_propagateEvent:function(e){e.target instanceof L.MarkerCluster&&(e.type="cluster"+e.type),L.FeatureGroup.prototype._propagateEvent.call(this,e)},_defaultIconCreateFunction:function(e){var t=e.getChildCount(),n=" marker-cluster-";return t<10?n+="small":t<100?n+="medium":n+="large",new L.DivIcon({html:"
"+t+"
",className:"marker-cluster"+n,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=null,t=this._map,n=this.options.spiderfyOnMaxZoom,r=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick;(n||i)&&this.on("clusterclick",function(e){t.getMaxZoom()===t.getZoom()?n&&e.layer.spiderfy():i&&e.layer.zoomToBounds()},this),r&&(this.on("clustermouseover",function(n){if(this._inZoomAnimation)return;e&&t.removeLayer(e),n.layer.getChildCount()>2&&(e=new L.Polygon(n.layer.getConvexHull()),t.addLayer(e))},this),this.on("clustermouseout",function(){e&&(t.removeLayer(e),e=null)},this),t.on("zoomend",function(){e&&(t.removeLayer(e),e=null)},this),t.on("layerremove",function(n){e&&n.layer===this&&(t.removeLayer(e),e=null)},this))},_sqDist:function(e,t){var n=t.x-e.x,r=t.y-e.y;return n*n+r*r},_zoomEnd:function(){if(!this._map)return;this._mergeSplitClusters(),this._zoom=this._map._zoom,this._currentShownBounds=this._getExpandedVisibleBounds()},_moveEnd:function(){if(this._inZoomAnimation)return;var e=this._getExpandedVisibleBounds(),t=this._zoom-this._topClusterLevel._zoom;this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t+1,e),this._currentShownBounds=e;return},_generateInitialClusters:function(){var e=this._map.getMinZoom(),t=this._map.getMaxZoom(),n=this._map.getZoom();this.options.disableClusteringAtZoom&&(t=this.options.disableClusteringAtZoom-1),this._topClusterLevel=this._clusterToMarkerCluster(this._needsClustering,t),this._needsClustering=[];while(ethis._map._zoom?(this._animationStart(),this._animationZoomOut(this._zoom,this._map._zoom)):this._moveEnd()},_clusterOne:function(e,t,n){var r=e.getNearObject(n);return r?(e.removeObject(r),new L.MarkerCluster(this,r,t)):null},_cluster:function(e,t){var n=this.options.maxClusterRadius,r=new L.DistanceGrid(n),i=new L.DistanceGrid(n),s,o,u,a,f,l;for(s=e.length-1;s>=0;s--)u=e[s],a=this._map.project(u.getLatLng(),t),f=r.getNearObject(a),f?f._addChild(u):(l=this._clusterOne(i,u,a),l?r.addObject(l,this._map.project(l.getLatLng(),t)):i.addObject(u,a));var c=[],h=this;return i.eachObject(function(e){return e instanceof L.MarkerCluster?(l=new L.MarkerCluster(h,e),l._haveGeneratedChildClusters=!0,r.addObject(l,e._dGridPoint),i.removeObject(e),!0):!1}),i.eachObject(function(e){c.push(e)}),r.eachObject(function(e){e._baseInit(),c.push(e)}),c},_clusterToMarkerCluster:function(e,t){var n=this._cluster(e,t),r=new L.MarkerCluster(this),i;for(i=n.length-1;i>=0;i--)r._addChild(n[i]);return r._zoom=t,r._haveGeneratedChildClusters=!0,r},_getExpandedVisibleBounds:function(){var e=this._map,t=e.getPixelBounds(),n=L.Browser.mobile?0:Math.abs(t.max.x-t.min.x),r=L.Browser.mobile?0:Math.abs(t.max.y-t.min.y),i=e.unproject(new L.Point(t.min.x-n,t.min.y-r)),s=e.unproject(new L.Point(t.max.x+n,t.max.y+r));return new L.LatLngBounds(i,s)},_animationAddLayerNonAnimated:function(e,t){t===!0?L.FeatureGroup.prototype.addLayer.call(this,e):t._childCount===2&&(t._addToMap(),t._recursivelyRemoveChildrenFromMap(t._bounds,this._map.getMaxZoom()))}}),L.MarkerClusterGroup.include(L.DomUtil.TRANSITION?{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--},_animationZoomIn:function(e,t){var n=this,r=this._getExpandedVisibleBounds(),i,s=1+e-this._topClusterLevel._zoom,o=t-e;this._topClusterLevel._recursively(r,s,0,function(e){var t=e._latlng,s=e._markers,u;e._isSingleParent()&&o===1?(L.FeatureGroup.prototype.removeLayer.call(n,e),e._recursivelyAddChildrenToMap(null,o,r)):(e.setOpacity(0),e._recursivelyAddChildrenToMap(t,o,r));for(i=s.length-1;i>=0;i--)u=s[i],r.contains(u._latlng)||L.FeatureGroup.prototype.removeLayer.call(n,u)}),this._forceLayout();var u,a;n._topClusterLevel._recursivelyBecomeVisible(r,s+o);for(u in n._layers)n._layers.hasOwnProperty(u)&&(a=n._layers[u],!(a instanceof L.MarkerCluster)&&a._icon&&a.setOpacity(1));n._topClusterLevel._recursively(r,s,0,function(e){e._recursivelyRestoreChildPositions(o)}),setTimeout(function(){n._topClusterLevel._recursively(r,s,0,function(e){L.FeatureGroup.prototype.removeLayer.call(n,e)}),n._animationEnd()},250)},_animationZoomOut:function(e,t){var n=1+t-this._topClusterLevel._zoom,r=e-t;this._animationZoomOutSingle(this._topClusterLevel,n,r),this._topClusterLevel._recursivelyAddChildrenToMap(null,n,this._getExpandedVisibleBounds())},_animationZoomOutSingle:function(e,t,n){var r=this._getExpandedVisibleBounds();e._recursivelyAnimateChildrenInAndAddSelfToMap(r,t,n);var i=this;this._forceLayout(),e._recursivelyBecomeVisible(r,t),setTimeout(function(){e._recursively(r,t,0,null,function(e){e._recursivelyRemoveChildrenFromMap(r,n-1)}),i._animationEnd()},250)},_animationAddLayer:function(e,t){var n=this;L.FeatureGroup.prototype.addLayer.call(this,e),t!==!0&&(t._childCount>2?(this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.setOpacity(0),setTimeout(function(){L.FeatureGroup.prototype.removeLayer.call(n,e),e.setOpacity(1),n._animationEnd()},250)):(this._forceLayout(),n._animationStart(),n._animationZoomOutSingle(t,0,this._map.getMaxZoom())))},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e-this._topClusterLevel._zoom),this._topClusterLevel._recursivelyAddChildrenToMap(null,t-this._topClusterLevel._zoom+1,this._getExpandedVisibleBounds())},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e-this._topClusterLevel._zoom),this._topClusterLevel._recursivelyAddChildrenToMap(null,t-this._topClusterLevel._zoom+1,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}}),L.MarkerCluster=L.Marker.extend({initialize:function(e,t,n){this._group=e,this._markers=[],this._childClusters=[],this._childCount=0,this._bounds=new L.LatLngBounds,t&&this._addChild(t),n&&this._addChild(n)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var n=this._markers.length-1;n>=0;n--)e.push(this._markers[n]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(){this._group._map.fitBounds(this._bounds)},_baseInit:function(){this._latlng=this._wLatLng,L.Marker.prototype.initialize.call(this,this._latlng,{icon:this._group.options.iconCreateFunction(this)})},_addChild:function(e){this._expandBounds(e),e instanceof L.MarkerCluster?(this._childClusters.push(e),this._childCount+=e._childCount):(this._markers.push(e),this._childCount++),this._icon&&this.setIcon(this._group.options.iconCreateFunction(this))},_expandBounds:function(e){var t,n=e._latlng;e instanceof L.MarkerCluster?(this._bounds.extend(e._bounds),t=e._childCount):(this._bounds.extend(n),t=1),this._latlng||(this._latlng=this._cLatLng=n);var r=this._childCount+t;this._wLatLng?(this._wLatLng.lat=(n.lat*t+this._wLatLng.lat*this._childCount)/r,this._wLatLng.lng=(n.lng*t+this._wLatLng.lng*this._childCount)/r):this._wLatLng=new L.LatLng(n.lat,n.lng)},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),L.FeatureGroup.prototype.addLayer.call(this._group,this)},_recursivelyAddLayer:function(e,t){var n=this._group,r=n._map,i=n.options.maxClusterRadius,s=!1,o=this._group._sqDist,u;for(u=this._childClusters.length-1;u>=0;u--){var a=this._childClusters[u];if(a._bounds.contains(e.getLatLng())||a._canAcceptPosition(e.getLatLng(),t+1)){s=a._recursivelyAddLayer(e,t+1);if(s){this._childCount++;break}}}if(!s&&(this._canAcceptPosition(e.getLatLng(),t)||"_zoom"in this)){if(t+1!==n.options.disableClusteringAtZoom){var f=r.project(e.getLatLng(),t+1);for(u=this._markers.length-1;u>=0;u--){var l=this._markers[u];if(o(f,r.project(l.getLatLng(),t+1))=i*i)break;p=new L.MarkerCluster(this._group,s._markers[0],e),p._baseInit(),s._markers=[],s._childClusters.push(p),s=p,t===c&&(d=s)}s=d}else this._addChild(e),s=!0}return s&&("_zoom"in this||this.setIcon(this._group.options.iconCreateFunction(this)),this._recalculateBounds()),s===!0&&this._icon&&(s=this),s},_canAcceptPosition:function(e,t){if(this._childCount===0)return!0;var n=this._group.options.maxClusterRadius*this._group.options.maxClusterRadius,r=this._group._map.project(this._cLatLng,t),i=this._group._map.project(e,t);return this._group._sqDist(r,i)<=n},_recursivelyRemoveLayer:function(e){var t=this._group,n=this._markers,r=this._childClusters,i;for(i=n.length-1;i>=0;i--)if(n[i]===e)return n[i]._icon&&L.FeatureGroup.prototype.removeLayer.call(t,n[i]),n.splice(i,1),this._childCount--,this._recalculateBounds(),"_zoom"in this||this.setIcon(t.options.iconCreateFunction(this)),!0;for(i=r.length-1;i>=0;i--){var s=r[i];if(s._bounds.contains(e._latlng)&&s._recursivelyRemoveLayer(e))return this._childCount--,"_zoom"in this||this.setIcon(t.options.iconCreateFunction(this)),s._childCount===1&&(s._icon&&(L.FeatureGroup.prototype.removeLayer.call(t,s),L.FeatureGroup.prototype.addLayer.call(t,s._markers[0])),n.push(s._markers[0]),r.splice(i,1)),this._recalculateBounds(),this._icon&&this._childCount>1&&this.setIcon(t.options.iconCreateFunction(this)),!0}return!1},_recursivelyAnimateChildrenIn:function(e,t,n){this._recursively(e,0,n-1,function(e){var n=e._markers,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))},function(e){var n=e._childClusters,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,n){this._recursively(e,t,0,function(t){t._recursivelyAnimateChildrenIn(e,t._group._map.latLngToLayerPoint(t.getLatLng()).round(),n),t._isSingleParent()&&n===1?(t.setOpacity(1),t._recursivelyRemoveChildrenFromMap(e,n-1)):t.setOpacity(0),t._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,0,t,null,function(e){e.setOpacity(1)})},_recursivelyAddChildrenToMap:function(e,t,n){this._recursively(n,0,t,function(t,r){if(r===0)return;for(var i=t._markers.length-1;i>=0;i--){var s=t._markers[i];if(!n.contains(s._latlng))continue;e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.setOpacity(0)),L.FeatureGroup.prototype.addLayer.call(t._group,s)}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var n=this._markers[t];n._backupLatlng&&(n.setLatLng(n._backupLatlng),delete n._backupLatlng)}if(e===1)for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._restorePosition();else for(var i=this._childClusters.length-1;i>=0;i--)this._childClusters[i]._recursivelyRestoreChildPositions(e-1)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,n){var r,i;this._recursively(e,0,t,function(e){for(i=e._markers.length-1;i>=0;i--){r=e._markers[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}},function(e){for(i=e._childClusters.length-1;i>=0;i--){r=e._childClusters[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}})},_recursively:function(e,t,n,r,i){var s=this._childClusters,o,u;if(t>0)for(o=s.length-1;o>=0;o--)u=s[o],e.intersects(u._bounds)&&u._recursively(e,t-1,n,r,i);else{r&&r(this,n),n===0&&i&&i(this);if(n>0)for(o=s.length-1;o>=0;o--)u=s[o],e.intersects(u._bounds)&&u._recursively(e,t,n-1,r,i)}},_recalculateBounds:function(){var e=this._markers,t=this._childClusters,n;this._bounds=new L.LatLngBounds;for(n=e.length-1;n>=0;n--)this._bounds.extend(e[n].getLatLng());for(n=t.length-1;n>=0;n--)this._bounds.extend(t[n]._bounds);this._childCount===0?delete this._latlng:this.setLatLng(this._wLatLng)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={}},L.DistanceGrid.prototype={addObject:function(e,t){var n=this._getCoord(t.x),r=this._getCoord(t.y),i=this._grid,s=i[r]=i[r]||{},o=s[n]=s[n]||[];e._dGridCell=o,e._dGridPoint=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e){var t=e._dGridCell,n=e._dGridPoint,r,i,s,o;for(r=0,i=t.length;r=0;s--){o=t[s],u=this.getDistant(o,e);if(!(u>0))continue;i.push(o),u>n&&(n=u,r=o)}return{maxPoint:r,newPoints:i}},buildConvexHull:function(e,t){var n=[],r=this.findMostDistantPointFromBaseLine(e,t);return r.maxPoint?(n=n.concat(this.buildConvexHull([e[0],r.maxPoint],r.newPoints)),n=n.concat(this.buildConvexHull([r.maxPoint,e[1]],r.newPoints)),n):[e]},getConvexHull:function(e){var t=!1,n=!1,r=null,i=null,s;for(s=e.length-1;s>=0;s--){var o=e[s];if(t===!1||o.lat>t)r=o,t=o.lat;if(n===!1||o.lat=0;s--)i=e[s].getLatLng(),t.push(i);r=L.QuickHull.getConvexHull(t);for(s=r.length-1;s>=0;s--)n.push(r[s][0]);return n}}),L.MarkerCluster.include({_2PI:Math.PI*2,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied===this||this._group._inZoomAnimation)return;var e=this.getAllChildMarkers(),t=this._group,n=t._map,r=n.latLngToLayerPoint(this._latlng),i;this._group._unspiderfy(),this._group._spiderfied=this,e.length>=this._circleSpiralSwitchover?i=this._generatePointsSpiral(e.length,r):(r.y+=10,i=this._generatePointsCircle(e.length,r)),this._animationSpiderfy(e,i)},unspiderfy:function(e){if(this._group._inZoomAnimation)return;this._animationUnspiderfy(e),this._group._spiderfied=null},_generatePointsCircle:function(e,t){var n=this._circleFootSeparation*(2+e),r=n/this._2PI,i=this._2PI/e,s=[],o,u;s.length=e;for(o=e-1;o>=0;o--)u=this._circleStartAngle+o*i,s[o]=(new L.Point(t.x+r*Math.cos(u),t.y+r*Math.sin(u)))._round();return s},_generatePointsSpiral:function(e,t){var n=this._spiralLengthStart,r=0,i=[],s;i.length=e;for(s=e-1;s>=0;s--)r+=this._spiralFootSeparation/n+s*5e-4,i[s]=(new L.Point(t.x+n*Math.cos(r),t.y+n*Math.sin(r)))._round(),n+=this._2PI*this._spiralLengthFactor/r;return i}}),L.MarkerCluster.include(L.DomUtil.TRANSITION?{_animationSpiderfy:function(e,t){var n=this,r=this._group,i=r._map,s=i.latLngToLayerPoint(this._latlng),o,u,a,f;for(o=e.length-1;o>=0;o--)u=e[o],u.setZIndexOffset(1e6),u.setOpacity(0),L.FeatureGroup.prototype.addLayer.call(r,u),u._setPos(s);r._forceLayout(),r._animationStart();var l=L.Browser.svg?0:.3,c=L.Path.SVG_NS;for(o=e.length-1;o>=0;o--){f=i.layerPointToLatLng(t[o]),u=e[o],u._preSpiderfyLatlng=u._latlng,u.setLatLng(f),u.setOpacity(1),a=new L.Polyline([n._latlng,f],{weight:1.5,color:"#222",opacity:l}),i.addLayer(a),u._spiderLeg=a;if(!L.Browser.svg)continue;var h=a._path.getTotalLength();a._path.setAttribute("stroke-dasharray",h+","+h);var p=document.createElementNS(c,"animate");p.setAttribute("attributeName","stroke-dashoffset"),p.setAttribute("begin","indefinite"),p.setAttribute("from",h),p.setAttribute("to",0),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement(),p=document.createElementNS(c,"animate"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("begin","indefinite"),p.setAttribute("from",0),p.setAttribute("to",.5),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement()}n.setOpacity(.3);if(L.Browser.svg){this._group._forceLayout();for(o=e.length-1;o>=0;o--)u=e[o]._spiderLeg,u.options.opacity=.5,u._path.setAttribute("stroke-opacity",.5)}setTimeout(function(){r._animationEnd()},250)},_animationUnspiderfy:function(e){var t=this._group,n=t._map,r=e?n._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):n.latLngToLayerPoint(this._latlng),i=this.getAllChildMarkers(),s=L.Browser.svg,o,u,a;t._animationStart(),this.setOpacity(1);for(u=i.length-1;u>=0;u--)o=i[u],o.setLatLng(o._preSpiderfyLatlng),delete o._preSpiderfyLatlng,o._setPos(r),o.setOpacity(0),s&&(a=o._spiderLeg._path.childNodes[0],a.setAttribute("to",a.getAttribute("from")),a.setAttribute("from",0),a.beginElement(),a=o._spiderLeg._path.childNodes[1],a.setAttribute("from",.5),a.setAttribute("to",0),a.setAttribute("stroke-opacity",0),a.beginElement(),o._spiderLeg._path.setAttribute("stroke-opacity",0));setTimeout(function(){var e=0;for(u=i.length-1;u>=0;u--)o=i[u],o._spiderLeg&&e++;for(u=i.length-1;u>=0;u--){o=i[u];if(!o._spiderLeg)continue;o.setOpacity(1),o.setZIndexOffset(0),e>1&&L.FeatureGroup.prototype.removeLayer.call(t,o),n.removeLayer(o._spiderLeg),delete o._spiderLeg}t._animationEnd()},250)}}:{_animationSpiderfy:function(e,t){var n=this._group,r=n._map,i,s,o,u;for(i=e.length-1;i>=0;i--)u=r.layerPointToLatLng(t[i]),s=e[i],s._preSpiderfyLatlng=s._latlng,s.setLatLng(u),s.setZIndexOffset(1e6),L.FeatureGroup.prototype.addLayer.call(n,s),o=new L.Polyline([this._latlng,u],{weight:1.5,color:"#222"}),r.addLayer(o),s._spiderLeg=o;this.setOpacity(.3)},_animationUnspiderfy:function(){var e=this._group,t=e._map,n=this.getAllChildMarkers(),r,i;this.setOpacity(1);for(i=n.length-1;i>=0;i--)r=n[i],L.FeatureGroup.prototype.removeLayer.call(e,r),r.setLatLng(r._preSpiderfyLatlng),delete r._preSpiderfyLatlng,r.setZIndexOffset(0),t.removeLayer(r._spiderLeg),delete r._spiderLeg}}),L.MarkerClusterGroup.include({_spiderfied:null,_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.on("zoomstart",this._unspiderfyZoomStart,this),L.Browser.svg&&!L.Browser.touch&&this._map._initPathRoot()},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy()},_unspiderfyZoomStart:function(){if(!this._map)return;this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){if(L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching"))return;this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e)},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_unspiderfyLayer:function(e){e._spiderLeg&&(L.FeatureGroup.prototype.removeLayer.call(this,e),e.setOpacity(1),e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}})})(this); \ No newline at end of file +(function(e,t){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,skipDuplicateAddTesting:!1,animateAddingMarkers:!1},initialize:function(e){L.Util.setOptions(this,e),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),L.FeatureGroup.prototype.initialize.call(this,[]),this._inZoomAnimation=0,this._needsClustering=[],this._currentShownBounds=null},addLayer:function(e){if(e instanceof L.LayerGroup){for(var t in e._layers)e._layers.hasOwnProperty(t)&&this.addLayer(e._layers[t]);return this}this.options.singleMarkerMode&&(e.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[e]}}));if(!this._map)return this._needsClustering.push(e),this;if(!this.options.skipDuplicateAddTesting&&this.hasLayer(e))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(e,this._maxZoom);var n=e,r=this._map.getZoom();if(e.__parent)while(n.__parent._zoom>=r)n=n.__parent;return this.options.animateAddingMarkers?this._animationAddLayer(e,n):this._animationAddLayerNonAnimated(e,n),this},removeLayer:function(e){return this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(e)),this._removeLayer(e,!0),e._icon&&L.FeatureGroup.prototype.removeLayer.call(this,e),this},_arraySplice:function(e,t){for(var n=e.length-1;n>=0;n--)if(e[n]===t){e.splice(n,1);return}},_removeLayer:function(e,t){var n=this._gridClusters,r=this._gridUnclustered,i=this._map;if(t)for(var s=this._maxZoom;s>=0;s--)if(!r[s].removeObject(e,i.project(e.getLatLng(),s)))break;var o=e.__parent,u=o._markers,a;this._arraySplice(u,e);while(o){o._childCount--;if(o._zoom<0)break;t&&o._childCount<=1?(a=o._markers[0]===e?o._markers[1]:o._markers[0],n[o._zoom].removeObject(o,i.project(o._cLatLng,o._zoom)),r[o._zoom].addObject(a,i.project(a.getLatLng(),o._zoom)),this._arraySplice(o.__parent._childClusters,o),o.__parent._markers.push(a),a.__parent=o.__parent,o._icon&&(L.FeatureGroup.prototype.removeLayer.call(this,o),L.FeatureGroup.prototype.addLayer.call(this,a))):(o._recalculateBounds(),o._updateIcon()),o=o.__parent}},clearLayers:function(){if(!this._map)return this._needsClustering=[],this;this._unspiderfy&&this._unspiderfy();for(var e in this._layers)this._layers.hasOwnProperty(e)&&L.FeatureGroup.prototype.removeLayer.call(this,this._layers[e]);return this._generateInitialClusters(),this},hasLayer:function(e){var t=!1;return this._topClusterLevel._recursively(new L.LatLngBounds([e.getLatLng()]),0,this._map.getMaxZoom()+1,function(n){for(var r=n._markers.length-1;r>=0&&!t;r--)n._markers[r]===e&&(t=!0)},null),t},onAdd:function(e){L.FeatureGroup.prototype.onAdd.call(this,e),this._gridClusters||this._generateInitialClusters();for(var t=0,n=this._needsClustering.length;t"+t+"",className:"marker-cluster"+n,iconSize:new L.Point(40,40)})},_bindEvents:function(){var e=null,t=this._map,n=this.options.spiderfyOnMaxZoom,r=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick;(n||i)&&this.on("clusterclick",function(e){t.getMaxZoom()===t.getZoom()?n&&e.layer.spiderfy():i&&e.layer.zoomToBounds()},this),r&&(this.on("clustermouseover",function(n){if(this._inZoomAnimation)return;e&&t.removeLayer(e),n.layer.getChildCount()>2&&(e=new L.Polygon(n.layer.getConvexHull()),t.addLayer(e))},this),this.on("clustermouseout",function(){e&&(t.removeLayer(e),e=null)},this),t.on("zoomend",function(){e&&(t.removeLayer(e),e=null)},this),t.on("layerremove",function(n){e&&n.layer===this&&(t.removeLayer(e),e=null)},this))},_zoomEnd:function(){if(!this._map)return;this._mergeSplitClusters(),this._zoom=this._map._zoom,this._currentShownBounds=this._getExpandedVisibleBounds()},_moveEnd:function(){if(this._inZoomAnimation)return;var e=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,this._zoom,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,e),this._currentShownBounds=e;return},_generateInitialClusters:function(){var e=this._map.getMaxZoom(),t=this.options.maxClusterRadius;this.options.disableClusteringAtZoom&&(e=this.options.disableClusteringAtZoom-1),this._maxZoom=e,this._gridClusters={},this._gridUnclustered={};for(var n=e;n>=0;n--)this._gridClusters[n]=new L.DistanceGrid(t),this._gridUnclustered[n]=new L.DistanceGrid(t);this._topClusterLevel=new L.MarkerCluster(this,-1)},_addLayer:function(e,t){var n=this._gridClusters,r=this._gridUnclustered,i,s;for(;t>=0;t--){i=this._map.project(e.getLatLng(),t);var o=n[t].getNearObject(i);if(o){o._addChild(e),e.__parent=o;return}o=r[t].getNearObject(i);if(o){o.__parent&&this._removeLayer(o,!1);var u=o.__parent||this._topClusterLevel,a=new L.MarkerCluster(this,t,o,e);n[t].addObject(a,this._map.project(a._cLatLng,t)),o.__parent=a,e.__parent=a;var f=a;for(s=t-1;s>u._zoom;s--)f=new L.MarkerCluster(this,s,f),n[s].addObject(f,this._map.project(o.getLatLng(),s));u._addChild(f);for(s=t;s>=0;s--)if(!r[s].removeObject(o,this._map.project(o.getLatLng(),s)))break;return}r[t].addObject(e,i)}return},_mergeSplitClusters:function(){this._zoomthis._map._zoom?(this._animationStart(),this._animationZoomOut(this._zoom,this._map._zoom)):this._moveEnd()},_getExpandedVisibleBounds:function(){var e=this._map,t=e.getPixelBounds(),n=L.Browser.mobile?0:Math.abs(t.max.x-t.min.x),r=L.Browser.mobile?0:Math.abs(t.max.y-t.min.y),i=e.unproject(new L.Point(t.min.x-n,t.min.y-r)),s=e.unproject(new L.Point(t.max.x+n,t.max.y+r));return new L.LatLngBounds(i,s)},_animationAddLayerNonAnimated:function(e,t){if(t===e)L.FeatureGroup.prototype.addLayer.call(this,e);else if(t._childCount===2){t._addToMap();var n=t.getAllChildMarkers();L.FeatureGroup.prototype.removeLayer.call(this,n[0]),L.FeatureGroup.prototype.removeLayer.call(this,n[1])}else t._updateIcon()}}),L.MarkerClusterGroup.include(L.DomUtil.TRANSITION?{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--},_animationZoomIn:function(e,t){var n=this,r=this._getExpandedVisibleBounds(),i;this._topClusterLevel._recursively(r,e,0,function(s){var o=s._latlng,u=s._markers,a;s._isSingleParent()&&e+1===t?(L.FeatureGroup.prototype.removeLayer.call(n,s),s._recursivelyAddChildrenToMap(null,t,r)):(s.setOpacity(0),s._recursivelyAddChildrenToMap(o,t,r));for(i=u.length-1;i>=0;i--)a=u[i],r.contains(a._latlng)||L.FeatureGroup.prototype.removeLayer.call(n,a)}),this._forceLayout();var s,o;n._topClusterLevel._recursivelyBecomeVisible(r,t);for(s in n._layers)n._layers.hasOwnProperty(s)&&(o=n._layers[s],!(o instanceof L.MarkerCluster)&&o._icon&&o.setOpacity(1));n._topClusterLevel._recursively(r,e,t,function(e){e._recursivelyRestoreChildPositions(t)}),setTimeout(function(){n._topClusterLevel._recursively(r,e,0,function(e){L.FeatureGroup.prototype.removeLayer.call(n,e)}),n._animationEnd()},250)},_animationZoomOut:function(e,t){this._animationZoomOutSingle(this._topClusterLevel,e,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds())},_animationZoomOutSingle:function(e,t,n){var r=this._getExpandedVisibleBounds();e._recursivelyAnimateChildrenInAndAddSelfToMap(r,t,n);var i=this;this._forceLayout(),e._recursivelyBecomeVisible(r,n),setTimeout(function(){e._recursively(r,n,0,function(e){e._recursivelyRemoveChildrenFromMap(r,t)}),i._animationEnd()},250)},_animationAddLayer:function(e,t){var n=this;L.FeatureGroup.prototype.addLayer.call(this,e),t!==e&&(t._childCount>2?(t._updateIcon(),this._forceLayout(),this._animationStart(),e._setPos(this._map.latLngToLayerPoint(t.getLatLng())),e.setOpacity(0),setTimeout(function(){L.FeatureGroup.prototype.removeLayer.call(n,e),e.setOpacity(1),n._animationEnd()},250)):(this._forceLayout(),n._animationStart(),n._animationZoomOutSingle(t,this._map.getMaxZoom(),this._map.getZoom())))},_forceLayout:function(){L.Util.falseFn(document.body.offsetWidth)}}:{_animationStart:function(){},_animationZoomIn:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds())},_animationZoomOut:function(e,t){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,t,this._getExpandedVisibleBounds())},_animationAddLayer:function(e,t){this._animationAddLayerNonAnimated(e,t)}}),L.MarkerCluster=L.Marker.extend({initialize:function(e,t,n,r){L.Marker.prototype.initialize.call(this,n?n._cLatLng||n.getLatLng():new L.LatLng(0,0),{icon:this}),this._group=e,this._zoom=t,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._bounds=new L.LatLngBounds,n&&this._addChild(n),r&&this._addChild(r)},getAllChildMarkers:function(e){e=e||[];for(var t=this._childClusters.length-1;t>=0;t--)this._childClusters[t].getAllChildMarkers(e);for(var n=this._markers.length-1;n>=0;n--)e.push(this._markers[n]);return e},getChildCount:function(){return this._childCount},zoomToBounds:function(){this._group._map.fitBounds(this._bounds)},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(e,t){this._iconNeedsUpdate=!0,this._expandBounds(e),e instanceof L.MarkerCluster?(t||(this._childClusters.push(e),e.__parent=this),this._childCount+=e._childCount):(t||this._markers.push(e),this._childCount++),this.__parent&&this.__parent._addChild(e,!0)},_expandBounds:function(e){var t,n=e._wLatLng||e._latlng;e instanceof L.MarkerCluster?(this._bounds.extend(e._bounds),t=e._childCount):(this._bounds.extend(n),t=1),this._cLatLng||(this._cLatLng=e._cLatLng||n);var r=this._childCount+t;this._wLatLng?(this._wLatLng.lat=(n.lat*t+this._wLatLng.lat*this._childCount)/r,this._wLatLng.lng=(n.lng*t+this._wLatLng.lng*this._childCount)/r):this._latlng=this._wLatLng=new L.LatLng(n.lat,n.lng)},_addToMap:function(e){e&&(this._backupLatlng=this._latlng,this.setLatLng(e)),L.FeatureGroup.prototype.addLayer.call(this._group,this)},_recursivelyAnimateChildrenIn:function(e,t,n){this._recursively(e,0,n-1,function(e){var n=e._markers,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))},function(e){var n=e._childClusters,r,i;for(r=n.length-1;r>=0;r--)i=n[r],i._icon&&(i._setPos(t),i.setOpacity(0))})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(e,t,n){this._recursively(e,n,0,function(r){r._recursivelyAnimateChildrenIn(e,r._group._map.latLngToLayerPoint(r.getLatLng()).round(),t),r._isSingleParent()&&t-1===n?(r.setOpacity(1),r._recursivelyRemoveChildrenFromMap(e,t)):r.setOpacity(0),r._addToMap()})},_recursivelyBecomeVisible:function(e,t){this._recursively(e,0,t,null,function(e){e.setOpacity(1)})},_recursivelyAddChildrenToMap:function(e,t,n){this._recursively(n,0,t,function(r){if(t===r._zoom)return;for(var i=r._markers.length-1;i>=0;i--){var s=r._markers[i];if(!n.contains(s._latlng))continue;e&&(s._backupLatlng=s.getLatLng(),s.setLatLng(e),s.setOpacity(0)),L.FeatureGroup.prototype.addLayer.call(r._group,s)}},function(t){t._addToMap(e)})},_recursivelyRestoreChildPositions:function(e){for(var t=this._markers.length-1;t>=0;t--){var n=this._markers[t];n._backupLatlng&&(n.setLatLng(n._backupLatlng),delete n._backupLatlng)}if(e-1===this._zoom)for(var r=this._childClusters.length-1;r>=0;r--)this._childClusters[r]._restorePosition();else for(var i=this._childClusters.length-1;i>=0;i--)this._childClusters[i]._recursivelyRestoreChildPositions(e)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(e,t,n){var r,i;this._recursively(e,-1,t-1,function(e){for(i=e._markers.length-1;i>=0;i--){r=e._markers[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}},function(e){for(i=e._childClusters.length-1;i>=0;i--){r=e._childClusters[i];if(!n||!n.contains(r._latlng))L.FeatureGroup.prototype.removeLayer.call(e._group,r),r.setOpacity(1)}})},_recursively:function(e,t,n,r,i){var s=this._childClusters,o=this._zoom,u,a;if(t>o)for(u=s.length-1;u>=0;u--)a=s[u],e.intersects(a._bounds)&&a._recursively(e,t,n,r,i);else{r&&r(this),i&&this._zoom===n&&i(this);if(n>o)for(u=s.length-1;u>=0;u--)a=s[u],e.intersects(a._bounds)&&a._recursively(e,t,n,r,i)}},_recalculateBounds:function(){var e=this._markers,t=this._childClusters,n;this._bounds=new L.LatLngBounds;for(n=e.length-1;n>=0;n--)this._bounds.extend(e[n].getLatLng());for(n=t.length-1;n>=0;n--)this._bounds.extend(t[n]._bounds);this._childCount===0?delete this._latlng:this.setLatLng(this._wLatLng)},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.DistanceGrid=function(e){this._cellSize=e,this._sqCellSize=e*e,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(e,t){var n=this._getCoord(t.x),r=this._getCoord(t.y),i=this._grid,s=i[r]=i[r]||{},o=s[n]=s[n]||[],u=L.Util.stamp(e);this._objectPoint[u]=t,o.push(e)},updateObject:function(e,t){this.removeObject(e),this.addObject(e,t)},removeObject:function(e,t){var n=this._getCoord(t.x),r=this._getCoord(t.y),i=this._grid,s=i[r]=i[r]||{},o=s[n]=s[n]||[],u,a;delete this._objectPoint[L.Util.stamp(e)];for(u=0,a=o.length;u=0;s--){o=t[s],u=this.getDistant(o,e);if(!(u>0))continue;i.push(o),u>n&&(n=u,r=o)}return{maxPoint:r,newPoints:i}},buildConvexHull:function(e,t){var n=[],r=this.findMostDistantPointFromBaseLine(e,t);return r.maxPoint?(n=n.concat(this.buildConvexHull([e[0],r.maxPoint],r.newPoints)),n=n.concat(this.buildConvexHull([r.maxPoint,e[1]],r.newPoints)),n):[e]},getConvexHull:function(e){var t=!1,n=!1,r=null,i=null,s;for(s=e.length-1;s>=0;s--){var o=e[s];if(t===!1||o.lat>t)r=o,t=o.lat;if(n===!1||o.lat=0;s--)i=e[s].getLatLng(),t.push(i);r=L.QuickHull.getConvexHull(t);for(s=r.length-1;s>=0;s--)n.push(r[s][0]);return n}}),L.MarkerCluster.include({_2PI:Math.PI*2,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied===this||this._group._inZoomAnimation)return;var e=this.getAllChildMarkers(),t=this._group,n=t._map,r=n.latLngToLayerPoint(this._latlng),i;this._group._unspiderfy(),this._group._spiderfied=this,e.length>=this._circleSpiralSwitchover?i=this._generatePointsSpiral(e.length,r):(r.y+=10,i=this._generatePointsCircle(e.length,r)),this._animationSpiderfy(e,i)},unspiderfy:function(e){if(this._group._inZoomAnimation)return;this._animationUnspiderfy(e),this._group._spiderfied=null},_generatePointsCircle:function(e,t){var n=this._circleFootSeparation*(2+e),r=n/this._2PI,i=this._2PI/e,s=[],o,u;s.length=e;for(o=e-1;o>=0;o--)u=this._circleStartAngle+o*i,s[o]=(new L.Point(t.x+r*Math.cos(u),t.y+r*Math.sin(u)))._round();return s},_generatePointsSpiral:function(e,t){var n=this._spiralLengthStart,r=0,i=[],s;i.length=e;for(s=e-1;s>=0;s--)r+=this._spiralFootSeparation/n+s*5e-4,i[s]=(new L.Point(t.x+n*Math.cos(r),t.y+n*Math.sin(r)))._round(),n+=this._2PI*this._spiralLengthFactor/r;return i}}),L.MarkerCluster.include(L.DomUtil.TRANSITION?{_animationSpiderfy:function(e,t){var n=this,r=this._group,i=r._map,s=i.latLngToLayerPoint(this._latlng),o,u,a,f;for(o=e.length-1;o>=0;o--)u=e[o],u.setZIndexOffset(1e6),u.setOpacity(0),L.FeatureGroup.prototype.addLayer.call(r,u),u._setPos(s);r._forceLayout(),r._animationStart();var l=L.Browser.svg?0:.3,c=L.Path.SVG_NS;for(o=e.length-1;o>=0;o--){f=i.layerPointToLatLng(t[o]),u=e[o],u._preSpiderfyLatlng=u._latlng,u.setLatLng(f),u.setOpacity(1),a=new L.Polyline([n._latlng,f],{weight:1.5,color:"#222",opacity:l}),i.addLayer(a),u._spiderLeg=a;if(!L.Browser.svg)continue;var h=a._path.getTotalLength();a._path.setAttribute("stroke-dasharray",h+","+h);var p=document.createElementNS(c,"animate");p.setAttribute("attributeName","stroke-dashoffset"),p.setAttribute("begin","indefinite"),p.setAttribute("from",h),p.setAttribute("to",0),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement(),p=document.createElementNS(c,"animate"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("attributeName","stroke-opacity"),p.setAttribute("begin","indefinite"),p.setAttribute("from",0),p.setAttribute("to",.5),p.setAttribute("dur",.25),a._path.appendChild(p),p.beginElement()}n.setOpacity(.3);if(L.Browser.svg){this._group._forceLayout();for(o=e.length-1;o>=0;o--)u=e[o]._spiderLeg,u.options.opacity=.5,u._path.setAttribute("stroke-opacity",.5)}setTimeout(function(){r._animationEnd()},250)},_animationUnspiderfy:function(e){var t=this._group,n=t._map,r=e?n._latLngToNewLayerPoint(this._latlng,e.zoom,e.center):n.latLngToLayerPoint(this._latlng),i=this.getAllChildMarkers(),s=L.Browser.svg,o,u,a;t._animationStart(),this.setOpacity(1);for(u=i.length-1;u>=0;u--)o=i[u],o.setLatLng(o._preSpiderfyLatlng),delete o._preSpiderfyLatlng,o._setPos(r),o.setOpacity(0),s&&(a=o._spiderLeg._path.childNodes[0],a.setAttribute("to",a.getAttribute("from")),a.setAttribute("from",0),a.beginElement(),a=o._spiderLeg._path.childNodes[1],a.setAttribute("from",.5),a.setAttribute("to",0),a.setAttribute("stroke-opacity",0),a.beginElement(),o._spiderLeg._path.setAttribute("stroke-opacity",0));setTimeout(function(){var e=0;for(u=i.length-1;u>=0;u--)o=i[u],o._spiderLeg&&e++;for(u=i.length-1;u>=0;u--){o=i[u];if(!o._spiderLeg)continue;o.setOpacity(1),o.setZIndexOffset(0),e>1&&L.FeatureGroup.prototype.removeLayer.call(t,o),n.removeLayer(o._spiderLeg),delete o._spiderLeg}t._animationEnd()},250)}}:{_animationSpiderfy:function(e,t){var n=this._group,r=n._map,i,s,o,u;for(i=e.length-1;i>=0;i--)u=r.layerPointToLatLng(t[i]),s=e[i],s._preSpiderfyLatlng=s._latlng,s.setLatLng(u),s.setZIndexOffset(1e6),L.FeatureGroup.prototype.addLayer.call(n,s),o=new L.Polyline([this._latlng,u],{weight:1.5,color:"#222"}),r.addLayer(o),s._spiderLeg=o;this.setOpacity(.3)},_animationUnspiderfy:function(){var e=this._group,t=e._map,n=this.getAllChildMarkers(),r,i;this.setOpacity(1);for(i=n.length-1;i>=0;i--)r=n[i],L.FeatureGroup.prototype.removeLayer.call(e,r),r.setLatLng(r._preSpiderfyLatlng),delete r._preSpiderfyLatlng,r.setZIndexOffset(0),t.removeLayer(r._spiderLeg),delete r._spiderLeg}}),L.MarkerClusterGroup.include({_spiderfied:null,_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.on("zoomstart",this._unspiderfyZoomStart,this),L.Browser.svg&&!L.Browser.touch&&this._map._initPathRoot()},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy()},_unspiderfyZoomStart:function(){if(!this._map)return;this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(e){if(L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching"))return;this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(e)},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(e){this._spiderfied&&this._spiderfied.unspiderfy(e)},_unspiderfyLayer:function(e){e._spiderLeg&&(L.FeatureGroup.prototype.removeLayer.call(this,e),e.setOpacity(1),e.setZIndexOffset(0),this._map.removeLayer(e._spiderLeg),delete e._spiderLeg)}})})(this); \ No newline at end of file