summaryrefslogtreecommitdiffstats
path: root/debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js
diff options
context:
space:
mode:
Diffstat (limited to 'debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js')
-rw-r--r--debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js477
1 files changed, 477 insertions, 0 deletions
diff --git a/debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js b/debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js
new file mode 100644
index 0000000..b84c2b1
--- /dev/null
+++ b/debian/missing-sources/leaflet.markercluster.js/MarkerCluster.Spiderfier.js
@@ -0,0 +1,477 @@
+//This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
+//Huge thanks to jawj for implementing it first to make my job easy :-)
+
+L.MarkerCluster.include({
+
+ _2PI: Math.PI * 2,
+ _circleFootSeparation: 25, //related to circumference of circle
+ _circleStartAngle: 0,
+
+ _spiralFootSeparation: 28, //related to size of spiral (experiment!)
+ _spiralLengthStart: 11,
+ _spiralLengthFactor: 5,
+
+ _circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards.
+ // 0 -> always spiral; Infinity -> always circle
+
+ spiderfy: function () {
+ if (this._group._spiderfied === this || this._group._inZoomAnimation) {
+ return;
+ }
+
+ var childMarkers = this.getAllChildMarkers(null, true),
+ group = this._group,
+ map = group._map,
+ center = map.latLngToLayerPoint(this._latlng),
+ positions;
+
+ this._group._unspiderfy();
+ this._group._spiderfied = this;
+
+ //TODO Maybe: childMarkers order by distance to center
+
+ if (this._group.options.spiderfyShapePositions) {
+ positions = this._group.options.spiderfyShapePositions(childMarkers.length, center);
+ } else if (childMarkers.length >= this._circleSpiralSwitchover) {
+ positions = this._generatePointsSpiral(childMarkers.length, center);
+ } else {
+ center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons.
+ positions = this._generatePointsCircle(childMarkers.length, center);
+ }
+
+ this._animationSpiderfy(childMarkers, positions);
+ },
+
+ unspiderfy: function (zoomDetails) {
+ /// <param Name="zoomDetails">Argument from zoomanim if being called in a zoom animation or null otherwise</param>
+ if (this._group._inZoomAnimation) {
+ return;
+ }
+ this._animationUnspiderfy(zoomDetails);
+
+ this._group._spiderfied = null;
+ },
+
+ _generatePointsCircle: function (count, centerPt) {
+ var circumference = this._group.options.spiderfyDistanceMultiplier * this._circleFootSeparation * (2 + count),
+ legLength = circumference / this._2PI, //radius from circumference
+ angleStep = this._2PI / count,
+ res = [],
+ i, angle;
+
+ legLength = Math.max(legLength, 35); // Minimum distance to get outside the cluster icon.
+
+ res.length = count;
+
+ for (i = 0; i < count; i++) { // Clockwise, like spiral.
+ angle = this._circleStartAngle + i * angleStep;
+ res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
+ }
+
+ return res;
+ },
+
+ _generatePointsSpiral: function (count, centerPt) {
+ var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier,
+ legLength = spiderfyDistanceMultiplier * this._spiralLengthStart,
+ separation = spiderfyDistanceMultiplier * this._spiralFootSeparation,
+ lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI,
+ angle = 0,
+ res = [],
+ i;
+
+ res.length = count;
+
+ // Higher index, closer position to cluster center.
+ for (i = count; i >= 0; i--) {
+ // Skip the first position, so that we are already farther from center and we avoid
+ // being under the default cluster icon (especially important for Circle Markers).
+ if (i < count) {
+ res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
+ }
+ angle += separation / legLength + i * 0.0005;
+ legLength += lengthFactor / angle;
+ }
+ return res;
+ },
+
+ _noanimationUnspiderfy: function () {
+ var group = this._group,
+ map = group._map,
+ fg = group._featureGroup,
+ childMarkers = this.getAllChildMarkers(null, true),
+ m, i;
+
+ group._ignoreMove = true;
+
+ this.setOpacity(1);
+ for (i = childMarkers.length - 1; i >= 0; i--) {
+ m = childMarkers[i];
+
+ fg.removeLayer(m);
+
+ if (m._preSpiderfyLatlng) {
+ m.setLatLng(m._preSpiderfyLatlng);
+ delete m._preSpiderfyLatlng;
+ }
+ if (m.setZIndexOffset) {
+ m.setZIndexOffset(0);
+ }
+
+ if (m._spiderLeg) {
+ map.removeLayer(m._spiderLeg);
+ delete m._spiderLeg;
+ }
+ }
+
+ group.fire('unspiderfied', {
+ cluster: this,
+ markers: childMarkers
+ });
+ group._ignoreMove = false;
+ group._spiderfied = null;
+ }
+});
+
+//Non Animated versions of everything
+L.MarkerClusterNonAnimated = L.MarkerCluster.extend({
+ _animationSpiderfy: function (childMarkers, positions) {
+ var group = this._group,
+ map = group._map,
+ fg = group._featureGroup,
+ legOptions = this._group.options.spiderLegPolylineOptions,
+ i, m, leg, newPos;
+
+ group._ignoreMove = true;
+
+ // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
+ // The reverse order trick no longer improves performance on modern browsers.
+ for (i = 0; i < childMarkers.length; i++) {
+ newPos = map.layerPointToLatLng(positions[i]);
+ m = childMarkers[i];
+
+ // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
+ leg = new L.Polyline([this._latlng, newPos], legOptions);
+ map.addLayer(leg);
+ m._spiderLeg = leg;
+
+ // Now add the marker.
+ m._preSpiderfyLatlng = m._latlng;
+ m.setLatLng(newPos);
+ if (m.setZIndexOffset) {
+ m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING
+ }
+
+ fg.addLayer(m);
+ }
+ this.setOpacity(0.3);
+
+ group._ignoreMove = false;
+ group.fire('spiderfied', {
+ cluster: this,
+ markers: childMarkers
+ });
+ },
+
+ _animationUnspiderfy: function () {
+ this._noanimationUnspiderfy();
+ }
+});
+
+//Animated versions here
+L.MarkerCluster.include({
+
+ _animationSpiderfy: function (childMarkers, positions) {
+ var me = this,
+ group = this._group,
+ map = group._map,
+ fg = group._featureGroup,
+ thisLayerLatLng = this._latlng,
+ thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng),
+ svg = L.Path.SVG,
+ legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation.
+ finalLegOpacity = legOptions.opacity,
+ i, m, leg, legPath, legLength, newPos;
+
+ if (finalLegOpacity === undefined) {
+ finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity;
+ }
+
+ if (svg) {
+ // If the initial opacity of the spider leg is not 0 then it appears before the animation starts.
+ legOptions.opacity = 0;
+
+ // Add the class for CSS transitions.
+ legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg';
+ } else {
+ // Make sure we have a defined opacity.
+ legOptions.opacity = finalLegOpacity;
+ }
+
+ group._ignoreMove = true;
+
+ // Add markers and spider legs to map, hidden at our center point.
+ // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
+ // The reverse order trick no longer improves performance on modern browsers.
+ for (i = 0; i < childMarkers.length; i++) {
+ m = childMarkers[i];
+
+ newPos = map.layerPointToLatLng(positions[i]);
+
+ // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
+ leg = new L.Polyline([thisLayerLatLng, newPos], legOptions);
+ map.addLayer(leg);
+ m._spiderLeg = leg;
+
+ // Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/
+ // In our case the transition property is declared in the CSS file.
+ if (svg) {
+ legPath = leg._path;
+ legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox.
+ legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated.
+ legPath.style.strokeDashoffset = legLength;
+ }
+
+ // If it is a marker, add it now and we'll animate it out
+ if (m.setZIndexOffset) {
+ m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING
+ }
+ if (m.clusterHide) {
+ m.clusterHide();
+ }
+
+ // Vectors just get immediately added
+ fg.addLayer(m);
+
+ if (m._setPos) {
+ m._setPos(thisLayerPos);
+ }
+ }
+
+ group._forceLayout();
+ group._animationStart();
+
+ // Reveal markers and spider legs.
+ for (i = childMarkers.length - 1; i >= 0; i--) {
+ newPos = map.layerPointToLatLng(positions[i]);
+ m = childMarkers[i];
+
+ //Move marker to new position
+ m._preSpiderfyLatlng = m._latlng;
+ m.setLatLng(newPos);
+
+ if (m.clusterShow) {
+ m.clusterShow();
+ }
+
+ // Animate leg (animation is actually delegated to CSS transition).
+ if (svg) {
+ leg = m._spiderLeg;
+ legPath = leg._path;
+ legPath.style.strokeDashoffset = 0;
+ //legPath.style.strokeOpacity = finalLegOpacity;
+ leg.setStyle({opacity: finalLegOpacity});
+ }
+ }
+ this.setOpacity(0.3);
+
+ group._ignoreMove = false;
+
+ setTimeout(function () {
+ group._animationEnd();
+ group.fire('spiderfied', {
+ cluster: me,
+ markers: childMarkers
+ });
+ }, 200);
+ },
+
+ _animationUnspiderfy: function (zoomDetails) {
+ var me = this,
+ group = this._group,
+ map = group._map,
+ fg = group._featureGroup,
+ thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng),
+ childMarkers = this.getAllChildMarkers(null, true),
+ svg = L.Path.SVG,
+ m, i, leg, legPath, legLength, nonAnimatable;
+
+ group._ignoreMove = true;
+ group._animationStart();
+
+ //Make us visible and bring the child markers back in
+ this.setOpacity(1);
+ for (i = childMarkers.length - 1; i >= 0; i--) {
+ m = childMarkers[i];
+
+ //Marker was added to us after we were spiderfied
+ if (!m._preSpiderfyLatlng) {
+ continue;
+ }
+
+ //Close any popup on the marker first, otherwise setting the location of the marker will make the map scroll
+ m.closePopup();
+
+ //Fix up the location to the real one
+ m.setLatLng(m._preSpiderfyLatlng);
+ delete m._preSpiderfyLatlng;
+
+ //Hack override the location to be our center
+ nonAnimatable = true;
+ if (m._setPos) {
+ m._setPos(thisLayerPos);
+ nonAnimatable = false;
+ }
+ if (m.clusterHide) {
+ m.clusterHide();
+ nonAnimatable = false;
+ }
+ if (nonAnimatable) {
+ fg.removeLayer(m);
+ }
+
+ // Animate the spider leg back in (animation is actually delegated to CSS transition).
+ if (svg) {
+ leg = m._spiderLeg;
+ legPath = leg._path;
+ legLength = legPath.getTotalLength() + 0.1;
+ legPath.style.strokeDashoffset = legLength;
+ leg.setStyle({opacity: 0});
+ }
+ }
+
+ group._ignoreMove = false;
+
+ setTimeout(function () {
+ //If we have only <= one child left then that marker will be shown on the map so don't remove it!
+ var stillThereChildCount = 0;
+ for (i = childMarkers.length - 1; i >= 0; i--) {
+ m = childMarkers[i];
+ if (m._spiderLeg) {
+ stillThereChildCount++;
+ }
+ }
+
+
+ for (i = childMarkers.length - 1; i >= 0; i--) {
+ m = childMarkers[i];
+
+ if (!m._spiderLeg) { //Has already been unspiderfied
+ continue;
+ }
+
+ if (m.clusterShow) {
+ m.clusterShow();
+ }
+ if (m.setZIndexOffset) {
+ m.setZIndexOffset(0);
+ }
+
+ if (stillThereChildCount > 1) {
+ fg.removeLayer(m);
+ }
+
+ map.removeLayer(m._spiderLeg);
+ delete m._spiderLeg;
+ }
+ group._animationEnd();
+ group.fire('unspiderfied', {
+ cluster: me,
+ markers: childMarkers
+ });
+ }, 200);
+ }
+});
+
+
+L.MarkerClusterGroup.include({
+ //The MarkerCluster currently spiderfied (if any)
+ _spiderfied: null,
+
+ unspiderfy: function () {
+ this._unspiderfy.apply(this, arguments);
+ },
+
+ _spiderfierOnAdd: function () {
+ this._map.on('click', this._unspiderfyWrapper, this);
+
+ if (this._map.options.zoomAnimation) {
+ this._map.on('zoomstart', this._unspiderfyZoomStart, this);
+ }
+ //Browsers without zoomAnimation or a big zoom don't fire zoomstart
+ this._map.on('zoomend', this._noanimationUnspiderfy, this);
+
+ if (!L.Browser.touch) {
+ this._map.getRenderer(this);
+ //Needs to happen in the pageload, not after, or animations don't work in webkit
+ // http://stackoverflow.com/questions/8455200/svg-animate-with-dynamically-added-elements
+ //Disable on touch browsers as the animation messes up on a touch zoom and isn't very noticable
+ }
+ },
+
+ _spiderfierOnRemove: function () {
+ this._map.off('click', this._unspiderfyWrapper, this);
+ this._map.off('zoomstart', this._unspiderfyZoomStart, this);
+ this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
+ this._map.off('zoomend', this._noanimationUnspiderfy, this);
+
+ //Ensure that markers are back where they should be
+ // Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane
+ this._noanimationUnspiderfy();
+ },
+
+ //On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated)
+ //This means we can define the animation they do rather than Markers doing an animation to their actual location
+ _unspiderfyZoomStart: function () {
+ if (!this._map) { //May have been removed from the map by a zoomEnd handler
+ return;
+ }
+
+ this._map.on('zoomanim', this._unspiderfyZoomAnim, this);
+ },
+
+ _unspiderfyZoomAnim: function (zoomDetails) {
+ //Wait until the first zoomanim after the user has finished touch-zooming before running the animation
+ if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) {
+ return;
+ }
+
+ this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
+ this._unspiderfy(zoomDetails);
+ },
+
+ _unspiderfyWrapper: function () {
+ /// <summary>_unspiderfy but passes no arguments</summary>
+ this._unspiderfy();
+ },
+
+ _unspiderfy: function (zoomDetails) {
+ if (this._spiderfied) {
+ this._spiderfied.unspiderfy(zoomDetails);
+ }
+ },
+
+ _noanimationUnspiderfy: function () {
+ if (this._spiderfied) {
+ this._spiderfied._noanimationUnspiderfy();
+ }
+ },
+
+ //If the given layer is currently being spiderfied then we unspiderfy it so it isn't on the map anymore etc
+ _unspiderfyLayer: function (layer) {
+ if (layer._spiderLeg) {
+ this._featureGroup.removeLayer(layer);
+
+ if (layer.clusterShow) {
+ layer.clusterShow();
+ }
+ //Position will be fixed up immediately in _animationUnspiderfy
+ if (layer.setZIndexOffset) {
+ layer.setZIndexOffset(0);
+ }
+
+ this._map.removeLayer(layer._spiderLeg);
+ delete layer._spiderLeg;
+ }
+ }
+});