cordova.define("cordova-plugin-googlemaps.MarkerCluster", function(require, exports, module) { /* eslint no-useless-escape: off */ var utils = require('cordova/utils'), common = require('./Common'), event = require('./event'), geomodel = require('./geomodel'), Marker = require('./Marker'), Cluster = require('./Cluster'), spherical = require('./spherical'), Overlay = require('./Overlay'), BaseArrayClass = require('./BaseArrayClass'); /***************************************************************************** * MarkerCluster Class *****************************************************************************/ var exec; var MarkerCluster = function (map, markerClusterOptions, _exec) { exec = _exec; Overlay.call(this, map, markerClusterOptions, 'MarkerCluster', _exec); var self = this; Object.defineProperty(self, 'maxZoomLevel', { value: markerClusterOptions.maxZoomLevel, writable: false }); Object.defineProperty(self, '_markerMap', { enumerable: false, value: {}, writable: false }); Object.defineProperty(self, '_clusterBounds', { enumerable: false, value: new BaseArrayClass(), writable: false }); Object.defineProperty(self, '_geocellBounds', { enumerable: false, value: {}, writable: false }); Object.defineProperty(self, '_clusters', { enumerable: false, value: {}, writable: false }); Object.defineProperty(self, 'debug', { value: markerClusterOptions.debug === true, writable: false }); Object.defineProperty(self, 'MAX_RESOLUTION', { enumerable: false, value: 11, writable: false }); Object.defineProperty(self, 'OUT_OF_RESOLUTION', { enumerable: false, value: 999, writable: false }); Object.defineProperty(self, 'boundsDraw', { value: markerClusterOptions.boundsDraw === true, writable: false }); if (self.boundsDraw) { self.map.addPolygon({ visible: false, points: [ { lat: 0, lng: 0 }, { lat: 0, lng: 0 }, { lat: 0, lng: 0 }, { lat: 0, lng: 0 } ], strokeWidth: 1 }, function (polygon) { self.set('polygon', polygon); }); } self.taskQueue = []; self._stopRequest = false; self._isWorking = false; var icons = markerClusterOptions.icons; if (icons.length > 0 && !icons[0].min) { icons[0].min = 2; } for (var i = 0; i < icons.length; i++) { if (!icons[i]) { continue; } if (icons[i].anchor && typeof icons[i].anchor === 'object' && 'x' in icons[i].anchor && 'y' in icons[i].anchor) { icons[i].anchor = [icons[i].anchor.x, icons[i].anchor.y]; } if (icons[i].infoWindowAnchor && typeof icons[i].infoWindowAnchor === 'object' && 'x' in icons[i].infoWindowAnchor && 'y' in icons[i].infoWindowAnchor) { icons[i].infoWindowAnchor = [icons[i].infoWindowAnchor.x, icons[i].infoWindowAnchor.anchor.y]; } if (icons[i].label && common.isHTMLColorString(icons[i].label.color)) { icons[i].label.color = common.HTMLColor2RGBA(icons[i].label.color); } } Object.defineProperty(self, 'icons', { value: icons, writable: false }); self.addMarker = function (markerOptions, skipRedraw) { markerOptions = common.markerOptionsFilter(markerOptions); var geocell = geomodel.getGeocell(markerOptions.position.lat, markerOptions.position.lng, self.MAX_RESOLUTION + 1); markerOptions._cluster = { isRemoved: false, isAdded: false, geocell: geocell }; var marker = self._createMarker(markerOptions); var markerId = marker.__pgmId.replace(/^.+-/, ''); Object.defineProperty(marker, '__cid', { value: markerId, writable: false }); self._markerMap[markerId] = marker; if (skipRedraw || !self._isReady) { return marker; } self._triggerRedraw({ force: true }); return marker; }; self.addMarkers = function (markers) { var results = []; if (utils.isArray(markers) || Array.isArray(markers)) { for (var i = 0; i < markers.length; i++) { results.push(self.addMarker(markers[i], true)); } if (!self._isReady) { return results; } self._triggerRedraw({ force: true }); } return results; }; map.on(event.CAMERA_MOVE_END, self._onCameraMoved.bind(self)); window.addEventListener('orientationchange', self._onCameraMoved.bind(self)); self.on('cluster_click', self.onClusterClicked); self.on('nextTask', function () { self._isWorking = false; if (self._stopRequest || self._isRemoved || self.taskQueue.length === 0 || !self._isReady) { return; } self._triggerRedraw.call(self); }); // self._triggerRedraw.call(self, { // force: true // }); if (self.debug) { self.debugTimer = setInterval(function () { console.log('self.taskQueue.push = ' + self.taskQueue.length); }, 5000); } return self; }; utils.extend(MarkerCluster, Overlay); MarkerCluster.prototype.onClusterClicked = function (cluster) { if (this._isRemoved) { return null; } var self = this; var polygon = self.get('polygon'); var bounds = cluster.getBounds(); if (self.boundsDraw) { polygon.setPoints([ bounds.southwest, { lat: bounds.northeast.lat, lng: bounds.southwest.lng }, bounds.northeast, { lat: bounds.southwest.lat, lng: bounds.northeast.lng } ]); polygon.setVisible(true); } var zoomLevel = computeZoom(cluster.getBounds(), self.map.getDiv()); zoomLevel += zoomLevel === self.map.get('camera_zoom') ? 1 : 0; self.map.animateCamera({ target: cluster.getBounds().getCenter(), zoom: zoomLevel, duration: 500 }, function () { if (self.boundsDraw) { setTimeout(function () { polygon.setVisible(false); }, 500); } }); }; Object.defineProperty(MarkerCluster.prototype, '_onCameraMoved', { value: function () { var self = this; if (self._isRemoved || self._stopRequest || !self._isReady) { return null; } self._triggerRedraw({ force: false }); }, enumerable: false, writable: false }); MarkerCluster.prototype.remove = function (callback) { var self = this; self._stopRequest = self.hashCode; if (self._isRemoved) { if (typeof callback === 'function') { return; } else { return Promise.resolve(); } } if (self.debug) { clearInterval(self.debugTimer); self.self.debugTimer = undefined; } if (self._isWorking) { setTimeout(arguments.callee.bind(self), 20); return; } var keys; var resolution = self.get('resolution'), activeMarker = self.map.get('active_marker'), deleteClusters = []; self.taskQueue = []; Object.defineProperty(self, '_isRemoved', { value: true, writable: false }); self.trigger('remove'); var activeMarkerId = activeMarker ? activeMarker.getId() : ''; activeMarkerId = activeMarkerId.replace(/^.+\-/, ''); var marker; if (resolution === self.OUT_OF_RESOLUTION) { while (self._clusters[resolution].length > 0) { marker = self._clusters[resolution].shift(); deleteClusters.push(marker.__cid); if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } } } else if (self._clusters[resolution]) { keys = Object.keys(self._clusters[resolution]); keys.forEach(function (geocell) { var cluster = self._clusters[resolution][geocell]; var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; if (noClusterMode) { cluster.getMarkers().forEach(function (marker) { if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } deleteClusters.push(marker.__cid); }); } if (!noClusterMode) { deleteClusters.push(geocell); } cluster.remove(); }); } var resolver = function (resolve, reject) { self.exec.call(self, resolve.bind(self), reject.bind(self), self.getPluginName(), 'remove', [self.getId()], { sync: true, remove: true }); }; var answer; if (typeof callback === 'function') { resolver(callback, self.errorHandler); } else { answer = new Promise(resolver); } keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { self._markerMap[markerId].remove(function() { self._markerMap[markerId].destroy(); self._markerMap[markerId] = undefined; delete self._markerMap[markerId]; }); }); if (self.boundsDraw && self.get('polygon')) { self.get('polygon').remove(); } self.off(); return answer; }; Object.defineProperty(MarkerCluster.prototype, '_removeMarkerById', { enumerable: false, value: function (markerId) { var self = this; if (self._isRemoved) { return null; } //if (markerId.indexOf(self.__cid + '-') === -1) { //} var marker = self._markerMap[markerId]; if (!marker) { return; } var isAdded = marker.get('_cluster').isAdded; var resolutionList = Object.keys(self._clusters); var resolution, geocellKey, cluster; for (var i = 0; i < resolutionList.length; i++) { resolution = parseInt(resolutionList[i], 10); geocellKey = marker.get('_cluster').geocell.substr(0, resolution + 1); if (geocellKey in self._clusters[resolution]) { cluster = self._clusters[resolution][geocellKey]; if (cluster) { cluster.removeMarker(marker); } } } marker.remove(); marker.destroy(); delete self._markerMap[markerId]; if (isAdded) { self.exec.call(self, null, null, self.getPluginName(), 'redrawClusters', [self.getId(), { 'delete': [markerId] }], { sync: true }); } }, writable: false }); MarkerCluster.prototype.getMarkerById = function (markerId) { var self = this; if (self._isRemoved) { return null; } return self._markerMap[markerId]; }; Object.defineProperty(MarkerCluster.prototype, '_getClusterByClusterId', { enumerable: false, value: function (clusterId) { var self = this; if (self._isRemoved) { return null; } var resolution = self.get('resolution'); if (!self._clusters[resolution]) { return null; } return self._clusters[resolution][clusterId]; }, writable: false }); Object.defineProperty(MarkerCluster.prototype, '_triggerRedraw', { enumerable: false, value: function (params) { var self = this; if (self._isRemoved || self._stopRequest || !self._isReady) { return null; } self.taskQueue.push(params); if (self.debug) { console.log('self.taskQueue.push = ' + self.taskQueue.length); } if (self._isRemoved || self._stopRequest || self.taskQueue.length > 1) { return; } if (self.debug) { self._clusterBounds.forEach(function(polyline, cb) { polyline.remove(); cb(); }, function() { self._clusterBounds.empty(); var taskParams = self.taskQueue.pop(); self.taskQueue.length = 0; var visibleRegion = self.map.getVisibleRegion(); self._redraw.call(self, { visibleRegion: visibleRegion, force: taskParams.force }); }); } else { var taskParams = self.taskQueue.pop(); self.taskQueue.length = 0; var visibleRegion = self.map.getVisibleRegion(); self._redraw.call(self, { visibleRegion: visibleRegion, force: taskParams.force }); } }, writable: false }); Object.defineProperty(MarkerCluster.prototype, '_redraw', { enumerable: false, value: function (params) { var self = this; if (self._isRemoved || self._stopRequest || self._isWorking || !self._isReady) { return null; } self._isWorking = true; var currentZoomLevel = self.map.getCameraZoom(), prevResolution = self.get('resolution'); currentZoomLevel = currentZoomLevel < 0 ? 0 : currentZoomLevel; self.set('zoom', currentZoomLevel); var resolution = 1; resolution = self.maxZoomLevel > 3 && currentZoomLevel > 3 ? 2 : resolution; resolution = self.maxZoomLevel > 5 && currentZoomLevel > 5 ? 3 : resolution; resolution = self.maxZoomLevel > 7 && currentZoomLevel > 7 ? 4 : resolution; resolution = self.maxZoomLevel > 9 && currentZoomLevel > 9 ? 5 : resolution; resolution = self.maxZoomLevel > 11 && currentZoomLevel > 11 ? 6 : resolution; resolution = self.maxZoomLevel > 13 && currentZoomLevel > 13 ? 7 : resolution; resolution = self.maxZoomLevel > 15 && currentZoomLevel > 15 ? 8 : resolution; resolution = self.maxZoomLevel > 17 && currentZoomLevel > 17 ? 9 : resolution; resolution = self.maxZoomLevel > 19 && currentZoomLevel > 19 ? 10 : resolution; resolution = self.maxZoomLevel > 21 && currentZoomLevel > 21 ? 11 : resolution; //------------------------------------------------------------------------ // If the current viewport contains the previous viewport, // and also the same resolution, // skip this task except the params.force = true (such as addMarker) //------------------------------------------------------------------------ var cellLen = resolution + 1; var prevSWcell = self.get('prevSWcell'); var prevNEcell = self.get('prevNEcell'); var distanceA = spherical.computeDistanceBetween(params.visibleRegion.farRight, params.visibleRegion.farLeft); var distanceB = spherical.computeDistanceBetween(params.visibleRegion.farRight, params.visibleRegion.nearRight); params.clusterDistance = Math.min(distanceA, distanceB) / 4; var expandedRegion = params.visibleRegion; var swCell = geomodel.getGeocell(expandedRegion.southwest.lat, expandedRegion.southwest.lng, cellLen); var neCell = geomodel.getGeocell(expandedRegion.northeast.lat, expandedRegion.northeast.lng, cellLen); if (!params.force && prevSWcell === swCell && prevNEcell === neCell) { self.trigger('nextTask'); return; } var nwCell = geomodel.getGeocell(expandedRegion.northeast.lat, expandedRegion.southwest.lng, cellLen); var seCell = geomodel.getGeocell(expandedRegion.southwest.lat, expandedRegion.northeast.lng, cellLen); if (currentZoomLevel > self.maxZoomLevel || resolution === 0) { resolution = self.OUT_OF_RESOLUTION; } self.set('resolution', resolution); //console.log('--->prevResolution = ' + prevResolution + ', resolution = ' + resolution); var targetMarkers = []; if (self._isRemoved || self._stopRequest) { self._isWorking = false; return; } //---------------------------------------------------------------- // Remove the clusters that is in outside of the visible region //---------------------------------------------------------------- if (resolution !== self.OUT_OF_RESOLUTION) { self._clusters[resolution] = self._clusters[resolution] || {}; } else { self._clusters[resolution] = self._clusters[resolution] || []; } var deleteClusters = {}; var keys, i, j; var ignoreGeocells = []; var allowGeocells = [swCell, neCell, nwCell, seCell]; var pos, cell; var coners = [ expandedRegion.northeast, { lat: expandedRegion.northeast.lat, lng: expandedRegion.southwest.lng }, expandedRegion.southwest, { lat: expandedRegion.southwest.lat, lng: expandedRegion.northeast.lng }, expandedRegion.northeast ]; for (j = 0; j < 4; j++) { for (i = 0.25; i < 1; i += 0.25) { pos = window.plugin.google.maps.geometry.spherical.interpolate(coners[j], coners[j + 1], i); cell = geomodel.getGeocell(pos.lat, pos.lng, cellLen); if (allowGeocells.indexOf(cell) === -1) { allowGeocells.push(cell); } } } //console.log('---->548'); var activeMarker = self.map.get('active_marker'); var activeMarkerId = activeMarker ? activeMarker.getId() : ''; activeMarkerId = activeMarkerId.replace(/^.+\-/, ''); if (prevResolution === self.OUT_OF_RESOLUTION) { if (resolution === self.OUT_OF_RESOLUTION) { //-------------------------------------- // Just camera move, no zoom changed //-------------------------------------- keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { var marker = self._markerMap[markerId]; if (self._isRemoved || self._stopRequest || !marker.isVisible() || marker.get('_cluster').isRemoved || marker.get('_cluster').isAdded) { return; } var geocell = marker.get('_cluster').geocell.substr(0, cellLen); if (ignoreGeocells.indexOf(geocell) > -1) { return; } if (allowGeocells.indexOf(geocell) > -1) { targetMarkers.push(marker); return; } if (expandedRegion.contains(marker.get('position'))) { allowGeocells.push(geocell); targetMarkers.push(marker); } else { ignoreGeocells.push(geocell); } }); } else { //-------------- // zoom out //-------------- var marker; var markerId; while (self._clusters[self.OUT_OF_RESOLUTION].length > 0) { marker = self._clusters[self.OUT_OF_RESOLUTION].shift(); marker.get('_cluster').isAdded = false; markerId = marker.__cid; deleteClusters[markerId] = 1; if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } if (self.debug) { console.log('---> (js:489)delete:' + markerId); } } keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { var marker = self._markerMap[markerId]; if (self._isRemoved || !marker.isVisible() || self._stopRequest || marker.get('_cluster').isRemoved) { return; } var geocell = marker.get('_cluster').geocell.substr(0, cellLen); if (ignoreGeocells.indexOf(geocell) > -1) { return; } if (allowGeocells.indexOf(geocell) > -1) { targetMarkers.push(marker); return; } if (expandedRegion.contains(marker.get('position'))) { allowGeocells.push(geocell); targetMarkers.push(marker); } else { ignoreGeocells.push(geocell); } }); } } else if (resolution === prevResolution) { //console.log('--->prevResolution(' + prevResolution + ') == resolution(' + resolution + ')'); //-------------------------------------- // Just camera move, no zoom changed //-------------------------------------- keys = Object.keys(self._clusters[prevResolution]); keys.forEach(function (geocell) { if (self._isRemoved || self._stopRequest) { return; } var cluster = self._clusters[prevResolution][geocell]; var bounds = cluster.getBounds(); if (!self._isRemoved && !expandedRegion.contains(bounds.northeast) && !expandedRegion.contains(bounds.southwest)) { ignoreGeocells.push(geocell); if (cluster.getMode() === cluster.NO_CLUSTER_MODE) { cluster.getMarkers().forEach(function (marker) { var markerId = marker.__cid; deleteClusters[markerId] = 1; marker.get('_cluster').isAdded = false; if (self.debug) { console.log('---> (js:534)delete:' + markerId); } if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } }); } else { deleteClusters[geocell] = 1; if (self.debug) { console.log('---> (js:549)delete:' + geocell); } } cluster.remove(); delete self._clusters[resolution][geocell]; } }); keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { var marker = self._markerMap[markerId]; var geocell = marker.get('_cluster').geocell.substr(0, cellLen); if (self._isRemoved || self._stopRequest || marker.get('_cluster').isRemoved || !marker.isVisible() || ignoreGeocells.indexOf(geocell) > -1 || marker.get('_cluster').isAdded) { return; } if (allowGeocells.indexOf(geocell) > -1) { targetMarkers.push(marker); return; } if (expandedRegion.contains(marker.get('position'))) { targetMarkers.push(marker); allowGeocells.push(geocell); } else { ignoreGeocells.push(geocell); marker.get('_cluster').isAdded = false; } }); } else if (prevResolution in self._clusters) { //console.log('--->prevResolution(' + prevResolution + ') != resolution(' + resolution + ')'); if (prevResolution < resolution) { //-------------- // zooming in //-------------- keys = Object.keys(self._clusters[prevResolution]); keys.forEach(function (geocell) { if (self._isRemoved) { return; } var cluster = self._clusters[prevResolution][geocell]; var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; cluster.getMarkers().forEach(function (marker) { if (self._isRemoved || self._stopRequest) { return; } var markerId = marker.__cid; marker.get('_cluster').isAdded = false; //targetMarkers.push(markerOpts); if (noClusterMode) { if (self.debug) { console.log('---> (js:581)delete:' + markerId); } if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } deleteClusters[markerId] = 1; } }); if (!noClusterMode) { if (self.debug) { console.log('---> (js:597)delete:' + geocell); } deleteClusters[geocell] = 1; } cluster.remove(); }); } else { //-------------- // zooming out //-------------- keys = Object.keys(self._clusters[prevResolution]); keys.forEach(function (geocell) { if (self._stopRequest || self._isRemoved) { return; } var cluster = self._clusters[prevResolution][geocell]; var noClusterMode = cluster.getMode() === cluster.NO_CLUSTER_MODE; cluster.getMarkers().forEach(function (marker) { marker.get('_cluster').isAdded = false; var markerId = marker.__cid; if (noClusterMode) { if (self.debug) { console.log('---> (js:614)delete:' + markerId); } if (marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } deleteClusters[markerId] = 1; } }); if (!noClusterMode) { deleteClusters[geocell] = 1; if (self.debug) { console.log('---> (js:632)delete:' + geocell); } } cluster.remove(); geocell = geocell.substr(0, cellLen); }); } keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { if (self._stopRequest || self._isRemoved) { return; } var marker = self._markerMap[markerId]; var geocell = marker.get('_cluster').geocell.substr(0, cellLen); if (marker.get('_cluster').isRemoved || ignoreGeocells.indexOf(geocell) > -1) { marker.get('_cluster').isAdded = false; return; } if (marker.get('_cluster').isAdded) { return; } if (allowGeocells.indexOf(geocell) > -1) { targetMarkers.push(marker); return; } if (expandedRegion.contains(marker.get('position'))) { targetMarkers.push(marker); allowGeocells.push(geocell); } else { ignoreGeocells.push(geocell); marker.get('_cluster').isAdded = false; } }); delete self._clusters[prevResolution]; } else { //console.log('-----> initialize'); keys = Object.keys(self._markerMap); keys.forEach(function (markerId) { if (self._stopRequest || self._isRemoved) { return; } var marker = self._markerMap[markerId]; var geocell = marker.get('_cluster').geocell.substr(0, cellLen); if (marker.get('_cluster').isRemoved || ignoreGeocells.indexOf(geocell) > -1) { return; } if (allowGeocells.indexOf(geocell) > -1) { targetMarkers.push(marker); return; } if (expandedRegion.contains(marker.get('position'))) { targetMarkers.push(marker); allowGeocells.push(geocell); } else { ignoreGeocells.push(geocell); } }); } if (self._stopRequest || self._isRemoved) { self._isWorking = false; return; } if (self.debug) { console.log('targetMarkers = ' + targetMarkers.length); } //-------------------------------- // Pick up markers are containted in the current viewport. //-------------------------------- var new_or_update_clusters = []; if (params.force || resolution == self.OUT_OF_RESOLUTION || resolution !== prevResolution || prevSWcell !== swCell || prevNEcell !== neCell) { self.set('prevSWcell', swCell); self.set('prevNEcell', neCell); if (resolution !== self.OUT_OF_RESOLUTION) { //------------------ // Create clusters //------------------ var prepareClusters = {}; targetMarkers.forEach(function (marker) { if (marker.get('_cluster').isAdded) { if (self.debug) { console.log('isAdded', marker); } return; } var geocell = marker.get('_cluster').geocell.substr(0, resolution + 1); prepareClusters[geocell] = prepareClusters[geocell] || []; prepareClusters[geocell].push(marker); if (marker && marker.__cid === activeMarkerId) { marker.trigger(event.INFO_CLOSE); marker.hideInfoWindow(); } }); if (self.debug) { console.log('prepareClusters = ', prepareClusters, targetMarkers); } //------------------------------------------ // Create/update clusters //------------------------------------------ keys = Object.keys(prepareClusters); var sortedClusters = []; keys.forEach(function (geocell) { var cluster = self.getClusterByGeocellAndResolution(geocell, resolution); cluster.addMarkers(prepareClusters[geocell]); cluster._markerCenter = cluster.getBounds().getCenter(); //cluster._distanceFrom0 = spherical.computeDistanceBetween({lat: 0, lng: 0}, cluster._markerCenter); sortedClusters.push(cluster); }); sortedClusters = sortedClusters.sort(function (a, b) { return a.geocell.localeCompare(b.geocell); }); //------------------------- // Union close clusters //------------------------- var cluster, anotherCluster, distance; var unionedMarkers = []; i = 0; while (i < sortedClusters.length) { cluster = sortedClusters[i]; for (j = i + 1; j < sortedClusters.length; j++) { anotherCluster = sortedClusters[j]; distance = spherical.computeDistanceBetween(cluster._markerCenter, anotherCluster._markerCenter); if (distance < params.clusterDistance) { if (self.debug) { console.log('---> (js:763)delete:' + anotherCluster.geocell); } cluster.addMarkers(anotherCluster.getMarkers()); deleteClusters[anotherCluster.getId()] = 1; delete self._clusters[resolution][anotherCluster.geocell]; self._clusters[resolution][cluster.geocell] = cluster; i = j; } else { break; } } i++; cluster._markerCnt = cluster.getItemLength(); unionedMarkers.push(cluster); } unionedMarkers.forEach(function (cluster) { var icon = self.getClusterIcon(cluster), clusterOpts = { 'count': cluster.getItemLength(), 'position': cluster.getBounds().getCenter(), '__pgmId': cluster.getId() }; if (self.debug) { clusterOpts.geocell = cluster.geocell; } if (icon) { clusterOpts.icon = icon; clusterOpts.isClusterIcon = true; if (cluster.getMode() === cluster.NO_CLUSTER_MODE) { cluster.getMarkers().forEach(function (marker) { var markerId = marker.__cid; deleteClusters[markerId] = 1; if (self.debug) { console.log('---> (js:800)delete:' + markerId); } }); } if (self.debug) { console.log('---> (js:805)add:' + clusterOpts.__pgmId, icon); var geocell = clusterOpts.geocell.substr(0, cellLen); var bounds = self._geocellBounds[geocell] || geomodel.computeBox(geocell); self._geocellBounds[geocell] = bounds; self.map.addPolyline({ color: 'blue', points: [ bounds.southwest, { lat: bounds.southwest.lat, lng: bounds.northeast.lng }, bounds.northeast, { lat: bounds.northeast.lat, lng: bounds.southwest.lng }, bounds.southwest ] }, function (polyline) { self._clusterBounds.push(polyline); }); } cluster.setMode(cluster.CLUSTER_MODE); new_or_update_clusters.push(clusterOpts); return; } cluster.getMarkers().forEach(function (marker) { if (!marker.get('_cluster').isAdded) { return; } var markerId = marker.__cid; delete deleteClusters[markerId]; marker.get('_cluster').isClusterIcon = false; if (self.debug) { console.log('---> (js:831)add:' + markerId + ', isAdded = ' + marker.get('_cluster').isAdded); marker.set('title', markerId, true); } var opts = marker.getOptions(); opts.__pgmId = markerId; new_or_update_clusters.push(opts); }); cluster.setMode(cluster.NO_CLUSTER_MODE); }); } else { cellLen = swCell.length; targetMarkers.forEach(function (marker) { if (marker.get('_cluster').isAdded) { return; } var markerId = marker.__cid; marker.get('_cluster').isClusterIcon = false; if (self.debug) { console.log('---> (js:859)add:' + markerId); marker.set('title', markerId, true); } delete deleteClusters[markerId]; marker.get('_cluster').isAdded = true; var opts = marker.getOptions(); opts.__pgmId = marker.__cid; new_or_update_clusters.push(opts); self._clusters[self.OUT_OF_RESOLUTION].push(marker); }); } } var delete_clusters = Object.keys(deleteClusters); if (self._stopRequest || new_or_update_clusters.length === 0 && delete_clusters.length === 0) { self.trigger('nextTask'); return; } if (self._stopRequest || self._isRemoved) { self._isWorking = false; return; } self.exec.call(self, function (allResults) { var markerIDs = Object.keys(allResults); markerIDs.forEach(function (markerId) { if (!self._markerMap[markerId]) { return; } var marker = self._markerMap[markerId]; var size = allResults[markerId]; if (typeof marker.get('icon') === 'string') { marker.set('icon', { 'url': marker.get('icon'), 'size': size, 'anchor': [size.width / 2, size.height] }, true); } else { var icon = marker.get('icon') || {}; icon.size = icon.size || size; icon.anchor = icon.anchor || [size.width / 2, size.height]; self._markerMap[markerId].set('icon', icon, true); } marker.set('infoWindowAnchor', marker.get('infoWindowAnchor') || [marker.get('icon').size.width / 2, 0], true); }); self.trigger('nextTask'); }, self.errorHandler, self.getPluginName(), 'redrawClusters', [self.getId(), { 'resolution': resolution, 'new_or_update': new_or_update_clusters, 'delete': delete_clusters }], { sync: true }); // console.log({ // 'resolution': resolution, // 'new_or_update': new_or_update_clusters, // 'delete': delete_clusters // }); } }); MarkerCluster.prototype.getClusterIcon = function (cluster) { var self = this, hit, clusterCnt = cluster.getItemLength(); for (var i = 0; i < self.icons.length; i++) { hit = false; if ('min' in self.icons[i]) { if (clusterCnt >= self.icons[i].min) { if ('max' in self.icons[i]) { hit = (clusterCnt <= self.icons[i].max); } else { hit = true; } } } else { if ('max' in self.icons[i]) { hit = (clusterCnt <= self.icons[i].max); } } if (hit) { return self.icons[i]; } } return null; }; MarkerCluster.prototype._createMarker = function (markerOpts) { var self = this; var markerId = self.getId() + '-marker_' + Math.floor(Date.now() * Math.random()); var marker = new Marker(self.getMap(), markerOpts, exec, { className: 'MarkerCluster', __pgmId: markerId }); marker._privateInitialize(markerOpts); delete marker._privateInitialize; // Recalulate geocell if marker position is changed. marker.onThrottled('position_changed', function(ignore, newPosition) { marker.get('_cluster').geocell = geomodel.getGeocell(newPosition.lat, newPosition.lng, self.MAX_RESOLUTION + 1); }, 500); marker.one(marker.getId() + '_remove', function () { self._removeMarkerById(markerId); }); return marker; }; MarkerCluster.prototype.getClusterByGeocellAndResolution = function (geocell, resolution) { var self = this; geocell = geocell.substr(0, resolution + 1); var cluster = self._clusters[resolution][geocell]; if (!cluster) { cluster = new Cluster(geocell, geocell); self._clusters[resolution][geocell] = cluster; } return cluster; }; /* * http://jsfiddle.net/john_s/BHHs8/6/ */ function latRad(lat) { var sin = Math.sin(lat * Math.PI / 180); var radX2 = Math.log((1 + sin) / (1 - sin)) / 2; return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2; } function zoom(mapPx, worldPx, fraction) { return Math.log(mapPx / worldPx / fraction) / Math.LN2; } function computeZoom(bounds, mapDim) { var tileSize = cordova.platformId === 'browser' ? 256 : 512; var ZOOM_MAX = 21; var ne = bounds.northeast; var sw = bounds.southwest; var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI; var lngDiff = ne.lng - sw.lng; var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; var latZoom = zoom(mapDim.offsetHeight, tileSize, latFraction); var lngZoom = zoom(mapDim.offsetWidth, tileSize, lngFraction); return Math.min(latZoom, lngZoom, ZOOM_MAX); } module.exports = MarkerCluster; });