function MarkerClusterer(map, opt_markers, opt_opts) { var clusters_ = []; var map_ = map; var maxZoom_ = null; var me_ = this; var gridSize_ = 60; var sizes = [53, 56, 66, 78, 90]; var styles_ = []; var leftMarkers_ = []; var mcfn_ = null; var i = 0; for (i = 1; i <= 5; ++i) { styles_.push({ 'url': "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m" + i + ".png", 'height': sizes[i - 1], 'width': sizes[i - 1] }) } if (typeof opt_opts === "object" && opt_opts !== null) { if (typeof opt_opts.gridSize === "number" && opt_opts.gridSize > 0) { gridSize_ = opt_opts.gridSize } if (typeof opt_opts.maxZoom === "number") { maxZoom_ = opt_opts.maxZoom } if (typeof opt_opts.styles === "object" && opt_opts.styles !== null && opt_opts.styles.length !== 0) { styles_ = opt_opts.styles } } function addLeftMarkers_() { if (leftMarkers_.length === 0) { return } var leftMarkers = []; for (i = 0; i < leftMarkers_.length; ++i) { me_.addMarker(leftMarkers_[i], true, null, null, true) } leftMarkers_ = leftMarkers } this.getStyles_ = function () { return styles_ }; this.clearMarkers = function () { for (var i = 0; i < clusters_.length; ++i) { if (typeof clusters_[i] !== "undefined" && clusters_[i] !== null) { clusters_[i].clearMarkers() } } clusters_ = []; leftMarkers_ = []; GEvent.removeListener(mcfn_) }; function isMarkerInViewport_(marker) { return map_.getBounds().containsLatLng(marker.getLatLng()) } function reAddMarkers_(markers) { var len = markers.length; var clusters = []; for (var i = len - 1; i >= 0; --i) { me_.addMarker(markers[i].marker, true, markers[i].isAdded, clusters, true) } addLeftMarkers_() } this.addMarker = function (marker, opt_isNodraw, opt_isAdded, opt_clusters, opt_isNoCheck) { if (opt_isNoCheck !== true) { if (!isMarkerInViewport_(marker)) { leftMarkers_.push(marker); return } } var isAdded = opt_isAdded; var clusters = opt_clusters; var pos = map_.fromLatLngToDivPixel(marker.getLatLng()); if (typeof isAdded !== "boolean") { isAdded = false } if (typeof clusters !== "object" || clusters === null) { clusters = clusters_ } var length = clusters.length; var cluster = null; for (var i = length - 1; i >= 0; i--) { cluster = clusters[i]; var center = cluster.getCenter(); if (center === null) { continue } center = map_.fromLatLngToDivPixel(center); if (pos.x >= center.x - gridSize_ && pos.x <= center.x + gridSize_ && pos.y >= center.y - gridSize_ && pos.y <= center.y + gridSize_) { cluster.addMarker({ 'isAdded': isAdded, 'marker': marker }); if (!opt_isNodraw) { cluster.redraw_() } return } } cluster = new Cluster(this, map); cluster.addMarker({ 'isAdded': isAdded, 'marker': marker }); if (!opt_isNodraw) { cluster.redraw_() } clusters.push(cluster); if (clusters !== clusters_) { clusters_.push(cluster) } }; this.removeMarker = function (marker) { for (var i = 0; i < clusters_.length; ++i) { if (clusters_[i].remove(marker)) { clusters_[i].redraw_(); return } } }; this.redraw_ = function () { var clusters = this.getClustersInViewport_(); for (var i = 0; i < clusters.length; ++i) { clusters[i].redraw_(true) } }; this.getClustersInViewport_ = function () { var clusters = []; var curBounds = map_.getBounds(); for (var i = 0; i < clusters_.length; i++) { if (clusters_[i].isInBounds(curBounds)) { clusters.push(clusters_[i]) } } return clusters }; this.getMaxZoom_ = function () { return maxZoom_ }; this.getMap_ = function () { return map_ }; this.getGridSize_ = function () { return gridSize_ }; this.getTotalMarkers = function () { var result = 0; for (var i = 0; i < clusters_.length; ++i) { result += clusters_[i].getTotalMarkers() } return result }; this.getTotalClusters = function () { return clusters_.length }; this.resetViewport = function () { var clusters = this.getClustersInViewport_(); var tmpMarkers = []; var removed = 0; for (var i = 0; i < clusters.length; ++i) { var cluster = clusters[i]; var oldZoom = cluster.getCurrentZoom(); if (oldZoom === null) { continue } var curZoom = map_.getZoom(); if (curZoom !== oldZoom) { var mks = cluster.getMarkers(); for (var j = 0; j < mks.length; ++j) { var newMarker = { 'isAdded': false, 'marker': mks[j].marker }; tmpMarkers.push(newMarker) } cluster.clearMarkers(); removed++; for (j = 0; j < clusters_.length; ++j) { if (cluster === clusters_[j]) { clusters_.splice(j, 1) } } } } reAddMarkers_(tmpMarkers); this.redraw_() }; this.addMarkers = function (markers) { for (var i = 0; i < markers.length; ++i) { this.addMarker(markers[i], true) } this.redraw_() }; if (typeof opt_markers === "object" && opt_markers !== null) { this.addMarkers(opt_markers) } mcfn_ = GEvent.addListener(map_, "moveend", function () { me_.resetViewport() }) } function Cluster(markerClusterer) { var center_ = null; var markers_ = []; var markerClusterer_ = markerClusterer; var map_ = markerClusterer.getMap_(); var clusterMarker_ = null; var zoom_ = map_.getZoom(); this.getMarkers = function () { return markers_ }; this.isInBounds = function (bounds) { if (center_ === null) { return false } if (!bounds) { bounds = map_.getBounds() } var sw = map_.fromLatLngToDivPixel(bounds.getSouthWest()); var ne = map_.fromLatLngToDivPixel(bounds.getNorthEast()); var centerxy = map_.fromLatLngToDivPixel(center_); var inViewport = true; var gridSize = markerClusterer.getGridSize_(); if (zoom_ !== map_.getZoom()) { var dl = map_.getZoom() - zoom_; gridSize = Math.pow(2, dl) * gridSize } if (ne.x !== sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x)) { inViewport = false } if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y)) { inViewport = false } return inViewport }; this.getCenter = function () { return center_ }; this.addMarker = function (marker) { if (center_ === null) { center_ = marker.marker.getLatLng() } markers_.push(marker) }; this.removeMarker = function (marker) { for (var i = 0; i < markers_.length; ++i) { if (marker === markers_[i].marker) { if (markers_[i].isAdded) { map_.removeOverlay(markers_[i].marker) } markers_.splice(i, 1); return true } } return false }; this.getCurrentZoom = function () { return zoom_ }; this.redraw_ = function (isForce) { if (!isForce && !this.isInBounds()) { return } zoom_ = map_.getZoom(); var i = 0; var mz = markerClusterer.getMaxZoom_(); if (mz === null) { mz = map_.getCurrentMapType().getMaximumResolution() } if (zoom_ >= mz || this.getTotalMarkers() === 1) { for (i = 0; i < markers_.length; ++i) { if (markers_[i].isAdded) { if (markers_[i].marker.isHidden()) { markers_[i].marker.show() } } else { map_.addOverlay(markers_[i].marker); markers_[i].isAdded = true } } if (clusterMarker_ !== null) { clusterMarker_.hide() } } else { for (i = 0; i < markers_.length; ++i) { if (markers_[i].isAdded && (!markers_[i].marker.isHidden())) { markers_[i].marker.hide() } } if (clusterMarker_ === null) { clusterMarker_ = new ClusterMarker_(center_, this.getTotalMarkers(), markerClusterer_.getStyles_(), markerClusterer_.getGridSize_()); map_.addOverlay(clusterMarker_) } else { if (clusterMarker_.isHidden()) { clusterMarker_.show() } clusterMarker_.redraw(true) } } }; this.clearMarkers = function () { if (clusterMarker_ !== null) { map_.removeOverlay(clusterMarker_) } for (var i = 0; i < markers_.length; ++i) { if (markers_[i].isAdded) { map_.removeOverlay(markers_[i].marker) } } markers_ = [] }; this.getTotalMarkers = function () { return markers_.length } } function ClusterMarker_(latlng, count, styles, padding) { var index = 0; var dv = count; while (dv !== 0) { dv = parseInt(dv / 10, 10); index++ } if (styles.length < index) { index = styles.length } this.url_ = styles[index - 1].url; this.height_ = styles[index - 1].height; this.width_ = styles[index - 1].width; this.textColor_ = styles[index - 1].opt_textColor; this.anchor_ = styles[index - 1].opt_anchor; this.latlng_ = latlng; this.index_ = index; this.styles_ = styles; this.text_ = count; this.padding_ = padding } ClusterMarker_.prototype = new GOverlay(); ClusterMarker_.prototype.initialize = function (map) { this.map_ = map; var div = document.createElement("div"); var latlng = this.latlng_; var pos = map.fromLatLngToDivPixel(latlng); pos.x -= parseInt(this.width_ / 2, 10); pos.y -= parseInt(this.height_ / 2, 10); var mstyle = ""; if (document.all) { mstyle = 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="' + this.url_ + '");' } else { mstyle = "background:url(" + this.url_ + ");" } if (typeof this.anchor_ === "object") { if (typeof this.anchor_[0] === "number" && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) { mstyle += 'height:' + (this.height_ - this.anchor_[0]) + 'px;padding-top:' + this.anchor_[0] + 'px;' } else { mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;' } if (typeof this.anchor_[1] === "number" && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) { mstyle += 'width:' + (this.width_ - this.anchor_[1]) + 'px;padding-left:' + this.anchor_[1] + 'px;' } else { mstyle += 'width:' + this.width_ + 'px;text-align:center;' } } else { mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;'; mstyle += 'width:' + this.width_ + 'px;text-align:center;' } var txtColor = this.textColor_ ? this.textColor_ : 'black'; div.style.cssText = mstyle + 'cursor:pointer;top:' + pos.y + "px;left:" + pos.x + "px;color:" + txtColor + ";position:absolute;font-size:12px;" + 'font-family:Arial,sans-serif;font-weight:normal'; div.innerHTML = this.text_; map.getPane(G_MAP_MAP_PANE).appendChild(div); var padding = this.padding_; GEvent.addDomListener(div, "click", function () { var pos = map.fromLatLngToDivPixel(latlng); var sw = new GPoint(pos.x - padding, pos.y + padding); sw = map.fromDivPixelToLatLng(sw); var ne = new GPoint(pos.x + padding, pos.y - padding); ne = map.fromDivPixelToLatLng(ne); var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize()); map.setCenter(latlng, zoom) }); this.div_ = div }; ClusterMarker_.prototype.remove = function () { this.div_.parentNode.removeChild(this.div_) }; ClusterMarker_.prototype.copy = function () { return new ClusterMarker_(this.latlng_, this.index_, this.text_, this.styles_, this.padding_) }; ClusterMarker_.prototype.redraw = function (force) { if (!force) { return } var pos = this.map_.fromLatLngToDivPixel(this.latlng_); pos.x -= parseInt(this.width_ / 2, 10); pos.y -= parseInt(this.height_ / 2, 10); this.div_.style.top = pos.y + "px"; this.div_.style.left = pos.x + "px" }; ClusterMarker_.prototype.hide = function () { this.div_.style.display = "none" }; ClusterMarker_.prototype.show = function () { this.div_.style.display = "" }; ClusterMarker_.prototype.isHidden = function () { return this.div_.style.display === "none" };
