diff --git a/README.md b/README.md
index 4c50d6b..e514c57 100644
--- a/README.md
+++ b/README.md
@@ -7,6 +7,27 @@ A Google Maps JavaScript API v3 library to create and manage per-zoom-level clus
Based on [Marker Clusterer – A Google Maps JavaScript API utility library](https://github.com/googlemaps/js-marker-clusterer) by Luke Mehe (Google Inc.).
+You can find the minified JS file [in the releases tab](https://github.com/Connum/data-layer-clusterer/releases).
+
+## About this fork
+
+While working with the data layer feature was fun because of the simplicity of adding and getting map content via GeoJSON, I soon encountered the problem of too many markers, lines and polygons being displayed when zooming out of the map. I knew about the marker clusterer for normal layers, but it took me browsing through several StackOverflow posts and pages of search results to stumble upon nantunes' approach to data layers.
+
+Seeing that there hadn't been any work done on the project for almost a year, I tried out jesusr's fork, which included some fixes/optimizations, but out-of-the-box it would just throw JS errors in the console. After forking it and getting it to work, I had to find out that just like the version in the initial repo, there was no support for LineStrings or Polygons, which I needed. Also, the URL to the cluster marker icons was no longer valid, so that had to be fixed as well, and so I did.
+
+My current implementation now includes the following changes:
+- Fixed cluster marker image URLs
+- Added SVG versions of the marker images which will be used by default if supported by the browser and falls back to the PNG versions
+- LineStrings and Polygons are being clustered as well, using the center point of their bounding rectangles
+- new option 'setProperty': If set to true, instead of changing the StyleOption attribute 'visible' of the features directly, a boolean property 'in_cluster' (or a configurable property name defined in the constant DataLayerClusterer.CLUSTER_PROPERTY_NAME) is set on the features, which can then be used to toggle visibility (for example in order to take into account other properties for additonal filtering)
+- new option 'recolorSvg': (string) only takes action if SVG is supported and being used: a selector string for an SVG element in the set imagePath that can be used for re-coloring the cluster marker image. This saves requests and prevents the different marker images popping up after loading.
+- new option: 'minimumPolySize': (number) The minimum width or height of the bounding box of a feature (other than type 'Point') in pixels before it is forced into a cluster, even if the cluster ends up containing only this one feature. 0 or false to disable this functionality. Defaults to 50.
+
+To read more and view a working example, see my blog post at www.constantinmedia.com/2016/09/google-maps-javascript-api-v3-handling-large-amounts-of-features-using-clustering-in-data-layers/
+
+## More to come
+- When LineStrings and Polygons are becoming too small according to the minimumPolySize option, display a marker instead for better visibility.
+
## License
Licensed under the Apache License, Version 2.0 (the "License");
diff --git a/bower.json b/bower.json
index f539b13..22934d0 100644
--- a/bower.json
+++ b/bower.json
@@ -1,17 +1,18 @@
{
"name": "data-layer-clusterer",
- "version": "0.7.3",
- "homepage": "https://github.com/nantunes/data-layer-clusterer",
+ "version": "1.0.1",
+ "homepage": "https://github.com/Connum/data-layer-clusterer",
"authors": [
"Nelson Antunes"
],
- "description": "The library creates and manages per-zoom-level clusters large amounts of data layer features. Google API v3.",
+ "description": "The library creates and manages per-zoom-level clusters for large amounts of data layer features. Google API v3.",
"main": "src/datalayerclusterer.js",
"keywords": [
"google",
"maps",
"data",
"layer",
+ "features",
"marker",
"cluster",
"clusterer",
diff --git a/images/m1.png b/images/m1.png
new file mode 100644
index 0000000..329ff52
Binary files /dev/null and b/images/m1.png differ
diff --git a/images/m1.svg b/images/m1.svg
new file mode 100644
index 0000000..c471f17
--- /dev/null
+++ b/images/m1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/m2.png b/images/m2.png
new file mode 100644
index 0000000..b999cbc
Binary files /dev/null and b/images/m2.png differ
diff --git a/images/m2.svg b/images/m2.svg
new file mode 100644
index 0000000..104fb04
--- /dev/null
+++ b/images/m2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/m3.png b/images/m3.png
new file mode 100644
index 0000000..9f30b30
Binary files /dev/null and b/images/m3.png differ
diff --git a/images/m3.svg b/images/m3.svg
new file mode 100644
index 0000000..3b9e7a2
--- /dev/null
+++ b/images/m3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/m4.png b/images/m4.png
new file mode 100644
index 0000000..0d3f826
Binary files /dev/null and b/images/m4.png differ
diff --git a/images/m4.svg b/images/m4.svg
new file mode 100644
index 0000000..26c3dab
--- /dev/null
+++ b/images/m4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/images/m5.png b/images/m5.png
new file mode 100644
index 0000000..61387d2
Binary files /dev/null and b/images/m5.png differ
diff --git a/images/m5.svg b/images/m5.svg
new file mode 100644
index 0000000..4f1c981
--- /dev/null
+++ b/images/m5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/package.json b/package.json
index f539b13..22934d0 100644
--- a/package.json
+++ b/package.json
@@ -1,17 +1,18 @@
{
"name": "data-layer-clusterer",
- "version": "0.7.3",
- "homepage": "https://github.com/nantunes/data-layer-clusterer",
+ "version": "1.0.1",
+ "homepage": "https://github.com/Connum/data-layer-clusterer",
"authors": [
"Nelson Antunes"
],
- "description": "The library creates and manages per-zoom-level clusters large amounts of data layer features. Google API v3.",
+ "description": "The library creates and manages per-zoom-level clusters for large amounts of data layer features. Google API v3.",
"main": "src/datalayerclusterer.js",
"keywords": [
"google",
"maps",
"data",
"layer",
+ "features",
"marker",
"cluster",
"clusterer",
diff --git a/src/datalayerclusterer.js b/src/datalayerclusterer.js
index d49a9d4..432d758 100755
--- a/src/datalayerclusterer.js
+++ b/src/datalayerclusterer.js
@@ -3,9 +3,11 @@
'use strict';
/**
- * @name DataLayerClusterer for Google Maps v3
- * @version version 0.7.2
+ * @name DataLayerClusterer for Google Maps v3 (Connum's Fork)
+ * @version version 1.0.1
* @author Nelson Antunes
+ * @author Jesús R Peinado
+ * @author Constantin Groß
*
* The library creates and manages per-zoom-level clusters for large amounts of
* data layer features.
@@ -33,7 +35,7 @@
*
* @param {google.maps.Map} map The Google map to attach to.
* @param {Object=} optOptions support the following options:
- * 'map': (google.maps.Map) The Google map to attach to.
+ * 'map': (google.maps.Map) The Google map to attach to.
* 'gridSize': (number) The grid size of a cluster in pixels.
* 'maxZoom': (number) The maximum zoom level that a feature can be part of a
* cluster.
@@ -44,6 +46,22 @@
* 'minimumClusterSize': (number) The minimum number of features to be in a
* cluster before the features are hidden and a count
* is shown.
+ * 'minimumPolySize': (number) The minimum width or height of the bounding box
+ * of a feature (other than type 'Point') in pixels before
+ * it is forced into a cluster, even if the cluster ends up
+ * containing only this one feature. 0 or false to disable
+ * this functionality.
+ * 'setProperty': (boolean) when true, the features will not be hidden, but
+ * the property 'in_cluster' (or a configurable property name defined
+ * in the constant DataLayerClusterer.CLUSTER_PROPERTY_NAME)
+ * will be set to a boolean value, indicating whether the feature is
+ * currently being clustered or not. This allows to handle
+ * hiding/showing manually, taking other factors (like filtering)
+ * into account.
+ * 'recolorSVG': (string) only takes action if SVG is being used:
+ * a selector for an SVG element in the set imagePath that can be used
+ * for re-coloring the cluster marker image. This saves requests and
+ * prevents the different marker images popping up after loading.
* 'styles': (object) An object that has style properties:
* 'url': (string) The image url.
* 'height': (number) The image height.
@@ -55,34 +73,68 @@
* @constructor
* @extends google.maps.OverlayView
*/
-function DataLayerClusterer(optOptions) {
+function DataLayerClusterer (optOptions) {
DataLayerClusterer.extend(DataLayerClusterer, google.maps.OverlayView);
+ this.addListener = function (type, callback) {
+ return this._dataLayer.addListener(type, callback);
+ };
+
var options = optOptions || {};
- DataLayerClusterer.extend(DataLayerClusterer, {
- clusters_: [],
- sizes: [53, 56, 66, 78, 90],
- ready_: false,
- map: options.map || null,
- gridSize_: options.gridSize || 60,
- minClusterSize_: options.minimumClusterSize || 2,
- maxZoom_: options.maxZoom || null,
- className_: options.className || 'cluster',
- styles_: options.styles || [],
- imagePath_: options.imagePath || DataLayerClusterer.MARKER_CLUSTER_IMAGE_PATH_,
- imageExtension_: options.imageExtension || DataLayerClusterer.MARKER_CLUSTER_IMAGE_EXTENSION_,
- zoomOnClick_: options.zoomOnClick !== undefined ? options.zoomOnClick : true,
- averageCenter_: options.averageCenter !== undefined ? options.averageCenter : true,
- _dataLayer: new google.maps.Data()
- });
- this.setupStyles_();
- this._dataLayer.setStyle(DataLayerClusterer.HIDDEN_FEATURE);
- if (this.map !== null) {
- this.setMap(this.map);
- }
+
+ this.clusters_ = [];
+ this.sizes = [53, 56, 66, 78, 90];
+ this.colors = ['#008cff','#ffbf00','#ff0000','#ff00ed','#9c00ff'];
+ this.ready_ = false;
+ this.map = options.map || null;
+ this.gridSize_ = options.gridSize || 60;
+ this.minClusterSize_ = options.minimumClusterSize || 2;
+ this.minPolySize_ = options.minimumPolySize || 50;
+ this.setProperty_ = options.setProperty || false;
+ this.maxZoom_ = options.maxZoom || null;
+ this.className_ = options.className || 'cluster';
+ this.styles_ = options.styles || [];
+ this.imagePath_ = options.imagePath || DataLayerClusterer.MARKER_CLUSTER_IMAGE_PATH_;
+ this.imageExtension_ = options.imageExtension || DataLayerClusterer.MARKER_CLUSTER_IMAGE_EXTENSION_;
+ this.zoomOnClick_ = options.zoomOnClick !== undefined ? options.zoomOnClick : true;
+ this.averageCenter_ = options.averageCenter !== undefined ? options.averageCenter : true;
+ this._dataLayer = new google.maps.Data();
+ this.firstIdle_ = true;
+ this.prevBounds_ = null;
+ this.recolorSVG_ = typeof options.recolorSVG !== "undefined" && (typeof options.recolorSVG === "string" || options.recolorSVG instanceof String || options.recolorSVG === false) ? options.recolorSVG : 'g:first-child';
+ this.baseSVG_ = null;
+
+ if (this.recolorSVG_ && this.imageExtension_ == 'svg') {
+ var self = this,
+ xhr = new XMLHttpRequest();
+ xhr.open("GET",/\.svg$/.test(this.imagePath_) ? this.imagePath_ : this.imagePath_ + '1.' + this.imageExtension_);
+ // Following line is just to be on the safe side;
+ // not needed if your server delivers SVG with correct MIME type
+ xhr.overrideMimeType("image/svg+xml");
+ xhr.send("");
+
+ xhr.onreadystatechange = function () {
+ if (this.readyState == 4) {
+ if (this.status == 200) {
+ self.baseSVG_ = {
+ 'document': xhr.responseXML.documentElement,
+ 'colorElement': xhr.responseXML.documentElement.querySelector(self.recolorSVG_)
+ };
+ if (!self.baseSVG_.document || !self.baseSVG_.colorElement) {
+ self.recolorSVG_ = false;
+ }
+ } else {
+ self.recolorSVG_ = false;
+ }
+ self.init_();
+ }
+ };
+ } else this.init_();
}
/* ---- Constants ---- */
+DataLayerClusterer.CLUSTER_PROPERTY_NAME = 'in_cluster';
+
DataLayerClusterer.VISIBLE_FEATURE = {
visible: true
};
@@ -99,7 +151,7 @@ DataLayerClusterer.HIDDEN_FEATURE = {
* @param {bool} v
* @return {void}
*/
-DataLayerClusterer.prototype.setVisible = function(v) {
+DataLayerClusterer.prototype.setVisible = function (v) {
if (!v) {
this.map__ = this.getMap();
google.maps.event.removeListener(this._idle);
@@ -124,7 +176,7 @@ DataLayerClusterer.prototype.setVisible = function(v) {
*
* @return {number} The number of clusters.
*/
-DataLayerClusterer.prototype.getTotalClusters = function() {
+DataLayerClusterer.prototype.getTotalClusters = function () {
return this.clusters_.length;
};
@@ -134,7 +186,7 @@ DataLayerClusterer.prototype.getTotalClusters = function() {
* @param {google.maps.LatLngBounds} bounds The bounds to extend.
* @return {google.maps.LatLngBounds} The extended bounds.
*/
-DataLayerClusterer.prototype.getExtendedBounds = function(bounds) {
+DataLayerClusterer.prototype.getExtendedBounds = function (bounds) {
var projection = this.getProjection();
// Turn the bounds into latlng.
@@ -153,8 +205,8 @@ DataLayerClusterer.prototype.getExtendedBounds = function(bounds) {
blPix.y += this.gridSize_;
// Convert the pixel points back to LatLng
- var ne = projection.fromDivPixelToLatLng(trPix);
- var sw = projection.fromDivPixelToLatLng(blPix);
+ var ne = projection.fromDivPixelToLatLng(trPix),
+ sw = projection.fromDivPixelToLatLng(blPix);
// Extend the bounds to contain the new bounds.
bounds.extend(ne);
@@ -166,7 +218,7 @@ DataLayerClusterer.prototype.getExtendedBounds = function(bounds) {
/**
* Redraws the clusters.
*/
-DataLayerClusterer.prototype.redraw = function() {
+DataLayerClusterer.prototype.redraw = function () {
var oldClusters = this.clusters_.slice();
this.clusters_.length = 0;
@@ -174,12 +226,16 @@ DataLayerClusterer.prototype.redraw = function() {
// Remove the old clusters.
// Do it in a timeout so the other clusters have been drawn first.
- window.requestAnimationFrame(function() {
+ window.requestAnimationFrame(function () {
var oldSize = oldClusters.length;
for (var i = 0; i !== oldSize; ++i) {
oldClusters[i].remove();
}
});
+
+ if (this.map_) {
+ this.prevBounds_ = this.map_.getBounds();
+ }
};
@@ -190,7 +246,7 @@ DataLayerClusterer.prototype.redraw = function() {
*
* @return {boolean} True if zoomOnClick_ is set.
*/
-DataLayerClusterer.prototype.isZoomOnClick = function() {
+DataLayerClusterer.prototype.isZoomOnClick = function () {
return this.zoomOnClick_;
};
@@ -199,7 +255,7 @@ DataLayerClusterer.prototype.isZoomOnClick = function() {
*
* @return {boolean} True if averageCenter_ is set.
*/
-DataLayerClusterer.prototype.isAverageCenter = function() {
+DataLayerClusterer.prototype.isAverageCenter = function () {
return this.averageCenter_;
};
@@ -208,7 +264,7 @@ DataLayerClusterer.prototype.isAverageCenter = function() {
*
* @param {number} maxZoom The max zoom level.
*/
-DataLayerClusterer.prototype.setMaxZoom = function(maxZoom) {
+DataLayerClusterer.prototype.setMaxZoom = function (maxZoom) {
this.maxZoom_ = maxZoom;
};
@@ -217,7 +273,7 @@ DataLayerClusterer.prototype.setMaxZoom = function(maxZoom) {
*
* @return {number} The max zoom level.
*/
-DataLayerClusterer.prototype.getMaxZoom = function() {
+DataLayerClusterer.prototype.getMaxZoom = function () {
return this.maxZoom_;
};
@@ -226,7 +282,7 @@ DataLayerClusterer.prototype.getMaxZoom = function() {
*
* @return {number} The grid size.
*/
-DataLayerClusterer.prototype.getGridSize = function() {
+DataLayerClusterer.prototype.getGridSize = function () {
return this.gridSize_;
};
@@ -235,7 +291,7 @@ DataLayerClusterer.prototype.getGridSize = function() {
*
* @param {number} size The grid size.
*/
-DataLayerClusterer.prototype.setGridSize = function(size) {
+DataLayerClusterer.prototype.setGridSize = function (size) {
this.gridSize_ = size;
};
@@ -244,7 +300,7 @@ DataLayerClusterer.prototype.setGridSize = function(size) {
*
* @return {number} The grid size.
*/
-DataLayerClusterer.prototype.getMinClusterSize = function() {
+DataLayerClusterer.prototype.getMinClusterSize = function () {
return this.minClusterSize_;
};
@@ -253,89 +309,93 @@ DataLayerClusterer.prototype.getMinClusterSize = function() {
*
* @param {number} size The grid size.
*/
-DataLayerClusterer.prototype.setMinClusterSize = function(size) {
+DataLayerClusterer.prototype.setMinClusterSize = function (size) {
this.minClusterSize_ = size;
};
/* ---- google.maps.Data interface ---- */
-DataLayerClusterer.prototype.add = function(feature) {
+DataLayerClusterer.prototype.add = function (feature) {
return this._dataLayer.add(feature);
};
-DataLayerClusterer.prototype.addGeoJson = function(geoJson, options) {
+DataLayerClusterer.prototype.addGeoJson = function (geoJson, options) {
return this._dataLayer.addGeoJson(geoJson, options);
};
-DataLayerClusterer.prototype.contains = function(feature) {
+DataLayerClusterer.prototype.contains = function (feature) {
return this._dataLayer.contains(feature);
};
-DataLayerClusterer.prototype.forEach = function(callback) {
+DataLayerClusterer.prototype.forEach = function (callback) {
return this._dataLayer.forEach(callback);
};
-DataLayerClusterer.prototype.getControlPosition = function() {
+DataLayerClusterer.prototype.getControlPosition = function () {
return this._dataLayer.getControlPosition();
};
-DataLayerClusterer.prototype.getControls = function() {
+DataLayerClusterer.prototype.getControls = function () {
return this._dataLayer.getControls();
};
-DataLayerClusterer.prototype.getDrawingMode = function() {
+DataLayerClusterer.prototype.getDrawingMode = function () {
return this._dataLayer.getDrawingMode();
};
-DataLayerClusterer.prototype.getFeatureById = function(id) {
+DataLayerClusterer.prototype.getFeatureById = function (id) {
return this._dataLayer.getFeatureById(id);
};
-DataLayerClusterer.prototype.getStyle = function() {
+DataLayerClusterer.prototype.getStyle = function () {
return this._dataLayer.getStyle();
};
-DataLayerClusterer.prototype.loadGeoJson = function(url, options, callback) {
+DataLayerClusterer.prototype.loadGeoJson = function (url, options, callback) {
return this._dataLayer.loadGeoJson(url, options, callback);
};
-DataLayerClusterer.prototype.overrideStyle = function(feature, style) {
+DataLayerClusterer.prototype.overrideStyle = function (feature, style) {
return this._dataLayer.overrideStyle(feature, style);
};
-DataLayerClusterer.prototype.remove = function(feature) {
+DataLayerClusterer.prototype.remove = function (feature) {
return this._dataLayer.remove(feature);
};
-DataLayerClusterer.prototype.revertStyle = function(feature) {
+DataLayerClusterer.prototype.revertStyle = function (feature) {
return this._dataLayer.revertStyle(feature);
};
-DataLayerClusterer.prototype.setControlPosition = function(controlPosition) {
+DataLayerClusterer.prototype.setControlPosition = function (controlPosition) {
return this._dataLayer.setControlPosition(controlPosition);
};
-DataLayerClusterer.prototype.setControls = function(controls) {
+DataLayerClusterer.prototype.setControls = function (controls) {
return this._dataLayer.setControls(controls);
};
-DataLayerClusterer.prototype.setDrawingMode = function(drawingMode) {
+DataLayerClusterer.prototype.setDrawingMode = function (drawingMode) {
return this._dataLayer.setDrawingMode(drawingMode);
};
-DataLayerClusterer.prototype.setStyle = function(style) {
- return this._dataLayer.setStyle(style);
+DataLayerClusterer.prototype.setStyle = function (style) {
+ var returnVal = this._dataLayer.setStyle(style);
+ if (this.setProperty_) {
+ this.redraw();
+ }
+ return returnVal;
};
-DataLayerClusterer.prototype.toGeoJson = function(callback) {
+DataLayerClusterer.prototype.toGeoJson = function (callback) {
return this._dataLayer.toGeoJson(callback);
};
/* ---- Private methods ---- */
-DataLayerClusterer.prototype.resetViewport = function() {
+DataLayerClusterer.prototype.resetViewport = function () {
// Remove all the clusters
var csize = this.clusters_.length;
for (var i = 0; i !== csize; ++i) {
@@ -351,9 +411,9 @@ DataLayerClusterer.prototype.resetViewport = function() {
* @param {boolean} ready The state.
* @private
*/
-DataLayerClusterer.prototype.setReady_ = function(ready) {
- if (!this.ready_) {
- this.ready_ = ready;
+DataLayerClusterer.prototype.setReady_ = function (ready) {
+ this.ready_ = ready;
+ if (ready) {
this.createClusters_();
}
};
@@ -366,8 +426,21 @@ DataLayerClusterer.prototype.setReady_ = function(ready) {
* @return {boolean} True if the feature is in the bounds.
* @private
*/
-DataLayerClusterer.prototype.isFeatureInBounds_ = function(f, bounds) {
- return bounds.contains(f.getGeometry().get());
+DataLayerClusterer.prototype.isFeatureInBounds_ = function (f, bounds) {
+ var geom = f.getGeometry(),
+ inBounds = false;
+
+ if (geom.getType() == 'Point') {
+ inBounds = bounds.contains(geom.get());
+ } else {
+ var self = this;
+ geom.getArray().forEach(function (g) {
+ inBounds = g instanceof google.maps.LatLng ? bounds.contains(g) : bounds.contains(self.featureCenter_(g));
+ return !inBounds;
+ });
+ }
+
+ return inBounds;
};
/**
@@ -379,54 +452,105 @@ DataLayerClusterer.prototype.isFeatureInBounds_ = function(f, bounds) {
* @return {number} The distance between the two points in km.
* @private
*/
-DataLayerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) {
+DataLayerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {
if (!p1 || !p2) {
return 0;
}
- var R = 6371; // Radius of the Earth in km
- var dLat = (p2.lat() - p1.lat()) * Math.PI / 180;
- var dLon = (p2.lng() - p1.lng()) * Math.PI / 180;
- var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
- Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
- Math.sin(dLon / 2) * Math.sin(dLon / 2);
- var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
- var d = R * c;
+ var R = 6371, // Radius of the Earth in km
+ dLat = (p2.lat() - p1.lat()) * Math.PI / 180,
+ dLon = (p2.lng() - p1.lng()) * Math.PI / 180,
+ a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
+ Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *
+ Math.sin(dLon / 2) * Math.sin(dLon / 2),
+ c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)),
+ d = R * c;
return d;
};
+/**
+ * Calculates the bounds of a feature
+ *
+ * @private
+ */
+DataLayerClusterer.prototype.featureBounds_ = function (feature, extendBounds) {
+ var geom = feature.getGeometry ? feature.getGeometry() : feature,
+ geom_bounds = extendBounds || new google.maps.LatLngBounds();
+
+ if (geom.getType() == 'Point') {
+ geom_bounds.extend(geom.get());
+ } else {
+ geom.getArray().forEach(function (g) {
+ if (g instanceof google.maps.LatLng) {
+ geom_bounds.extend(g);
+ } else {
+ g.getArray().forEach(function (LatLng) {
+ geom_bounds.extend(LatLng);
+ });
+ }
+ });
+ }
+
+ return geom_bounds;
+};
+
+/**
+ * Calculates the center point of the bounds of a feature
+ *
+ * @private
+ */
+DataLayerClusterer.prototype.featureCenter_ = function (feature) {
+ var geom = feature.getGeometry ? feature.getGeometry() : feature;
+ if (geom.getType() == 'Point') {
+ return geom.get();
+ } else {
+ return this.featureBounds_(feature).getCenter();
+ }
+};
+
/**
* Add a feature to a cluster, or creates a new cluster.
*
* @param {google.maps.Data.Feature} feature The feature to add.
* @private
*/
-DataLayerClusterer.prototype.addToClosestCluster_ = function(feature) {
- var distance = 40000; // Some large number
-
- var pos = feature.getGeometry().get();
+DataLayerClusterer.prototype.addToClosestCluster_ = function (feature) {
+ var distance = 40000, // Some large number
+ pos = this.featureCenter_(feature),
+ cluster,
+ isVisible = true;
+
+ if (this.setProperty_) {
+ var propBefore = feature.getProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME);
+ feature.setProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME, false);
+ var fStyle = this.getStyle(feature);
+ if (typeof fStyle == 'function') fStyle = fStyle(feature);
+ isVisible = typeof fStyle.visible == 'undefined' || fStyle.visible;
+ feature.setProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME, propBefore);
+ }
- var cluster;
+ if (isVisible) {
+ var csize = this.clusters_.length;
- var csize = this.clusters_.length;
- for (var i = 0; i !== csize; ++i) {
- var center = this.clusters_[i].getCenter();
+ for (var i = 0; i !== csize; ++i) {
+ var center = this.clusters_[i].getCenter();
- if (center) {
- var d = this.distanceBetweenPoints_(center, pos);
- if (d < distance) {
- distance = d;
- cluster = this.clusters_[i];
+ if (center) {
+ var d = this.distanceBetweenPoints_(center, pos);
+ if (d < distance) {
+ distance = d;
+ cluster = this.clusters_[i];
+ }
}
}
- }
- if (cluster && cluster.isFeatureInClusterBounds(feature)) {
- cluster.addFeature(feature);
- } else {
- cluster = new FeatureCluster(this);
- cluster.addFeature(feature);
- this.clusters_.push(cluster);
+ if (cluster && cluster.isFeatureInClusterBounds(feature)) {
+ cluster.addFeature(feature);
+ } else {
+ cluster = new FeatureCluster(this);
+ cluster.addFeature(feature);
+ this.clusters_.push(cluster);
+ }
}
};
@@ -435,17 +559,15 @@ DataLayerClusterer.prototype.addToClosestCluster_ = function(feature) {
*
* @private
*/
-DataLayerClusterer.prototype.createClusters_ = function() {
+DataLayerClusterer.prototype.createClusters_ = function () {
if (!this.ready_ || !this.map_) {
return;
}
- var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),
- this.map_.getBounds().getNorthEast());
- var bounds = this.getExtendedBounds(mapBounds);
-
- var self = this;
- this.forEach(function(feature) {
+ var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), this.map_.getBounds().getNorthEast()),
+ bounds = this.getExtendedBounds(mapBounds),
+ self = this;
+ this.forEach(function (feature) {
if (self.isFeatureInBounds_(feature, bounds)) {
self.addToClosestCluster_(feature);
}
@@ -460,7 +582,7 @@ DataLayerClusterer.prototype.createClusters_ = function() {
*
* Adds the data layer to the map and setup the events listeners.
*/
-DataLayerClusterer.prototype.onAdd = function() {
+DataLayerClusterer.prototype.onAdd = function () {
var map = this.getMap();
if (this.map_ !== map) {
@@ -476,21 +598,24 @@ DataLayerClusterer.prototype.onAdd = function() {
// Add the map event listeners
var self = this;
- this._zoomchanged = google.maps.event.addListener(this.map_, 'zoom_changed', function() {
- var zoom = self.map_.getZoom();
-
- if (self.prevZoom_ !== zoom) {
+ this._zoomchanged = google.maps.event.addListener(this.map_, 'zoom_changed', function () {
+ var zoom = self.map_.getZoom(),
+ nothingChanged = (self.prevBounds_ && self.prevBounds_.equals(self.map_.getBounds()));
+ if (self.prevZoom_ !== zoom && nothingChanged !== true) {
self.prevZoom_ = zoom;
self.resetViewport();
}
});
- this._idle = google.maps.event.addListener(this.map_, 'idle', function() {
- self.redraw();
+ this._idle = google.maps.event.addListener(this.map_, 'idle', function () {
+ var nothingChanged = (self.map_ && self.prevZoom_ && self.prevZoom_ === self.map_.getZoom() && self.prevBounds_ && self.prevBounds_.equals(self.map_.getBounds()));
+ if (!self.firstIdle_ && nothingChanged !== true) {
+ self.redraw();
+ }
+ self.firstIdle_ = false;
});
this.setReady_(true);
- this.redraw();
} else {
this.setReady_(false);
}
@@ -501,7 +626,7 @@ DataLayerClusterer.prototype.onAdd = function() {
*
* Removes the data layer from the map and cleans the events listeners.
*/
-DataLayerClusterer.prototype.onRemove = function() {
+DataLayerClusterer.prototype.onRemove = function () {
if (this.map_ !== null) {
if (this._zoomchanged !== null) {
try {
@@ -526,7 +651,7 @@ DataLayerClusterer.prototype.onRemove = function() {
/**
* Empty implementation of the interface method.
*/
-DataLayerClusterer.prototype.draw = function() {};
+DataLayerClusterer.prototype.draw = function () {};
/* ---- Utils ---- */
@@ -538,8 +663,8 @@ DataLayerClusterer.prototype.draw = function() {};
* @param {Object} obj2 The object to extend with.
* @return {Object} The new extended object.
*/
-DataLayerClusterer.extend = function(obj1, obj2) {
- return (function(object) {
+DataLayerClusterer.extend = function (obj1, obj2) {
+ return (function (object) {
for (var property in object.prototype) {
if (object.prototype[property]) {
this.prototype[property] = object.prototype[property];
@@ -558,7 +683,7 @@ DataLayerClusterer.extend = function(obj1, obj2) {
* @constructor
* @ignore
*/
-function FeatureCluster(featureClusterer) {
+function FeatureCluster (featureClusterer) {
this.featureClusterer_ = featureClusterer;
this.map_ = featureClusterer.getMap();
@@ -573,6 +698,8 @@ function FeatureCluster(featureClusterer) {
this.clusterIcon_ = new FeatureClusterIcon(this, featureClusterer.getStyles(),
featureClusterer.getGridSize(), this.classId);
+
+ this.forced_ = false;
}
/**
@@ -581,7 +708,7 @@ function FeatureCluster(featureClusterer) {
* @param {google.maps.Data.Feature} feature The feature to check.
* @return {boolean} True if the feature is already added.
*/
-FeatureCluster.prototype.isFeatureAlreadyAdded = function(feature) {
+FeatureCluster.prototype.isFeatureAlreadyAdded = function (feature) {
if (this.features_.indexOf) {
return this.features_.indexOf(feature) !== -1;
} else {
@@ -603,19 +730,21 @@ FeatureCluster.prototype.isFeatureAlreadyAdded = function(feature) {
* @param {google.maps.Data.Feature} feature The feature to add.
* @return {boolean} True if the feature was added.
*/
-FeatureCluster.prototype.addFeature = function(feature) {
+FeatureCluster.prototype.addFeature = function (feature) {
if (this.isFeatureAlreadyAdded(feature)) {
return false;
}
+ var geom = feature.getGeometry(), centerPoint = this.center_ || this.featureClusterer_.featureCenter_(feature);
+
if (!this.center_) {
- this.center_ = feature.getGeometry().get();
+ this.center_ = centerPoint;
this.calculateBounds_();
} else {
if (this.averageCenter_) {
- var l = this.features_.length + 1;
- var lat = (this.center_.lat() * (l - 1) + feature.getGeometry().get().lat()) / l;
- var lng = (this.center_.lng() * (l - 1) + feature.getGeometry().get().lng()) / l;
+ var l = this.features_.length + 1,
+ lat = (this.center_.lat() * (l - 1) + centerPoint.lat()) / l,
+ lng = (this.center_.lng() * (l - 1) + centerPoint.lng()) / l;
this.center_ = new google.maps.LatLng(lat, lng);
this.calculateBounds_();
}
@@ -624,21 +753,42 @@ FeatureCluster.prototype.addFeature = function(feature) {
this.features_.push(feature);
var len = this.features_.length;
- if (len < this.minClusterSize_) {
- // Min cluster size not reached so show the feature.
- this.featureClusterer_.overrideStyle(feature, DataLayerClusterer.VISIBLE_FEATURE);
+
+ if (len == 1 && !!this.featureClusterer_.minPolySize_ && feature.getGeometry().getType() != 'Point') {
+ var polyMinSize = this.featureClusterer_.minPolySize_,
+ bounds = this.featureClusterer_.featureBounds_(feature),
+ SW = bounds.getSouthWest(),
+ NE = bounds.getNorthEast(),
+ proj = this.map_.getProjection(),
+ swPx = proj.fromLatLngToPoint(SW),
+ nePx = proj.fromLatLngToPoint(NE),
+ pixelWidth = Math.round(Math.abs((nePx.x - swPx.x)* Math.pow(2, this.map_.getZoom()))),
+ pixelHeight = Math.round(Math.abs((swPx.y - nePx.y)* Math.pow(2, this.map_.getZoom())));
+
+ if (pixelWidth < polyMinSize && pixelHeight < polyMinSize) {
+ this.forced_ = true;
+ } else {
+ this.forced_ = false;
+ }
}
- if (len === this.minClusterSize_) {
- // Hide the features that were showing.
- for (var i = 0; i < len; i++) {
- this.featureClusterer_.overrideStyle(this.features_[i], DataLayerClusterer.HIDDEN_FEATURE);
+ if (len < this.minClusterSize_ && !this.forced_) {
+ // Min cluster size not reached so show the feature.
+ if (this.featureClusterer_.setProperty_) {
+ feature.setProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME, false);
+ } else {
+ this.featureClusterer_.overrideStyle(feature, DataLayerClusterer.VISIBLE_FEATURE);
}
}
- if (len >= this.minClusterSize_) {
- for (var j = 0; j < len; j++) {
- this.featureClusterer_.overrideStyle(this.features_[j], DataLayerClusterer.HIDDEN_FEATURE);
+ if (len >= this.minClusterSize_ || this.forced_) {
+ // Hide the features that were showing.
+ for (var i = 0; i < len; i++) {
+ if (this.featureClusterer_.setProperty_) {
+ this.features_[i].setProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME, true);
+ } else {
+ this.featureClusterer_.overrideStyle(this.features_[i], DataLayerClusterer.HIDDEN_FEATURE);
+ }
}
}
@@ -651,7 +801,7 @@ FeatureCluster.prototype.addFeature = function(feature) {
*
* @return {DataLayerClusterer} The associated feature clusterer.
*/
-FeatureCluster.prototype.getDataLayerClusterer = function() {
+FeatureCluster.prototype.getDataLayerClusterer = function () {
return this.featureClusterer_;
};
@@ -660,12 +810,12 @@ FeatureCluster.prototype.getDataLayerClusterer = function() {
*
* @return {google.maps.LatLngBounds} the cluster bounds.
*/
-FeatureCluster.prototype.getBounds = function() {
- var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
+FeatureCluster.prototype.getBounds = function () {
+ var bounds = new google.maps.LatLngBounds(this.center_, this.center_),
+ fsize = this.features_.length;
- var fsize = this.features_.length;
for (var i = 0; i !== fsize; ++i) {
- bounds.extend(this.features_[i].getGeometry().get());
+ bounds = this.featureClusterer_.featureBounds_(this.features_[i], bounds);
}
return bounds;
@@ -674,7 +824,7 @@ FeatureCluster.prototype.getBounds = function() {
/**
* Removes the cluster
*/
-FeatureCluster.prototype.remove = function() {
+FeatureCluster.prototype.remove = function () {
this.clusterIcon_.remove();
this.features_.length = 0;
delete this.features_;
@@ -685,7 +835,7 @@ FeatureCluster.prototype.remove = function() {
*
* @return {number} The cluster size.
*/
-FeatureCluster.prototype.getSize = function() {
+FeatureCluster.prototype.getSize = function () {
return this.features_.length;
};
@@ -694,7 +844,7 @@ FeatureCluster.prototype.getSize = function() {
*
* @return {Array.} The cluster's features.
*/
-FeatureCluster.prototype.getFeatures = function() {
+FeatureCluster.prototype.getFeatures = function () {
return this.features_;
};
@@ -703,7 +853,7 @@ FeatureCluster.prototype.getFeatures = function() {
*
* @return {google.maps.LatLng} The cluster center.
*/
-FeatureCluster.prototype.getCenter = function() {
+FeatureCluster.prototype.getCenter = function () {
return this.center_;
};
@@ -713,7 +863,7 @@ FeatureCluster.prototype.getCenter = function() {
*
* @private
*/
-FeatureCluster.prototype.calculateBounds_ = function() {
+FeatureCluster.prototype.calculateBounds_ = function () {
var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
this.bounds_ = this.featureClusterer_.getExtendedBounds(bounds);
};
@@ -724,8 +874,8 @@ FeatureCluster.prototype.calculateBounds_ = function() {
* @param {google.maps.Data.Feature} feature The feature to check.
* @return {boolean} True if the feature lies in the bounds.
*/
-FeatureCluster.prototype.isFeatureInClusterBounds = function(feature) {
- return this.bounds_.contains(feature.getGeometry().get());
+FeatureCluster.prototype.isFeatureInClusterBounds = function (feature) {
+ return this.bounds_.contains(this.featureClusterer_.featureCenter_(feature));
};
/**
@@ -733,35 +883,39 @@ FeatureCluster.prototype.isFeatureInClusterBounds = function(feature) {
*
* @return {google.maps.Map} The map.
*/
-FeatureCluster.prototype.getMap = function() {
+FeatureCluster.prototype.getMap = function () {
return this.map_;
};
/**
* Updates the cluster icon
*/
-FeatureCluster.prototype.updateIcon = function() {
- var zoom = this.map_.getZoom();
- var mz = this.featureClusterer_.getMaxZoom();
+FeatureCluster.prototype.updateIcon = function () {
+ var zoom = this.map_.getZoom(),
+ mz = this.featureClusterer_.getMaxZoom();
if (mz && zoom > mz) {
// The zoom is greater than our max zoom so show all the features in cluster.
var fsize = this.features_.length;
for (var i = 0; i !== fsize; ++i) {
- this.featureClusterer_.overrideStyle(this.features_[i], DataLayerClusterer.VISIBLE_FEATURE);
+ if (this.featureClusterer_.setProperty_) {
+ this.features_[i].setProperty(DataLayerClusterer.CLUSTER_PROPERTY_NAME, false);
+ } else {
+ this.featureClusterer_.overrideStyle(this.features_[i], DataLayerClusterer.VISIBLE_FEATURE);
+ }
}
return;
}
- if (this.features_.length < this.minClusterSize_) {
+ if (this.features_.length < this.minClusterSize_ && !this.forced_) {
// Min cluster size not yet reached.
this.clusterIcon_.hide();
return;
}
- var numStyles = this.featureClusterer_.getStyles().length;
- var sums = this.featureClusterer_.getCalculator()(this.features_, numStyles);
+ var numStyles = this.featureClusterer_.getStyles().length,
+ sums = this.featureClusterer_.getCalculator()(this.features_, numStyles);
this.clusterIcon_.setSums(sums);
@@ -786,7 +940,7 @@ FeatureCluster.prototype.updateIcon = function() {
* @constructor
* @extends google.maps.OverlayView
*/
-function FeatureClusterIcon(cluster, styles, optpadding, classId) {
+function FeatureClusterIcon (cluster, styles, optpadding, classId) {
DataLayerClusterer.extend(FeatureClusterIcon, google.maps.OverlayView);
this.styles_ = styles;
@@ -807,7 +961,7 @@ function FeatureClusterIcon(cluster, styles, optpadding, classId) {
/**
* Hide the icon.
*/
-FeatureClusterIcon.prototype.hide = function() {
+FeatureClusterIcon.prototype.hide = function () {
if (this.div_) {
this.div_.style.display = 'none';
}
@@ -817,7 +971,7 @@ FeatureClusterIcon.prototype.hide = function() {
/**
* Position and show the icon.
*/
-FeatureClusterIcon.prototype.show = function() {
+FeatureClusterIcon.prototype.show = function () {
if (this.div_) {
var pos = this.getPosFromLatLng_(this.center_);
this.div_.style.cssText = this.createCss(pos);
@@ -829,7 +983,7 @@ FeatureClusterIcon.prototype.show = function() {
/**
* Remove the icon from the map
*/
-FeatureClusterIcon.prototype.remove = function() {
+FeatureClusterIcon.prototype.remove = function () {
this.setMap(null);
};
@@ -838,7 +992,7 @@ FeatureClusterIcon.prototype.remove = function() {
*
* @param {google.maps.LatLng} center The latlng to set as the center.
*/
-FeatureClusterIcon.prototype.setCenter = function(center) {
+FeatureClusterIcon.prototype.setCenter = function (center) {
this.center_ = center;
};
@@ -849,7 +1003,7 @@ FeatureClusterIcon.prototype.setCenter = function(center) {
* Adding the cluster icon to the dom.
* @ignore
*/
-FeatureClusterIcon.prototype.onAdd = function() {
+FeatureClusterIcon.prototype.onAdd = function () {
this.div_ = document.createElement('DIV');
if (this.visible_) {
var pos = this.getPosFromLatLng_(this.center_);
@@ -862,7 +1016,7 @@ FeatureClusterIcon.prototype.onAdd = function() {
panes.overlayMouseTarget.appendChild(this.div_);
var self = this;
- google.maps.event.addDomListener(this.div_, 'click', function() {
+ google.maps.event.addDomListener(this.div_, 'click', function () {
self.triggerClusterClick();
});
};
@@ -871,7 +1025,7 @@ FeatureClusterIcon.prototype.onAdd = function() {
* Draw the icon.
* @ignore
*/
-FeatureClusterIcon.prototype.draw = function() {
+FeatureClusterIcon.prototype.draw = function () {
if (this.visible_) {
var pos = this.getPosFromLatLng_(this.center_);
this.div_.style.top = pos.y + 'px';
@@ -883,7 +1037,7 @@ FeatureClusterIcon.prototype.draw = function() {
* Implementation of the onRemove interface.
* @ignore
*/
-FeatureClusterIcon.prototype.onRemove = function() {
+FeatureClusterIcon.prototype.onRemove = function () {
if (this.div_ && this.div_.parentNode) {
this.hide();
this.div_.parentNode.removeChild(this.div_);
@@ -897,7 +1051,7 @@ FeatureClusterIcon.prototype.onRemove = function() {
/**
* Triggers the clusterclick event and zoom's if the option is set.
*/
-FeatureClusterIcon.prototype.triggerClusterClick = function() {
+FeatureClusterIcon.prototype.triggerClusterClick = function () {
var featureClusterer = this.cluster_.getDataLayerClusterer();
// Trigger the clusterclick event.
@@ -916,7 +1070,7 @@ FeatureClusterIcon.prototype.triggerClusterClick = function() {
* @return {google.maps.Point} The position in pixels.
* @private
*/
-FeatureClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
+FeatureClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {
var pos = this.getProjection().fromLatLngToDivPixel(latlng);
pos.x -= parseInt(this.width_ / 2, 10);
pos.y -= parseInt(this.height_ / 2, 10);
@@ -929,9 +1083,10 @@ FeatureClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
* @param {google.maps.Point} pos The position.
* @return {string} The css style text.
*/
-FeatureClusterIcon.prototype.createCss = function(pos) {
+FeatureClusterIcon.prototype.createCss = function (pos) {
var style = [];
style.push('background-image:url(' + this.url_ + ');');
+ if (this.cluster_.featureClusterer_.recolorSVG_) style.push('background-size: contain;');
var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
style.push('background-position:' + backgroundPosition + ';');
@@ -956,8 +1111,8 @@ FeatureClusterIcon.prototype.createCss = function(pos) {
this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
}
- var txtColor = this.textColor_ ? this.textColor_ : 'black';
- var txtSize = this.textSize_ ? this.textSize_ : 11;
+ var txtColor = this.textColor_ ? this.textColor_ : 'black',
+ txtSize = this.textSize_ ? this.textSize_ : 11;
style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
@@ -968,7 +1123,7 @@ FeatureClusterIcon.prototype.createCss = function(pos) {
/**
* Sets the icon to the the styles.
*/
-FeatureClusterIcon.prototype.useStyle = function() {
+FeatureClusterIcon.prototype.useStyle = function () {
var index = Math.max(0, this.sums_.index - 1);
index = Math.min(this.styles_.length - 1, index);
var style = this.styles_[index];
@@ -988,7 +1143,7 @@ FeatureClusterIcon.prototype.useStyle = function() {
* 'text': (string) The text to display in the icon.
* 'index': (number) The style index of the icon.
*/
-FeatureClusterIcon.prototype.setSums = function(sums) {
+FeatureClusterIcon.prototype.setSums = function (sums) {
this.sums_ = sums;
this.text_ = sums.text;
this.index_ = sums.index;
@@ -1003,7 +1158,6 @@ FeatureClusterIcon.prototype.setSums = function(sums) {
/* ---- To remove soon ---- */
/*
* TODO: Allow the styling using a similar interface than google.map.Data.
- * Use SVG icon by default, remove dependency of google-maps-utility-library-v3.googlecode.com.
*/
/**
@@ -1012,25 +1166,46 @@ FeatureClusterIcon.prototype.setSums = function(sums) {
* @type {string}
*/
DataLayerClusterer.MARKER_CLUSTER_IMAGE_PATH_ =
- 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/images/m';
-DataLayerClusterer.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
+ 'https://cdn.rawgit.com/Connum/data-layer-clusterer/master/images/m';
+DataLayerClusterer.MARKER_CLUSTER_IMAGE_EXTENSION_ = document.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#Image", "1.1") ? 'svg' : 'png';
+
+
+/**
+ * Initialises the clustering when everything is ready
+ *
+ * @private
+ */
+DataLayerClusterer.prototype.init_ = function () {
+ this.setupStyles_();
+
+ if (this.map !== null) {
+ this.setMap(this.map);
+ }
+}
/**
* Sets up the styles object.
*
* @private
*/
-DataLayerClusterer.prototype.setupStyles_ = function() {
+DataLayerClusterer.prototype.setupStyles_ = function () {
if (this.styles_.length) {
return;
}
var ssizes = this.sizes.length;
for (var i = 0; i !== ssizes; ++i) {
+ var thisSize = this.sizes[i],
+ thisColor = this.colors[i],
+ markerUrl = this.imagePath_ + (i + 1) + '.' + this.imageExtension_;
+ if (this.recolorSVG_) {
+ this.baseSVG_.colorElement.style.fill = thisColor;
+ markerUrl = 'data:image/svg+xml;base64,' + btoa(this.baseSVG_.document.outerHTML);
+ }
this.styles_.push({
- url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,
- height: this.sizes[i],
- width: this.sizes[i]
+ url: markerUrl,
+ height: thisSize,
+ width: thisSize
});
}
};
@@ -1040,7 +1215,7 @@ DataLayerClusterer.prototype.setupStyles_ = function() {
*
* @param {Object} styles The style to set.
*/
-DataLayerClusterer.prototype.setStyles = function(styles) {
+DataLayerClusterer.prototype.setStyles = function (styles) {
this.styles_ = styles;
};
@@ -1049,28 +1224,28 @@ DataLayerClusterer.prototype.setStyles = function(styles) {
*
* @return {Object} The styles object.
*/
-DataLayerClusterer.prototype.getStyles = function() {
+DataLayerClusterer.prototype.getStyles = function () {
return this.styles_;
};
/**
* Set the calculator function.
*
- * @param {function(Array, number)} calculator The function to set as the
+ * @param {function (Array, number)} calculator The function to set as the
* calculator. The function should return a object properties:
* 'text' (string) and 'index' (number).
*
*/
-DataLayerClusterer.prototype.setCalculator = function(calculator) {
+DataLayerClusterer.prototype.setCalculator = function (calculator) {
this.calculator_ = calculator;
};
/**
* Get the calculator function.
*
- * @return {function(Array, number)} the calculator function.
+ * @return {function (Array, number)} the calculator function.
*/
-DataLayerClusterer.prototype.getCalculator = function() {
+DataLayerClusterer.prototype.getCalculator = function () {
return this.calculator_;
};
@@ -1082,10 +1257,10 @@ DataLayerClusterer.prototype.getCalculator = function() {
* @return {Object} A object properties: 'text' (string) and 'index' (number).
* @private
*/
-DataLayerClusterer.prototype.calculator_ = function(features, numStyles) {
- var index = 0;
- var count = features.length;
- var dv = count;
+DataLayerClusterer.prototype.calculator_ = function (features, numStyles) {
+ var index = 0,
+ count = features.length,
+ dv = count;
while (dv !== 0) {
dv = parseInt(dv / 10, 10);
index++;