cordova-plugin-googlemaps
Version:
Google Maps native SDK for Android and iOS, and Google Maps JavaScript API v3 for browser.
1,241 lines (1,117 loc) • 38.5 kB
JavaScript
/* 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;
}
var link;
if (typeof icons[i] === 'string') {
if (icons[i].indexOf('://') === -1 &&
icons[i].indexOf('.') === 0) {
link = document.createElement('a');
link.href = icons[i];
icons[i] = link.protocol + '//' + link.host + link.pathname + link.search;
link = undefined;
}
} else if (typeof icons[i].url === 'string') {
if (icons[i].url.indexOf('://') === -1 &&
icons[i].url.indexOf('.') === 0) {
link = document.createElement('a');
link.href = icons[i].url;
icons[i].url = link.protocol + '//' + link.host + link.pathname + link.search;
link = undefined;
}
}
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
};
if (typeof markerOptions.icon === 'string') {
if (markerOptions.icon.indexOf('://') === -1 &&
markerOptions.icon.indexOf('.') === 0) {
link = document.createElement('a');
link.href = markerOptions.icon;
markerOptions.icon = link.protocol + '//' + link.host + link.pathname + link.search;
link = undefined;
}
} else if (typeof markerOptions.icon === 'object' && typeof markerOptions.icon.url === 'string') {
if (markerOptions.icon.url.indexOf('://') === -1 &&
markerOptions.icon.url.indexOf('.') === 0) {
link = document.createElement('a');
link.href = markerOptions.icon.url;
markerOptions.icon.url = link.protocol + '//' + link.host + link.pathname + link.search;
link = undefined;
}
}
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) {
try {
self._markerMap[markerId].remove();
} catch(e) {
// ignore
}
});
keys.forEach(function (markerId) {
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;