/* eslint-disable */ let __extends = (this && this.__extends) || (function () { let extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b }) || function (d, b) { for (let p in b) if (b.hasOwnProperty(p)) d[p] = b[p] } return function (d, b) { extendStatics(d, b) function __() { this.constructor = d } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()) } })() export let PruneCluster; (function (PruneCluster_1) { let Point = (function () { function Point() { } return Point }()) PruneCluster_1.Point = Point let ClusterObject = (function () { function ClusterObject() { } return ClusterObject }()) PruneCluster_1.ClusterObject = ClusterObject let hashCodeCounter = 1 let maxHashCodeValue = Math.pow(2, 53) - 1 let Marker = (function (_super) { __extends(Marker, _super) function Marker(lat, lng, data, category, weight, filtered) { if (data === void 0) { data = {} } if (weight === void 0) { weight = 1 } if (filtered === void 0) { filtered = false } let _this = _super.call(this) || this _this.data = data _this.position = { lat: +lat, lng: +lng } _this.weight = weight _this.category = category _this.filtered = filtered _this.hashCode = hashCodeCounter++ return _this } Marker.prototype.Move = function (lat, lng) { this.position.lat = +lat this.position.lng = +lng } Marker.prototype.SetData = function (data) { for (let key in data) { this.data[key] = data[key] } } return Marker }(ClusterObject)) PruneCluster_1.Marker = Marker let Cluster = (function (_super) { __extends(Cluster, _super) function Cluster(marker) { let _this = _super.call(this) || this _this.stats = [0, 0, 0, 0, 0, 0, 0, 0] _this.data = {} if (!marker) { _this.hashCode = 1 if (Cluster.ENABLE_MARKERS_LIST) { _this._clusterMarkers = [] } return _this } if (Cluster.ENABLE_MARKERS_LIST) { _this._clusterMarkers = [marker] } _this.lastMarker = marker _this.hashCode = 31 + marker.hashCode _this.population = 1 if (marker.category !== undefined) { _this.stats[marker.category] = 1 } _this.totalWeight = marker.weight _this.position = { lat: marker.position.lat, lng: marker.position.lng } _this.averagePosition = { lat: marker.position.lat, lng: marker.position.lng } return _this } Cluster.prototype.AddMarker = function (marker) { if (Cluster.ENABLE_MARKERS_LIST) { this._clusterMarkers.push(marker) } let h = this.hashCode h = ((h << 5) - h) + marker.hashCode if (h >= maxHashCodeValue) { this.hashCode = h % maxHashCodeValue } else { this.hashCode = h } this.lastMarker = marker let weight = marker.weight, currentTotalWeight = this.totalWeight, newWeight = weight + currentTotalWeight this.averagePosition.lat = (this.averagePosition.lat * currentTotalWeight + marker.position.lat * weight) / newWeight this.averagePosition.lng = (this.averagePosition.lng * currentTotalWeight + marker.position.lng * weight) / newWeight ++this.population this.totalWeight = newWeight if (marker.category !== undefined) { this.stats[marker.category] = (this.stats[marker.category] + 1) || 1 } } Cluster.prototype.Reset = function () { this.hashCode = 1 this.lastMarker = undefined this.population = 0 this.totalWeight = 0 this.stats = [0, 0, 0, 0, 0, 0, 0, 0] if (Cluster.ENABLE_MARKERS_LIST) { this._clusterMarkers = [] } } Cluster.prototype.ComputeBounds = function (cluster) { let proj = cluster.Project(this.position.lat, this.position.lng) let size = cluster.Size let nbX = Math.floor(proj.x / size), nbY = Math.floor(proj.y / size), startX = nbX * size, startY = nbY * size let a = cluster.UnProject(startX, startY), b = cluster.UnProject(startX + size, startY + size) this.bounds = { minLat: b.lat, maxLat: a.lat, minLng: a.lng, maxLng: b.lng } } Cluster.prototype.GetClusterMarkers = function () { return this._clusterMarkers } Cluster.prototype.ApplyCluster = function (newCluster) { this.hashCode = this.hashCode * 41 + newCluster.hashCode * 43 if (this.hashCode > maxHashCodeValue) { this.hashCode = this.hashCode = maxHashCodeValue } let weight = newCluster.totalWeight, currentTotalWeight = this.totalWeight, newWeight = weight + currentTotalWeight this.averagePosition.lat = (this.averagePosition.lat * currentTotalWeight + newCluster.averagePosition.lat * weight) / newWeight this.averagePosition.lng = (this.averagePosition.lng * currentTotalWeight + newCluster.averagePosition.lng * weight) / newWeight this.population += newCluster.population this.totalWeight = newWeight this.bounds.minLat = Math.min(this.bounds.minLat, newCluster.bounds.minLat) this.bounds.minLng = Math.min(this.bounds.minLng, newCluster.bounds.minLng) this.bounds.maxLat = Math.max(this.bounds.maxLat, newCluster.bounds.maxLat) this.bounds.maxLng = Math.max(this.bounds.maxLng, newCluster.bounds.maxLng) for (let category in newCluster.stats) { if (newCluster.stats.hasOwnProperty(category)) { if (this.stats.hasOwnProperty(category)) { this.stats[category] += newCluster.stats[category] } else { this.stats[category] = newCluster.stats[category] } } } if (Cluster.ENABLE_MARKERS_LIST) { this._clusterMarkers = this._clusterMarkers.concat(newCluster.GetClusterMarkers()) } } Cluster.ENABLE_MARKERS_LIST = false return Cluster }(ClusterObject)) PruneCluster_1.Cluster = Cluster function checkPositionInsideBounds(a, b) { return (a.lat >= b.minLat && a.lat <= b.maxLat) && a.lng >= b.minLng && a.lng <= b.maxLng } function insertionSort(list) { for (let i = 1, j, tmp, tmpLng, length = list.length; i < length; ++i) { tmp = list[i] tmpLng = tmp.position.lng for (j = i - 1; j >= 0 && list[j].position.lng > tmpLng; --j) { list[j + 1] = list[j] } list[j + 1] = tmp } } function shouldUseInsertionSort(total, nbChanges) { if (nbChanges > 300) { return false } else { return (nbChanges / total) < 0.2 } } let PruneCluster = (function () { function PruneCluster() { this._markers = [] this._nbChanges = 0 this._clusters = [] this.Size = 166 this.ViewPadding = 0.2 } PruneCluster.prototype.RegisterMarker = function (marker) { if (marker._removeFlag) { delete marker._removeFlag } this._markers.push(marker) this._nbChanges += 1 } PruneCluster.prototype.RegisterMarkers = function (markers) { let _this = this markers.forEach(function (marker) { _this.RegisterMarker(marker) }) } PruneCluster.prototype._sortMarkers = function () { let markers = this._markers, length = markers.length if (this._nbChanges && !shouldUseInsertionSort(length, this._nbChanges)) { this._markers.sort(function (a, b) { return a.position.lng - b.position.lng }) } else { insertionSort(markers) } this._nbChanges = 0 } PruneCluster.prototype._sortClusters = function () { insertionSort(this._clusters) } PruneCluster.prototype._indexLowerBoundLng = function (lng) { let markers = this._markers, it, step, first = 0, count = markers.length while (count > 0) { step = Math.floor(count / 2) it = first + step if (markers[it].position.lng < lng) { first = ++it count -= step + 1 } else { count = step } } return first } PruneCluster.prototype._resetClusterViews = function () { for (let i = 0, l = this._clusters.length; i < l; ++i) { let cluster = this._clusters[i] cluster.Reset() cluster.ComputeBounds(this) } } PruneCluster.prototype.ProcessView = function (bounds) { let heightBuffer = Math.abs(bounds.maxLat - bounds.minLat) * this.ViewPadding, widthBuffer = Math.abs(bounds.maxLng - bounds.minLng) * this.ViewPadding let extendedBounds = { minLat: bounds.minLat - heightBuffer - heightBuffer, maxLat: bounds.maxLat + heightBuffer + heightBuffer, minLng: bounds.minLng - widthBuffer - widthBuffer, maxLng: bounds.maxLng + widthBuffer + widthBuffer } this._sortMarkers() this._resetClusterViews() let firstIndex = this._indexLowerBoundLng(extendedBounds.minLng) let markers = this._markers, clusters = this._clusters let workingClusterList = clusters.slice(0) for (let i = firstIndex, l = markers.length; i < l; ++i) { let marker = markers[i], markerPosition = marker.position if (markerPosition.lng > extendedBounds.maxLng) { break } if (markerPosition.lat > extendedBounds.minLat && markerPosition.lat < extendedBounds.maxLat && !marker.filtered) { let clusterFound = false, cluster for (let j = 0, ll = workingClusterList.length; j < ll; ++j) { cluster = workingClusterList[j] if (cluster.bounds.maxLng < marker.position.lng) { workingClusterList.splice(j, 1) --j --ll continue } if (checkPositionInsideBounds(markerPosition, cluster.bounds)) { cluster.AddMarker(marker) clusterFound = true break } } if (!clusterFound) { cluster = new Cluster(marker) cluster.ComputeBounds(this) clusters.push(cluster) workingClusterList.push(cluster) } } } let newClustersList = [] for (i = 0, l = clusters.length; i < l; ++i) { let cluster = clusters[i] if (cluster.population > 0) { newClustersList.push(cluster) } } this._clusters = newClustersList this._sortClusters() return this._clusters } PruneCluster.prototype.RemoveMarkers = function (markers) { if (!markers) { this._markers = [] return } for (let i = 0, l = markers.length; i < l; ++i) { markers[i]._removeFlag = true } let newMarkersList = [] for (i = 0, l = this._markers.length; i < l; ++i) { if (!this._markers[i]._removeFlag) { newMarkersList.push(this._markers[i]) } else { delete this._markers[i]._removeFlag } } this._markers = newMarkersList } PruneCluster.prototype.FindMarkersInArea = function (area) { let aMinLat = area.minLat, aMaxLat = area.maxLat, aMinLng = area.minLng, aMaxLng = area.maxLng, markers = this._markers, result = [] let firstIndex = this._indexLowerBoundLng(aMinLng) for (let i = firstIndex, l = markers.length; i < l; ++i) { let pos = markers[i].position if (pos.lng > aMaxLng) { break } if (pos.lat >= aMinLat && pos.lat <= aMaxLat && pos.lng >= aMinLng) { result.push(markers[i]) } } return result } PruneCluster.prototype.ComputeBounds = function (markers, withFiltered) { if (withFiltered === void 0) { withFiltered = true } if (!markers || !markers.length) { return null } let rMinLat = Number.MAX_VALUE, rMaxLat = -Number.MAX_VALUE, rMinLng = Number.MAX_VALUE, rMaxLng = -Number.MAX_VALUE for (let i = 0, l = markers.length; i < l; ++i) { if (!withFiltered && markers[i].filtered) { continue } let pos = markers[i].position if (pos.lat < rMinLat) { rMinLat = pos.lat } if (pos.lat > rMaxLat) { rMaxLat = pos.lat } if (pos.lng < rMinLng) { rMinLng = pos.lng } if (pos.lng > rMaxLng) { rMaxLng = pos.lng } } return { minLat: rMinLat, maxLat: rMaxLat, minLng: rMinLng, maxLng: rMaxLng } } PruneCluster.prototype.FindMarkersBoundsInArea = function (area) { return this.ComputeBounds(this.FindMarkersInArea(area)) } PruneCluster.prototype.ComputeGlobalBounds = function (withFiltered) { if (withFiltered === void 0) { withFiltered = true } return this.ComputeBounds(this._markers, withFiltered) } PruneCluster.prototype.GetMarkers = function () { return this._markers } PruneCluster.prototype.GetPopulation = function () { return this._markers.length } PruneCluster.prototype.ResetClusters = function () { this._clusters = [] } return PruneCluster }()) PruneCluster_1.PruneCluster = PruneCluster })(PruneCluster || (PruneCluster = {})) // let PruneCluster; // (function (PruneCluster) { // })(PruneCluster || (PruneCluster = {})); export let PruneClusterForLeaflet = (L.Layer ? L.Layer : L.Class).extend({ initialize: function (size, clusterMargin) { let _this = this if (size === void 0) { size = 120 } if (clusterMargin === void 0) { clusterMargin = 20 } this.Cluster = new PruneCluster.PruneCluster() this.Cluster.Size = size this.clusterMargin = Math.min(clusterMargin, size / 4) this.Cluster.Project = function (lat, lng) { return _this._map.project(new L.LatLng(lat, lng), Math.floor(_this._map.getZoom())) } this.Cluster.UnProject = function (x, y) { return _this._map.unproject(new L.Point(x, y), Math.floor(_this._map.getZoom())) } this._objectsOnMap = [] this.spiderfier = new PruneClusterLeafletSpiderfier(this) this._hardMove = false this._resetIcons = false this._removeTimeoutId = 0 this._markersRemoveListTimeout = [] }, RegisterMarker: function (marker) { this.Cluster.RegisterMarker(marker) }, RegisterMarkers: function (markers) { this.Cluster.RegisterMarkers(markers) }, RemoveMarkers: function (markers) { this.Cluster.RemoveMarkers(markers) }, BuildLeafletCluster: function (cluster, position) { let _this = this let m = new L.Marker(position, { icon: this.BuildLeafletClusterIcon(cluster) }) m._leafletClusterBounds = cluster.bounds m.on('click', function () { let cbounds = m._leafletClusterBounds let markersArea = _this.Cluster.FindMarkersInArea(cbounds) let b = _this.Cluster.ComputeBounds(markersArea) if (b) { let bounds = new L.LatLngBounds(new L.LatLng(b.minLat, b.maxLng), new L.LatLng(b.maxLat, b.minLng)) let zoomLevelBefore = _this._map.getZoom(), zoomLevelAfter = _this._map.getBoundsZoom(bounds, false, new L.Point(20, 20)) if (zoomLevelAfter === zoomLevelBefore) { let filteredBounds = [] for (let i = 0, l = _this._objectsOnMap.length; i < l; ++i) { let o = _this._objectsOnMap[i] if (o.data._leafletMarker !== m) { if (o.bounds.minLat >= cbounds.minLat && o.bounds.maxLat <= cbounds.maxLat && o.bounds.minLng >= cbounds.minLng && o.bounds.maxLng <= cbounds.maxLng) { filteredBounds.push(o.bounds) } } } if (filteredBounds.length > 0) { let newMarkersArea = [] let ll = filteredBounds.length for (i = 0, l = markersArea.length; i < l; ++i) { let markerPos = markersArea[i].position let isFiltered = false for (let j = 0; j < ll; ++j) { let currentFilteredBounds = filteredBounds[j] if (markerPos.lat >= currentFilteredBounds.minLat && markerPos.lat <= currentFilteredBounds.maxLat && markerPos.lng >= currentFilteredBounds.minLng && markerPos.lng <= currentFilteredBounds.maxLng) { isFiltered = true break } } if (!isFiltered) { newMarkersArea.push(markersArea[i]) } } markersArea = newMarkersArea } if (markersArea.length < 200 || zoomLevelAfter >= _this._map.getMaxZoom()) { _this._map.fire('overlappingmarkers', { cluster: _this, markers: markersArea, center: m.getLatLng(), marker: m }) } else { zoomLevelAfter++ } _this._map.setView(m.getLatLng(), zoomLevelAfter) } else { _this._map.fitBounds(bounds) } } }) return m }, BuildLeafletClusterIcon: function (cluster) { let c = 'prunecluster prunecluster-' let iconSize = 38 let maxPopulation = this.Cluster.GetPopulation() if (cluster.population < Math.max(10, maxPopulation * 0.01)) { c += 'small' } else if (cluster.population < Math.max(100, maxPopulation * 0.05)) { c += 'medium' iconSize = 40 } else { c += 'large' iconSize = 44 } return new L.DivIcon({ html: '