-
-
Save domenicomonaco/020fe8ed96a21697f9f5414a1f2368da to your computer and use it in GitHub Desktop.
Google Maps V3 Cluster Marker With Minimum Cluster Size
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==ClosureCompiler== | |
// @compilation_level ADVANCED_OPTIMIZATIONS | |
// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/google_maps_api_v3.js | |
// ==/ClosureCompiler== | |
/** | |
* @author Michael Bianco <http://mabblog.com> | |
* @fileoverview Added minimum cluster size option | |
**/ | |
/** | |
* @name MarkerClusterer for Google Maps v3 | |
* @version version 1.0 | |
* @author Luke Mahe | |
* @fileoverview | |
* The library creates and manages per-zoom-level clusters for large amounts of | |
* markers. | |
* <br/> | |
* This is a v3 implementation of the | |
* <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" | |
* >v2 MarkerClusterer</a>. | |
*/ | |
/** | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
/** | |
* A Marker Clusterer that clusters markers. | |
* | |
* @param {google.maps.Map} map The Google map to attach to. | |
* @param {Array.<google.maps.Marker>} opt_markers Optional markers to add to | |
* the cluster. | |
* @param {Object} opt_options support the following options: | |
* 'gridSize': (number) The grid size of a cluster in pixels. | |
* 'maxZoom': (number) The maximum zoom level that a marker can be part of a | |
* cluster. | |
* 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a | |
* cluster is to zoom into it. | |
* 'styles': (object) An object that has style properties: | |
* 'url': (string) The image url. | |
* 'height': (number) The image height. | |
* 'width': (number) The image width. | |
* 'anchor': (Array) The anchor position of the label text. | |
* 'textColor': (string) The text color. | |
* 'textSize': (number) The text size. | |
* @constructor | |
* @extends google.maps.OverlayView | |
*/ | |
function MarkerClusterer(map, opt_markers, opt_options) { | |
// MarkerClusterer implements google.maps.OverlayView interface. We use the | |
// extend function to extend MarkerClusterer with google.maps.OverlayView | |
// because it might not always be available when the code is defined so we | |
// look for it at the last possible moment. If it doesn't exist now then | |
// there is no point going ahead :) | |
this.extend(MarkerClusterer, google.maps.OverlayView); | |
this.map_ = map; | |
this.markers_ = []; | |
this.clusters_ = []; | |
this.sizes = [53, 56, 66, 78, 90]; | |
this.styles_ = []; | |
this.ready_ = false; | |
var options = opt_options || {}; | |
this.gridSize_ = options['gridSize'] || 60; | |
this.maxZoom_ = options['maxZoom'] || null; | |
this.styles_ = options['styles'] || []; | |
this.imagePath_ = options['imagePath'] || | |
this.MARKER_CLUSTER_IMAGE_PATH_; | |
this.imageExtension_ = options['imageExtension'] || | |
this.MARKER_CLUSTER_IMAGE_EXTENSION_; | |
this.zoomOnClick_ = options['zoomOnClick'] || true; | |
this.minimumClusterSize = options['minimumClusterSize'] || 1; | |
this.setupStyles_(); | |
this.setMap(map); | |
this.prevZoom_ = this.map_.getZoom(); | |
// Add the map event listeners | |
var that = this; | |
google.maps.event.addListener(this.map_, 'zoom_changed', function() { | |
var maxZoom = that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom; | |
var zoom = that.map_.getZoom(); | |
if (zoom < 0 || zoom > maxZoom) { | |
return; | |
} | |
if (that.prevZoom_ != zoom) { | |
that.prevZoom_ = that.map_.getZoom(); | |
that.resetViewport(); | |
} | |
}); | |
google.maps.event.addListener(this.map_, 'idle', function() { | |
that.redraw(); | |
}); | |
// Finally, add the markers | |
if (opt_markers && opt_markers.length) { | |
this.addMarkers(opt_markers, false); | |
} | |
} | |
/** | |
* The marker cluster image path. | |
* | |
* @type {string} | |
* @private | |
*/ | |
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = | |
'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + | |
'images/m'; | |
/** | |
* The marker cluster image path. | |
* | |
* @type {string} | |
* @private | |
*/ | |
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; | |
/** | |
* Extends a objects prototype by anothers. | |
* | |
* @param {Object} obj1 The object to be extended. | |
* @param {Object} obj2 The object to extend with. | |
* @return {Object} The new extended object. | |
* @ignore | |
*/ | |
MarkerClusterer.prototype.extend = function(obj1, obj2) { | |
return (function(object) { | |
for (property in object.prototype) { | |
this.prototype[property] = object.prototype[property]; | |
} | |
return this; | |
}).apply(obj1, [obj2]); | |
}; | |
/** | |
* Implementaion of the interface method. | |
* @ignore | |
*/ | |
MarkerClusterer.prototype.onAdd = function() { | |
this.setReady_(true); | |
}; | |
/** | |
* Implementation of the interface. | |
* @ignore | |
*/ | |
MarkerClusterer.prototype.idle = function() {}; | |
/** | |
* Implementation of the interface. | |
* @ignore | |
*/ | |
MarkerClusterer.prototype.draw = function() {}; | |
/** | |
* Sets up the styles object. | |
* | |
* @private | |
*/ | |
MarkerClusterer.prototype.setupStyles_ = function() { | |
for (var i = 0, size; size = this.sizes[i]; i++) { | |
this.styles_.push({ | |
url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, | |
height: size, | |
width: size | |
}); | |
} | |
}; | |
/** | |
* Sets the styles. | |
* | |
* @param {Object} styles The style to set. | |
*/ | |
MarkerClusterer.prototype.setStyles = function(styles) { | |
this.styles_ = styles; | |
}; | |
/** | |
* Gets the styles. | |
* | |
* @return {Object} The styles object. | |
*/ | |
MarkerClusterer.prototype.getStyles = function() { | |
return this.styles_; | |
}; | |
/** | |
* Whether zoom on click is set. | |
* | |
* @return {boolean} True if zoomOnClick_ is set. | |
*/ | |
MarkerClusterer.prototype.isZoomOnClick = function() { | |
return this.zoomOnClick_; | |
}; | |
/** | |
* Returns the array of markers in the clusterer. | |
* | |
* @return {Array.<google.maps.Marker>} The markers. | |
*/ | |
MarkerClusterer.prototype.getMarkers = function() { | |
return this.markers_; | |
}; | |
/** | |
* Returns the array of markers in the clusterer. | |
* | |
* @return {Array.<google.maps.Marker>} The number of markers. | |
*/ | |
MarkerClusterer.prototype.getTotalMarkers = function() { | |
return this.markers_; | |
}; | |
/** | |
* Sets the max zoom for the clusterer. | |
* | |
* @param {number} maxZoom The max zoom level. | |
*/ | |
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { | |
this.maxZoom_ = maxZoom; | |
}; | |
/** | |
* Gets the max zoom for the clusterer. | |
* | |
* @return {number} The max zoom level. | |
*/ | |
MarkerClusterer.prototype.getMaxZoom = function() { | |
return this.maxZoom_ || this.map_.mapTypes[this.map_.getMapTypeId()].maxZoom; | |
}; | |
/** | |
* The function for calculating the cluster icon image. | |
* | |
* @param {Array.<google.maps.Marker>} markers The markers in the clusterer. | |
* @param {number} numStyles The number of styles available. | |
* @return {Object} A object properties: 'text' (string) and 'index' (number). | |
* @private | |
*/ | |
MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { | |
var index = 0; | |
var count = markers.length; | |
var dv = count; | |
while (dv !== 0) { | |
dv = parseInt(dv / 10, 10); | |
index++; | |
} | |
index = Math.min(index, numStyles); | |
return { | |
text: count, | |
index: index | |
}; | |
}; | |
/** | |
* Set the calculator function. | |
* | |
* @param {function(Array, number)} calculator The function to set as the | |
* calculator. The function should return a object properties: | |
* 'text' (string) and 'index' (number). | |
* | |
*/ | |
MarkerClusterer.prototype.setCalculator = function(calculator) { | |
this.calculator_ = calculator; | |
}; | |
/** | |
* Get the calculator function. | |
* | |
* @return {function(Array, number)} the calculator function. | |
*/ | |
MarkerClusterer.prototype.getCalculator = function() { | |
return this.calculator_; | |
}; | |
/** | |
* Add an array of markers to the clusterer. | |
* | |
* @param {Array.<google.maps.Marker>} markers The markers to add. | |
* @param {boolean} opt_nodraw Whether to redraw the clusters. | |
*/ | |
MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { | |
for (var i = 0, marker; marker = markers[i]; i++) { | |
this.pushMarkerTo_(marker); | |
} | |
if (!opt_nodraw) { | |
this.redraw(); | |
} | |
}; | |
/** | |
* Pushes a marker to the clusterer. | |
* | |
* @param {google.maps.Marker} marker The marker to add. | |
* @private | |
*/ | |
MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { | |
marker.setVisible(false); | |
marker.setMap(null); | |
marker.isAdded = false; | |
if (marker['draggable']) { | |
// If the marker is draggable add a listener so we update the clusters on | |
// the drag end. | |
var that = this; | |
google.maps.event.addListener(marker, 'dragend', function() { | |
marker.isAdded = false; | |
that.resetViewport(); | |
that.redraw(); | |
}); | |
} | |
this.markers_.push(marker); | |
}; | |
/** | |
* Adds a marker to the clusterer and redraws if needed. | |
* | |
* @param {google.maps.Marker} marker The marker to add. | |
* @param {boolean} opt_nodraw Whether to redraw the clusters. | |
*/ | |
MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { | |
this.pushMarkerTo_(marker); | |
if (!opt_nodraw) { | |
this.redraw(); | |
} | |
}; | |
/** | |
* Remove a marker from the cluster. | |
* | |
* @param {google.maps.Marker} marker The marker to remove. | |
* @return {boolean} True if the marker was removed. | |
*/ | |
MarkerClusterer.prototype.removeMarker = function(marker) { | |
var index = -1; | |
if (this.markers_.indexOf) { | |
index = this.markers_.indexOf(marker); | |
} else { | |
for (var i = 0, m; m = this.markers_[i]; i++) { | |
if (m == marker) { | |
index = i; | |
continue; | |
} | |
} | |
} | |
if (index == -1) { | |
// Marker is not in our list of markers. | |
return false; | |
} | |
this.markers_.splice(index, 1); | |
marker.setVisible(false); | |
marker.setMap(null); | |
this.resetViewport(); | |
this.redraw(); | |
return true; | |
}; | |
/** | |
* Sets the clusterer's ready state. | |
* | |
* @param {boolean} ready The state. | |
* @private | |
*/ | |
MarkerClusterer.prototype.setReady_ = function(ready) { | |
if (!this.ready_) { | |
this.ready_ = ready; | |
this.createClusters_(); | |
} | |
}; | |
/** | |
* Returns the number of clusters in the clusterer. | |
* | |
* @return {number} The number of clusters. | |
*/ | |
MarkerClusterer.prototype.getTotalClusters = function() { | |
return this.clusters_.length; | |
}; | |
/** | |
* Returns the google map that the clusterer is associated with. | |
* | |
* @return {google.maps.Map} The map. | |
*/ | |
MarkerClusterer.prototype.getMap = function() { | |
return this.map_; | |
}; | |
/** | |
* Sets the google map that the clusterer is associated with. | |
* | |
* @param {google.maps.Map} map The map. | |
*/ | |
MarkerClusterer.prototype.setMap = function(map) { | |
this.map_ = map; | |
}; | |
/** | |
* Returns the size of the grid. | |
* | |
* @return {number} The grid size. | |
*/ | |
MarkerClusterer.prototype.getGridSize = function() { | |
return this.gridSize_; | |
}; | |
/** | |
* Returns the size of the grid. | |
* | |
* @param {number} size The grid size. | |
*/ | |
MarkerClusterer.prototype.setGridSize = function(size) { | |
this.gridSize_ = size; | |
}; | |
/** | |
* Extends a bounds object by the grid size. | |
* | |
* @param {google.maps.LatLngBounds} bounds The bounds to extend. | |
* @return {google.maps.LatLngBounds} The extended bounds. | |
*/ | |
MarkerClusterer.prototype.getExtendedBounds = function(bounds) { | |
var projection = this.getProjection(); | |
// Turn the bounds into latlng. | |
var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), | |
bounds.getNorthEast().lng()); | |
var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), | |
bounds.getSouthWest().lng()); | |
// Convert the points to pixels and the extend out by the grid size. | |
var trPix = projection.fromLatLngToDivPixel(tr); | |
trPix.x += this.gridSize_; | |
trPix.y -= this.gridSize_; | |
var blPix = projection.fromLatLngToDivPixel(bl); | |
blPix.x -= this.gridSize_; | |
blPix.y += this.gridSize_; | |
// Convert the pixel points back to LatLng | |
var ne = projection.fromDivPixelToLatLng(trPix); | |
var sw = projection.fromDivPixelToLatLng(blPix); | |
// Extend the bounds to contain the new bounds. | |
bounds.extend(ne); | |
bounds.extend(sw); | |
return bounds; | |
}; | |
/** | |
* Determins if a marker is contained in a bounds. | |
* | |
* @param {google.maps.Marker} marker The marker to check. | |
* @param {google.maps.LatLngBounds} bounds The bounds to check against. | |
* @return {boolean} True if the marker is in the bounds. | |
* @private | |
*/ | |
MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { | |
return bounds.contains(marker.getPosition()); | |
}; | |
/** | |
* Clears all clusters and markers from the clusterer. | |
*/ | |
MarkerClusterer.prototype.clearMarkers = function() { | |
this.resetViewport(); | |
// Set the markers a empty array. | |
this.markers_ = []; | |
}; | |
/** | |
* Clears all existing clusters and recreates them. | |
*/ | |
MarkerClusterer.prototype.resetViewport = function() { | |
// Remove all the clusters | |
for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { | |
cluster.remove(); | |
} | |
// Reset the markers to not be added and to be invisible. | |
for (var i = 0, marker; marker = this.markers_[i]; i++) { | |
marker.isAdded = false; | |
marker.setMap(null); | |
marker.setVisible(false); | |
} | |
this.clusters_ = []; | |
}; | |
/** | |
* Redraws the clusters. | |
*/ | |
MarkerClusterer.prototype.redraw = function() { | |
this.createClusters_(); | |
}; | |
/** | |
* Creates the clusters. | |
* | |
* @private | |
*/ | |
MarkerClusterer.prototype.createClusters_ = function() { | |
if (!this.ready_) { | |
return; | |
} | |
// Get our current map view bounds. | |
// Create a new bounds object so we don't affect the map. | |
var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), this.map_.getBounds().getNorthEast()); | |
var bounds = this.getExtendedBounds(mapBounds); | |
for (var i = 0, marker; marker = this.markers_[i]; i++) { | |
var added = false; | |
if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { | |
for (var j = 0, cluster; cluster = this.clusters_[j]; j++) { | |
if (!added && cluster.getCenter() && cluster.isMarkerInClusterBounds(marker)) { | |
added = true; | |
cluster.addMarker(marker); | |
break; | |
} | |
} | |
if (!added) { | |
// Create a new cluster. | |
var cluster = new Cluster(this); | |
cluster.addMarker(marker); | |
this.clusters_.push(cluster); | |
} | |
} | |
} | |
}; | |
/** | |
* A cluster that contains markers. | |
* | |
* @param {MarkerClusterer} markerClusterer The markerclusterer that this | |
* cluster is associated with. | |
* @constructor | |
* @ignore | |
*/ | |
function Cluster(markerClusterer) { | |
this.markerClusterer_ = markerClusterer; | |
this.map_ = markerClusterer.getMap(); | |
this.gridSize_ = markerClusterer.getGridSize(); | |
this.minimumSize = markerClusterer.minimumClusterSize; | |
this.center_ = null; | |
this.markers_ = []; | |
this.bounds_ = null; | |
this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), | |
markerClusterer.getGridSize()); | |
} | |
/** | |
* Determins if a marker is already added to the cluster. | |
* | |
* @param {google.maps.Marker} marker The marker to check. | |
* @return {boolean} True if the marker is already added. | |
*/ | |
Cluster.prototype.isMarkerAlreadyAdded = function(marker) { | |
if (this.markers_.indexOf) { | |
return this.markers_.indexOf(marker) != -1; | |
} else { | |
for (var i = 0, m; m = this.markers_[i]; i++) { | |
if (m == marker) { | |
return true; | |
} | |
} | |
} | |
return false; | |
}; | |
/** | |
* Add a marker the cluster. | |
* | |
* @param {google.maps.Marker} marker The marker to add. | |
* @return {boolean} True if the marker was added. | |
*/ | |
Cluster.prototype.addMarker = function(marker) { | |
if (this.isMarkerAlreadyAdded(marker)) { | |
return false; | |
} | |
if (!this.center_) { | |
this.center_ = marker.getPosition(); | |
this.calculateBounds_(); | |
} | |
if(this.markers_.length < this.minimumSize) { | |
marker.setMap(this.map_); | |
marker.setVisible(true); | |
} else if(this.markers_.length == this.minimumSize) { | |
// then we have hit our marker minimum, hide the existing markers | |
// note that all markers added past this point will default to be hidden | |
for(var i = 0; i < this.markers_.length; i++) { | |
this.markers_[i].setMap(null); | |
this.markers_[i].setVisible(false); | |
} | |
} | |
marker.isAdded = true; | |
this.markers_.push(marker); | |
this.updateIcon(); | |
return true; | |
}; | |
/** | |
* Returns the marker clusterer that the cluster is associated with. | |
* | |
* @return {MarkerClusterer} The associated marker clusterer. | |
*/ | |
Cluster.prototype.getMarkerClusterer = function() { | |
return this.markerClusterer_; | |
}; | |
/** | |
* Returns the bounds of the cluster. | |
* | |
* @return {google.maps.LatLngBounds} the cluster bounds. | |
*/ | |
Cluster.prototype.getBounds = function() { | |
this.calculateBounds_(); | |
return this.bounds_; | |
}; | |
/** | |
* Removes the cluster | |
*/ | |
Cluster.prototype.remove = function() { | |
this.clusterIcon_.remove(); | |
delete this.markers_; | |
}; | |
/** | |
* Returns the center of the cluster. | |
* | |
* @return {google.maps.LatLng} The cluster center. | |
*/ | |
Cluster.prototype.getCenter = function() { | |
return this.center_; | |
}; | |
/** | |
* Calculated the bounds of the cluster with the grid. | |
* | |
* @private | |
*/ | |
Cluster.prototype.calculateBounds_ = function() { | |
var bounds = new google.maps.LatLngBounds(this.center_, this.center_); | |
this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); | |
}; | |
/** | |
* Determines if a marker lies in the clusters bounds. | |
* | |
* @param {google.maps.Marker} marker The marker to check. | |
* @return {boolean} True if the marker lies in the bounds. | |
*/ | |
Cluster.prototype.isMarkerInClusterBounds = function(marker) { | |
return this.bounds_.contains(marker.getPosition()); | |
}; | |
/** | |
* Returns the map that the cluster is associated with. | |
* | |
* @return {google.maps.Map} The map. | |
*/ | |
Cluster.prototype.getMap = function() { | |
return this.map_; | |
}; | |
/** | |
* Updates the cluster icon | |
*/ | |
Cluster.prototype.updateIcon = function() { | |
var zoom = this.map_.getZoom(); | |
var mz = this.markerClusterer_.getMaxZoom(); | |
if (zoom > mz) { | |
// The zoom is greater than our max zoom so show all the markers in cluster. | |
for (var i = 0, marker; marker = this.markers_[i]; i++) { | |
marker.setMap(this.map_); | |
marker.setVisible(true); | |
} | |
return; | |
} | |
// hide the cluster if we have less than the minimum amount of clustered markers | |
if(this.markers_.length <= this.minimumSize) { | |
this.clusterIcon_.hide(); | |
return; | |
} | |
var numStyles = this.markerClusterer_.getStyles().length; | |
var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); | |
this.clusterIcon_.setCenter(this.center_); | |
this.clusterIcon_.setSums(sums); | |
this.clusterIcon_.show(); | |
}; | |
/** | |
* A cluster icon | |
* | |
* @param {Cluster} cluster The cluster to be associated with. | |
* @param {Object} styles An object that has style properties: | |
* 'url': (string) The image url. | |
* 'height': (number) The image height. | |
* 'width': (number) The image width. | |
* 'anchor': (Array) The anchor position of the label text. | |
* 'textColor': (string) The text color. | |
* 'textSize': (number) The text size. | |
* @param {number} opt_padding Optional padding to apply to the cluster icon. | |
* @constructor | |
* @extends google.maps.OverlayView | |
* @ignore | |
*/ | |
function ClusterIcon(cluster, styles, opt_padding) { | |
cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); | |
this.styles_ = styles; | |
this.padding_ = opt_padding || 0; | |
this.cluster_ = cluster; | |
this.center_ = null; | |
this.map_ = cluster.getMap(); | |
this.div_ = null; | |
this.sums_ = null; | |
this.visible_ = false; | |
this.setMap(this.map_); | |
} | |
/** | |
* Triggers the clusterclick event and zoom's if the option is set. | |
*/ | |
ClusterIcon.prototype.triggerClusterClick = function() { | |
var markerClusterer = this.cluster_.getMarkerClusterer(); | |
// Trigger the clusterclick event. | |
google.maps.event.trigger(markerClusterer, 'clusterclick', [this.cluster_]); | |
if (markerClusterer.isZoomOnClick()) { | |
// Center the map on this cluster. | |
this.map_.panTo(this.cluster_.getCenter()); | |
// Zoom into the cluster. | |
this.map_.fitBounds(this.cluster_.getBounds()); | |
} | |
}; | |
/** | |
* Adding the cluster icon to the dom. | |
* @ignore | |
*/ | |
ClusterIcon.prototype.onAdd = function() { | |
this.div_ = document.createElement('DIV'); | |
if (this.visible_) { | |
var pos = this.getPosFromLatLng_(this.center_); | |
this.div_.style.cssText = this.createCss(pos); | |
this.div_.innerHTML = this.sums_.text; | |
} | |
var panes = this.getPanes(); | |
panes.overlayImage.appendChild(this.div_); | |
var that = this; | |
google.maps.event.addDomListener(this.div_, 'click', function() { | |
that.triggerClusterClick(); | |
}); | |
}; | |
/** | |
* Returns the position to place the div dending on the latlng. | |
* | |
* @param {google.maps.LatLng} latlng The position in latlng. | |
* @return {google.maps.Point} The position in pixels. | |
* @private | |
*/ | |
ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { | |
var pos = this.getProjection().fromLatLngToDivPixel(latlng); | |
pos.x -= parseInt(this.width_ / 2, 10); | |
pos.y -= parseInt(this.height_ / 2, 10); | |
return pos; | |
}; | |
/** | |
* Draw the icon. | |
* @ignore | |
*/ | |
ClusterIcon.prototype.draw = function() { | |
if (this.visible_) { | |
var pos = this.getPosFromLatLng_(this.center_); | |
this.div_.style.top = pos.y + 'px'; | |
this.div_.style.left = pos.x + 'px'; | |
} | |
}; | |
/** | |
* Hide the icon. | |
*/ | |
ClusterIcon.prototype.hide = function() { | |
if (this.div_) { | |
this.div_.style.display = 'none'; | |
} | |
this.visible_ = false; | |
}; | |
/** | |
* Position and show the icon. | |
*/ | |
ClusterIcon.prototype.show = function() { | |
if (this.div_) { | |
var pos = this.getPosFromLatLng_(this.center_); | |
this.div_.style.cssText = this.createCss(pos); | |
this.div_.style.display = ''; | |
} | |
this.visible_ = true; | |
}; | |
/** | |
* Remove the icon from the map | |
*/ | |
ClusterIcon.prototype.remove = function() { | |
this.setMap(null); | |
}; | |
/** | |
* Implementation of the onRemove interface. | |
* @ignore | |
*/ | |
ClusterIcon.prototype.onRemove = function() { | |
if (this.div_ && this.div_.parentNode) { | |
this.hide(); | |
this.div_.parentNode.removeChild(this.div_); | |
this.div_ = null; | |
} | |
}; | |
/** | |
* Set the sums of the icon. | |
* | |
* @param {Object} sums The sums containing: | |
* 'text': (string) The text to display in the icon. | |
* 'index': (number) The style index of the icon. | |
*/ | |
ClusterIcon.prototype.setSums = function(sums) { | |
this.sums_ = sums; | |
this.text_ = sums.text; | |
this.index_ = sums.index; | |
if (this.div_) { | |
this.div_.innerHTML = sums.text; | |
} | |
this.useStyle(); | |
}; | |
/** | |
* Sets the icon to the the styles. | |
*/ | |
ClusterIcon.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]; | |
this.url_ = style.url; | |
this.height_ = style.height; | |
this.width_ = style.width; | |
this.textColor_ = style.opt_textColor; | |
this.anchor = style.opt_anchor; | |
this.textSize_ = style.opt_textSize; | |
}; | |
/** | |
* Sets the center of the icon. | |
* | |
* @param {google.maps.LatLng} center The latlng to set as the center. | |
*/ | |
ClusterIcon.prototype.setCenter = function(center) { | |
this.center_ = center; | |
}; | |
/** | |
* Create the css text based on the position of the icon. | |
* | |
* @param {google.maps.Point} pos The position. | |
* @return {string} The css style text. | |
*/ | |
ClusterIcon.prototype.createCss = function(pos) { | |
var style = []; | |
if (document.all) { | |
style.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' + | |
'sizingMethod=scale,src="' + this.url_ + '");'); | |
} else { | |
style.push('background:url(' + this.url_ + ');'); | |
} | |
if (typeof this.anchor_ === 'object') { | |
if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && | |
this.anchor_[0] < this.height_) { | |
style.push('height:' + (this.height_ - this.anchor_[0]) + | |
'px; padding-top:' + this.anchor_[0] + 'px;'); | |
} else { | |
style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + | |
'px;'); | |
} | |
if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && | |
this.anchor_[1] < this.width_) { | |
style.push('width:' + (this.width_ - this.anchor_[1]) + | |
'px; padding-left:' + this.anchor_[1] + 'px;'); | |
} else { | |
style.push('width:' + this.width_ + 'px; text-align:center;'); | |
} | |
} else { | |
style.push('height:' + this.height_ + 'px; line-height:' + | |
this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); | |
} | |
var txtColor = this.textColor_ ? this.textColor_ : 'black'; | |
var txtSize = this.textSize_ ? this.textSize_ : 11; | |
style.push('cursor:pointer; top:' + pos.y + 'px; left:' + | |
pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + | |
txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); | |
return style.join(''); | |
}; | |
// Export Symbols for Closure | |
// If you are not going to compile with closure then you can remove the | |
// code below. | |
window['MarkerClusterer'] = MarkerClusterer; | |
MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; | |
MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; | |
MarkerClusterer.prototype['clearMarkers'] = | |
MarkerClusterer.prototype.clearMarkers; | |
MarkerClusterer.prototype['getCalculator'] = | |
MarkerClusterer.prototype.getCalculator; | |
MarkerClusterer.prototype['getGridSize'] = | |
MarkerClusterer.prototype.getGridSize; | |
MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; | |
MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; | |
MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; | |
MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; | |
MarkerClusterer.prototype['getTotalClusters'] = | |
MarkerClusterer.prototype.getTotalClusters; | |
MarkerClusterer.prototype['getTotalMarkers'] = | |
MarkerClusterer.prototype.getTotalMarkers; | |
MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; | |
MarkerClusterer.prototype['removeMarker'] = | |
MarkerClusterer.prototype.removeMarker; | |
MarkerClusterer.prototype['resetViewport'] = | |
MarkerClusterer.prototype.resetViewport; | |
MarkerClusterer.prototype['setCalculator'] = | |
MarkerClusterer.prototype.setCalculator; | |
MarkerClusterer.prototype['setGridSize'] = | |
MarkerClusterer.prototype.setGridSize; | |
MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; | |
MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; | |
MarkerClusterer.prototype['idle'] = MarkerClusterer.prototype.idle; | |
ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd; | |
ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw; | |
ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment