https://americanproperties.net/wp-content/themes/wpresidence/js/google_js/markerclusterer.js?ver=1.0
1/** 2 * @name MarkerClustererPlus for Google Maps V3 3 * @version 2.1.1 [November 4, 2013] 4 * @author Gary Little 5 * @fileoverview 6 * The library creates and manages per-zoom-level clusters for large amounts of markers. 7 * <p> 8 * This is an enhanced V3 implementation of the 9 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 10 * >V2 MarkerClusterer</a> by Xiaoxi Wu. It is based on the 11 * <a href="http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/" 12 * >V3 MarkerClusterer</a> port by Luke Mahe. MarkerClustererPlus was created by Gary Little. 13 * <p> 14 * v2.0 release: MarkerClustererPlus v2.0 is backward compatible with MarkerClusterer v1.0. It 15 * adds support for the <code>ignoreHidden</code>, <code>title</code>, <code>batchSizeIE</code>, 16 * and <code>calculator</code> properties as well as support for four more events. It also allows 17 * greater control over the styling of the text that appears on the cluster marker. The 18 * documentation has been significantly improved and the overall code has been simplified and 19 * polished. Very large numbers of markers can now be managed without causing Javascript timeout 20 * errors on Internet Explorer. Note that the name of the <code>clusterclick</code> event has been 21 * deprecated. The new name is <code>click</code>, so please change your application code now. 22 */ 23 24/** 25 * Licensed under the Apache License, Version 2.0 (the "License"); 26 * you may not use this file except in compliance with the License. 27 * You may obtain a copy of the License at 28 * 29 * http://www.apache.org/licenses/LICENSE-2.0 30 * 31 * Unless required by applicable law or agreed to in writing, software 32 * distributed under the License is distributed on an "AS IS" BASIS, 33 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 34 * See the License for the specific language governing permissions and 35 * limitations under the License. 36 */ 37 38 39/** 40 * @name ClusterIconStyle 41 * @class This class represents the object for values in the <code>styles</code> array passed 42 * to the {@link MarkerClusterer} constructor. The element in this array that is used to 43 * style the cluster icon is determined by calling the <code>calculator</code> function. 44 * 45 * @property {string} url The URL of the cluster icon image file. Required. 46 * @property {number} height The display height (in pixels) of the cluster icon. Required. 47 * @property {number} width The display width (in pixels) of the cluster icon. Required. 48 * @property {Array} [anchorText] The position (in pixels) from the center of the cluster icon to 49 * where the text label is to be centered and drawn. The format is <code>[yoffset, xoffset]</code> 50 * where <code>yoffset</code> increases as you go down from center and <code>xoffset</code> 51 * increases to the right of center. The default is <code>[0, 0]</code>. 52 * @property {Array} [anchorIcon] The anchor position (in pixels) of the cluster icon. This is the 53 * spot on the cluster icon that is to be aligned with the cluster position. The format is 54 * <code>[yoffset, xoffset]</code> where <code>yoffset</code> increases as you go down and 55 * <code>xoffset</code> increases to the right of the top-left corner of the icon. The default 56 * anchor position is the center of the cluster icon. 57 * @property {string} [textColor="black"] The color of the label text shown on the 58 * cluster icon. 59 * @property {number} [textSize=11] The size (in pixels) of the label text shown on the 60 * cluster icon. 61 * @property {string} [textDecoration="none"] The value of the CSS <code>text-decoration</code> 62 * property for the label text shown on the cluster icon. 63 * @property {string} [fontWeight="bold"] The value of the CSS <code>font-weight</code> 64 * property for the label text shown on the cluster icon. 65 * @property {string} [fontStyle="normal"] The value of the CSS <code>font-style</code> 66 * property for the label text shown on the cluster icon. 67 * @property {string} [fontFamily="Arial,sans-serif"] The value of the CSS <code>font-family</code> 68 * property for the label text shown on the cluster icon. 69 * @property {string} [backgroundPosition="0 0"] The position of the cluster icon image 70 * within the image defined by <code>url</code>. The format is <code>"xpos ypos"</code> 71 * (the same format as for the CSS <code>background-position</code> property). You must set 72 * this property appropriately when the image defined by <code>url</code> represents a sprite 73 * containing multiple images. Note that the position <i>must</i> be specified in px units. 74 */ 75/** 76 * @name ClusterIconInfo
77 * @class This class is an object containing general information about a cluster icon. This is 78 * the object that a <code>calculator</code> function returns. 79 * 80 * @property {string} text The text of the label to be shown on the cluster icon. 81 * @property {number} index The index plus 1 of the element in the <code>styles</code> 82 * array to be used to style the cluster icon. 83 * @property {string} title The tooltip to display when the mouse moves over the cluster icon. 84 * If this value is <code>undefined</code> or <code>""</code>, <code>title</code> is set to the 85 * value of the <code>title</code> property passed to the MarkerClusterer. 86 */ 87/** 88 * A cluster icon. 89 * 90 * @constructor 91 * @extends google.maps.OverlayView 92 * @param {Cluster} cluster The cluster with which the icon is to be associated. 93 * @param {Array} [styles] An array of {@link ClusterIconStyle} defining the cluster icons 94 * to use for various cluster sizes. 95 * @private 96 */ 97function ClusterIcon(cluster, styles) { 98 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 99 100 this.cluster_ = cluster; 101 this.className_ = cluster.getMarkerClusterer().getClusterClass(); 102 this.styles_ = styles; 103 this.center_ = null; 104 this.div_ = null; 105 this.sums_ = null; 106 this.visible_ = false; 107 108 this.setMap(cluster.getMap()); // Note: this causes onAdd to be called 109} 110 111 112/** 113 * Adds the icon to the DOM. 114 */ 115ClusterIcon.prototype.onAdd = function () { 116 var cClusterIcon = this; 117 var cMouseDownInCluster; 118 var cDraggingMapByCluster; 119 120 this.div_ = document.createElement("div"); 121 this.div_.className = this.className_; 122 if (this.visible_) { 123 this.show(); 124 } 125 126 this.getPanes().overlayMouseTarget.appendChild(this.div_); 127 128 // Fix for Issue 157 129 this.boundsChangedListener_ = google.maps.event.addListener(this.getMap(), "bounds_changed", function () { 130 cDraggingMapByCluster = cMouseDownInCluster; 131 }); 132 133 google.maps.event.addDomListener(this.div_, "mousedown", function () { 134 cMouseDownInCluster = true; 135 cDraggingMapByCluster = false; 136 }); 137 138 google.maps.event.addDomListener(this.div_, "click", function (e) { 139 cMouseDownInCluster = false; 140 if (!cDraggingMapByCluster) { 141 var theBounds; 142 var mz; 143 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 144 /** 145 * This event is fired when a cluster marker is clicked. 146 * @name MarkerClusterer#click 147 * @param {Cluster} c The cluster that was clicked. 148 * @event 149 */ 150 google.maps.event.trigger(mc, "click", cClusterIcon.cluster_); 151 google.maps.event.trigger(mc, "clusterclick", cClusterIcon.cluster_); // deprecated name 152 153 // The default click handler follows. Disable it by setting 154 // the zoomOnClick property to false. 155 if (mc.getZoomOnClick()) { 156 // Zoom into the cluster. 157 mz = mc.getMaxZoom(); 158 theBounds = cClusterIcon.cluster_.getBounds(); 159 mc.getMap().fitBounds(theBounds); 160 // There is a fix for Issue 170 here: 161 setTimeout(function () { 162 mc.getMap().fitBounds(theBounds); 163 // Don't zoom beyond the max zoom level 164 if (mz !== null && (mc.getMap().getZoom() > mz)) { 165 166 mc.getMap().setZoom(mz + 1); 167 } 168 }, 100); 169 } 170 171 // Prevent event propagation to the map: 172 e.cancelBubble = true; 173 if (e.stopPropagation) { 174 e.stopPropagation(); 175 } 176 } 177 }); 178 179 google.maps.event.addDomListener(this.div_, "mouseover", function () { 180 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 181 /** 182 * This event is fired when the mouse moves over a cluster marker. 183 * @name MarkerClusterer#mouseover 184 * @param {Cluster} c The cluster that the mouse moved over. 185 * @event 186 */ 187 google.maps.event.trigger(mc, "mouseover", cClusterIcon.cluster_); 188 }); 189 190 google.maps.event.addDomListener(this.div_, "mouseout", function () { 191 var mc = cClusterIcon.cluster_.getMarkerClusterer(); 192 /** 193 * This event is fired when the mouse moves out of a cluster marker. 194 * @name MarkerClusterer#mouseout 195 * @param {Cluster} c The cluster that the mouse moved out of. 196 * @event 197 */ 198 google.maps.event.trigger(mc, "mouseout", cClusterIcon.cluster_); 199 }); 200}; 201 202 203/** 204 * Removes the icon from the DOM. 205 */ 206ClusterIcon.prototype.onRemove = function () { 207 if (this.div_ && this.div_.parentNode) { 208 this.hide(); 209 google.maps.event.removeListener(this.boundsChangedListener_); 210 google.maps.event.clearInstanceListeners(this.div_); 211 this.div_.parentNode.removeChild(this.div_); 212 this.div_ = null; 213 } 214}; 215 216 217/** 218 * Draws the icon. 219 */ 220ClusterIcon.prototype.draw = function () { 221 if (this.visible_) { 222 var pos = this.getPosFromLatLng_(this.center_); 223 this.div_.style.top = pos.y + "px"; 224 this.div_.style.left = pos.x + "px"; 225 } 226}; 227 228 229/** 230 * Hides the icon. 231 */ 232ClusterIcon.prototype.hide = function () { 233 if (this.div_) { 234 this.div_.style.display = "none"; 235 } 236 this.visible_ = false;
vendor: 13,324 bytes, lines 237-624
237}; 238 239 240/** 241 * Positions and shows the icon. 242 */ 243ClusterIcon.prototype.show = function () { 244 if (this.div_) { 245 var img = ""; 246 // NOTE: values must be specified in px units 247 var bp = this.backgroundPosition_.split(" "); 248 var spriteH = parseInt(bp[0].trim(), 10); 249 var spriteV = parseInt(bp[1].trim(), 10); 250 var pos = this.getPosFromLatLng_(this.center_); 251 this.div_.style.cssText = this.createCss(pos); 252 img = "<img src='" + this.url_ + "' style='position: absolute; top: " + spriteV + "px; left: " + spriteH + "px; "; 253 if (!this.cluster_.getMarkerClusterer().enableRetinaIcons_) { 254 img += "clip: rect(" + (-1 * spriteV) + "px, " + ((-1 * spriteH) + this.width_) + "px, " + 255 ((-1 * spriteV) + this.height_) + "px, " + (-1 * spriteH) + "px);"; 256 } 257 img += "'>"; 258 this.div_.innerHTML = img + "<div style='" + 259 "position: absolute;" + 260 "top: " + this.anchorText_[0] + "px;" + 261 "left: " + this.anchorText_[1] + "px;" + 262 "color: " + this.textColor_ + ";" + 263 "font-size: " + this.textSize_ + "px;" + 264 "font-family: " + this.fontFamily_ + ";" + 265 "font-weight: " + this.fontWeight_ + ";" + 266 "font-style: " + this.fontStyle_ + ";" + 267 "text-decoration: " + this.textDecoration_ + ";" + 268 "text-align: center;" + 269 "width: " + this.width_ + "px;" + 270 "line-height:" + this.height_ + "px;" + 271 "'>" + this.sums_.text + "</div>"; 272 if (typeof this.sums_.title === "undefined" || this.sums_.title === "") { 273 this.div_.title = this.cluster_.getMarkerClusterer().getTitle(); 274 } else { 275 this.div_.title = this.sums_.title; 276 } 277 this.div_.style.display = ""; 278 } 279 this.visible_ = true; 280}; 281 282 283/** 284 * Sets the icon styles to the appropriate element in the styles array. 285 * 286 * @param {ClusterIconInfo} sums The icon label text and styles index. 287 */ 288ClusterIcon.prototype.useStyle = function (sums) { 289 this.sums_ = sums; 290 var index = Math.max(0, sums.index - 1); 291 index = Math.min(this.styles_.length - 1, index); 292 var style = this.styles_[index]; 293 this.url_ = style.url; 294 this.height_ = style.height; 295 this.width_ = style.width; 296 this.anchorText_ = style.anchorText || [0, 0]; 297 this.anchorIcon_ = style.anchorIcon || [parseInt(this.height_ / 2, 10), parseInt(this.width_ / 2, 10)]; 298 this.textColor_ = style.textColor || "black"; 299 this.textSize_ = style.textSize || 11; 300 this.textDecoration_ = style.textDecoration || "none"; 301 this.fontWeight_ = style.fontWeight || "bold"; 302 this.fontStyle_ = style.fontStyle || "normal"; 303 this.fontFamily_ = style.fontFamily || "Arial,sans-serif"; 304 this.backgroundPosition_ = style.backgroundPosition || "0 0"; 305}; 306 307 308/** 309 * Sets the position at which to center the icon. 310 * 311 * @param {google.maps.LatLng} center The latlng to set as the center. 312 */ 313ClusterIcon.prototype.setCenter = function (center) { 314 this.center_ = center; 315}; 316 317 318/** 319 * Creates the cssText style parameter based on the position of the icon. 320 * 321 * @param {google.maps.Point} pos The position of the icon. 322 * @return {string} The CSS style text. 323 */ 324ClusterIcon.prototype.createCss = function (pos) { 325 var style = []; 326 style.push("cursor: pointer;"); 327 style.push("position: absolute; top: " + pos.y + "px; left: " + pos.x + "px;"); 328 style.push("width: " + this.width_ + "px; height: " + this.height_ + "px;"); 329 return style.join(""); 330}; 331 332 333/** 334 * Returns the position at which to place the DIV depending on the latlng. 335 * 336 * @param {google.maps.LatLng} latlng The position in latlng. 337 * @return {google.maps.Point} The position in pixels. 338 */ 339ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) { 340 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 341 pos.x -= this.anchorIcon_[1]; 342 pos.y -= this.anchorIcon_[0]; 343 pos.x = parseInt(pos.x, 10); 344 pos.y = parseInt(pos.y, 10); 345 return pos; 346}; 347 348 349/** 350 * Creates a single cluster that manages a group of proximate markers. 351 * Used internally, do not call this constructor directly. 352 * @constructor 353 * @param {MarkerClusterer} mc The <code>MarkerClusterer</code> object with which this 354 * cluster is associated. 355 */ 356function Cluster(mc) { 357 this.markerClusterer_ = mc; 358 this.map_ = mc.getMap(); 359 this.gridSize_ = mc.getGridSize(); 360 this.minClusterSize_ = mc.getMinimumClusterSize(); 361 this.averageCenter_ = mc.getAverageCenter(); 362 this.markers_ = []; 363 this.center_ = null; 364 this.bounds_ = null; 365 this.clusterIcon_ = new ClusterIcon(this, mc.getStyles()); 366} 367 368 369/** 370 * Returns the number of markers managed by the cluster. You can call this from 371 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 372 * for the <code>MarkerClusterer</code> object. 373 * 374 * @return {number} The number of markers in the cluster. 375 */ 376Cluster.prototype.getSize = function () { 377 return this.markers_.length; 378}; 379 380 381/** 382 * Returns the array of markers managed by the cluster. You can call this from 383 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 384 * for the <code>MarkerClusterer</code> object. 385 * 386 * @return {Array} The array of markers in the cluster. 387 */ 388Cluster.prototype.getMarkers = function () { 389 return this.markers_; 390}; 391 392 393/** 394 * Returns the center of the cluster. You can call this from 395 * a <code>click</code>, <code>mouseover</code>, or <code>mouseout</code> event handler 396 * for the <code>MarkerClusterer</code> object. 397 * 398 * @return {google.maps.LatLng} The center of the cluster. 399 */ 400Cluster.prototype.getCenter = function () { 401 return this.center_; 402}; 403 404 405/** 406 * Returns the map with which the cluster is associated. 407 * 408 * @return {google.maps.Map} The map. 409 * @ignore 410 */ 411Cluster.prototype.getMap = function () { 412 return this.map_; 413}; 414 415 416/** 417 * Returns the <code>MarkerClusterer</code> object with which the cluster is associated. 418 * 419 * @return {MarkerClusterer} The associated marker clusterer. 420 * @ignore 421 */ 422Cluster.prototype.getMarkerClusterer = function () { 423 return this.markerClusterer_; 424}; 425 426 427/** 428 * Returns the bounds of the cluster. 429 * 430 * @return {google.maps.LatLngBounds} the cluster bounds. 431 * @ignore 432 */ 433Cluster.prototype.getBounds = function () { 434 var i; 435 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 436 var markers = this.getMarkers(); 437 for (i = 0; i < markers.length; i++) { 438 bounds.extend(markers[i].getPosition()); 439 } 440 return bounds; 441}; 442 443 444/** 445 * Removes the cluster from the map. 446 * 447 * @ignore 448 */ 449Cluster.prototype.remove = function () { 450 this.clusterIcon_.setMap(null); 451 this.markers_ = []; 452 delete this.markers_; 453}; 454 455 456/** 457 * Adds a marker to the cluster. 458 * 459 * @param {google.maps.Marker} marker The marker to be added. 460 * @return {boolean} True if the marker was added. 461 * @ignore 462 */ 463Cluster.prototype.addMarker = function (marker) { 464 var i; 465 var mCount; 466 var mz; 467 468 if (this.isMarkerAlreadyAdded_(marker)) { 469 return false; 470 } 471 472 if (!this.center_) { 473 this.center_ = marker.getPosition(); 474 this.calculateBounds_(); 475 } else { 476 if (this.averageCenter_) { 477 var l = this.markers_.length + 1; 478 var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; 479 var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; 480 this.center_ = new google.maps.LatLng(lat, lng); 481 this.calculateBounds_(); 482 } 483 } 484 485 marker.isAdded = true; 486 this.markers_.push(marker); 487 488 mCount = this.markers_.length; 489 mz = this.markerClusterer_.getMaxZoom(); 490 if (mz !== null && this.map_.getZoom() > mz) { 491 // Zoomed in past max zoom, so show the marker. 492 if (marker.getMap() !== this.map_) { 493 marker.setMap(this.map_); 494 } 495 } else if (mCount < this.minClusterSize_) { 496 // Min cluster size not reached so show the marker. 497 if (marker.getMap() !== this.map_) { 498 marker.setMap(this.map_); 499 } 500 } else if (mCount === this.minClusterSize_) { 501 // Hide the markers that were showing. 502 for (i = 0; i < mCount; i++) { 503 this.markers_[i].setMap(null); 504 } 505 } else { 506 marker.setMap(null); 507 } 508 509 this.updateIcon_(); 510 return true; 511}; 512 513 514/** 515 * Determines if a marker lies within the cluster's bounds. 516 * 517 * @param {google.maps.Marker} marker The marker to check. 518 * @return {boolean} True if the marker lies in the bounds. 519 * @ignore 520 */ 521Cluster.prototype.isMarkerInClusterBounds = function (marker) { 522 return this.bounds_.contains(marker.getPosition()); 523}; 524 525 526/** 527 * Calculates the extended bounds of the cluster with the grid. 528 */ 529Cluster.prototype.calculateBounds_ = function () { 530 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 531 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 532}; 533 534 535/** 536 * Updates the cluster icon. 537 */ 538Cluster.prototype.updateIcon_ = function () { 539 var mCount = this.markers_.length; 540 var mz = this.markerClusterer_.getMaxZoom(); 541 542 if (mz !== null && this.map_.getZoom() > mz) { 543 this.clusterIcon_.hide(); 544 return; 545 } 546 547 if (mCount < this.minClusterSize_) { 548 // Min cluster size not yet reached. 549 this.clusterIcon_.hide(); 550 return; 551 } 552 553 var numStyles = this.markerClusterer_.getStyles().length; 554 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 555 this.clusterIcon_.setCenter(this.center_); 556 this.clusterIcon_.useStyle(sums); 557 this.clusterIcon_.show(); 558}; 559 560 561/** 562 * Determines if a marker has already been added to the cluster. 563 * 564 * @param {google.maps.Marker} marker The marker to check. 565 * @return {boolean} True if the marker has already been added. 566 */ 567Cluster.prototype.isMarkerAlreadyAdded_ = function (marker) { 568 var i; 569 if (this.markers_.indexOf) { 570 return this.markers_.indexOf(marker) !== -1; 571 } else { 572 for (i = 0; i < this.markers_.length; i++) { 573 if (marker === this.markers_[i]) { 574 return true; 575 } 576 } 577 } 578 return false; 579}; 580 581 582/** 583 * @name MarkerClustererOptions 584 * @class This class represents the optional parameter passed to 585 * the {@link MarkerClusterer} constructor. 586 * @property {number} [gridSize=60] The grid size of a cluster in pixels. The grid is a square. 587 * @property {number} [maxZoom=null] The maximum zoom level at which clustering is enabled or 588 * <code>null</code> if clustering is to be enabled at all zoom levels. 589 * @property {boolean} [zoomOnClick=true] Whether to zoom the map when a cluster marker is 590 * clicked. You may want to set this to <code>false</code> if you have installed a handler 591 * for the <code>click</code> event and it deals with zooming on its own. 592 * @property {boolean} [averageCenter=false] Whether the position of a cluster marker should be 593 * the average position of all markers in the cluster. If set to <code>false</code>, the 594 * cluster marker is positioned at the location of the first marker added to the cluster. 595 * @property {number} [minimumClusterSize=2] The minimum number of markers needed in a cluster 596 * before the markers are hidden and a cluster marker appears. 597 * @property {boolean} [ignoreHidden=false] Whether to ignore hidden markers in clusters. You 598 * may want to set this to <code>true</code> to ensure that hidden markers are not included 599 * in the marker count that appears on a cluster marker (this count is the value of the 600 * <code>text</code> property of the result returned by the default <code>calculator</code>). 601 * If set to <code>true</code> and you change the visibility of a marker being clustered, be 602 * sure to also call <code>MarkerClusterer.repaint()</code>. 603 * @property {string} [title=""] The tooltip to display when the mouse moves over a cluster 604 * marker. (Alternatively, you can use a custom <code>calculator</code> function to specify a 605 * different tooltip for each cluster marker.) 606 * @property {function} [calculator=MarkerClusterer.CALCULATOR] The function used to determine 607 * the text to be displayed on a cluster marker and the index indicating which style to use 608 * for the cluster marker. The input parameters for the function are (1) the array of markers 609 * represented by a cluster marker and (2) the number of cluster icon styles. It returns a 610 * {@link ClusterIconInfo} object. The default <code>calculator</code> returns a 611 * <code>text</code> property which is the number of markers in the cluster and an 612 * <code>index</code> property which is one higher than the lowest integer such that 613 * <code>10^i</code> exceeds the number of markers in the cluster, or the size of the styles 614 * array, whichever is less. The <code>styles</code> array element used has an index of 615 * <code>index</code> minus 1. For example, the default <code>calculator</code> returns a 616 * <code>text</code> value of <code>"125"</code> and an <code>index</code> of <code>3</code> 617 * for a cluster icon representing 125 markers so the element used in the <code>styles</code> 618 * array is <code>2</code>. A <code>calculator</code> may also return a <code>title</code> 619 * property that contains the text of the tooltip to be used for the cluster marker. If 620 * <code>title</code> is not defined, the tooltip is set to the value of the <code>title</code> 621 * property for the MarkerClusterer. 622 * @property {string} [clusterClass="cluster"] The name of the CSS class defining general styles 623 * for the cluster markers. Use this class to define CSS styles that are not set up by the code 624 * that processes the <code>styles</code> array.
625 * @property {Array} [styles] An array of {@link ClusterIconStyle} elements defining the styles 626 * of the cluster markers to be used. The element to be used to style a given cluster marker 627 * is determined by the function defined by the <code>calculator</code> property. 628 * The default is an array of {@link ClusterIconStyle} elements whose properties are derived 629 * from the values for <code>imagePath</code>, <code>imageExtension</code>, and 630 * <code>imageSizes</code>. 631 * @property {boolean} [enableRetinaIcons=false] Whether to allow the use of cluster icons that 632 * have sizes that are some multiple (typically double) of their actual display size. Icons such 633 * as these look better when viewed on high-resolution monitors such as Apple's Retina displays. 634 * Note: if this property is <code>true</code>, sprites cannot be used as cluster icons. 635 * @property {number} [batchSize=MarkerClusterer.BATCH_SIZE] Set this property to the 636 * number of markers to be processed in a single batch when using a browser other than 637 * Internet Explorer (for Internet Explorer, use the batchSizeIE property instead). 638 * @property {number} [batchSizeIE=MarkerClusterer.BATCH_SIZE_IE] When Internet Explorer is 639 * being used, markers are processed in several batches with a small delay inserted between 640 * each batch in an attempt to avoid Javascript timeout errors. Set this property to the 641 * number of markers to be processed in a single batch; select as high a number as you can 642 * without causing a timeout error in the browser. This number might need to be as low as 100 643 * if 15,000 markers are being managed, for example. 644 * @property {string} [imagePath=MarkerClusterer.IMAGE_PATH] 645 * The full URL of the root name of the group of image files to use for cluster icons. 646 * The complete file name is of the form <code>imagePath</code>n.<code>imageExtension</code> 647 * where n is the image file number (1, 2, etc.). 648 * @property {string} [imageExtension=MarkerClusterer.IMAGE_EXTENSION] 649 * The extension name for the cluster icon image files (e.g., <code>"png"</code> or 650 * <code>"jpg"</code>). 651 * @property {Array} [imageSizes=MarkerClusterer.IMAGE_SIZES] 652 * An array of numbers containing the widths of the group of 653 * <code>imagePath</code>n.<code>imageExtension</code> image files. 654 * (The images are assumed to be square.) 655 */ 656/** 657 * Creates a MarkerClusterer object with the options specified in {@link MarkerClustererOptions}. 658 * @constructor 659 * @extends google.maps.OverlayView 660 * @param {google.maps.Map} map The Google map to attach to. 661 * @param {Array.<google.maps.Marker>} [opt_markers] The markers to be added to the cluster. 662 * @param {MarkerClustererOptions} [opt_options] The optional parameters. 663 */ 664function MarkerClusterer(map, opt_markers, opt_options) { 665 // MarkerClusterer implements google.maps.OverlayView interface. We use the 666 // extend function to extend MarkerClusterer with google.maps.OverlayView 667 // because it might not always be available when the code is defined so we 668 // look for it at the last possible moment. If it doesn't exist now then 669 // there is no point going ahead :) 670 this.extend(MarkerClusterer, google.maps.OverlayView); 671 672 opt_markers = opt_markers || []; 673 opt_options = opt_options || {}; 674 675 this.markers_ = []; 676 this.clusters_ = []; 677 this.listeners_ = []; 678 this.activeMap_ = null; 679 this.ready_ = false; 680 681 this.gridSize_ = opt_options.gridSize || 60; 682 this.minClusterSize_ = opt_options.minimumClusterSize || 2; 683 this.maxZoom_ = opt_options.maxZoom || null; 684 this.styles_ = opt_options.styles || []; 685 this.title_ = opt_options.title || ""; 686 this.zoomOnClick_ = true; 687 if (opt_options.zoomOnClick !== undefined) { 688 this.zoomOnClick_ = opt_options.zoomOnClick; 689 } 690 this.averageCenter_ = false; 691 if (opt_options.averageCenter !== undefined) { 692 this.averageCenter_ = opt_options.averageCenter; 693 } 694 this.ignoreHidden_ = false; 695 if (opt_options.ignoreHidden !== undefined) { 696 this.ignoreHidden_ = opt_options.ignoreHidden; 697 } 698 this.enableRetinaIcons_ = false; 699 if (opt_options.enableRetinaIcons !== undefined) { 700 this.enableRetinaIcons_ = opt_options.enableRetinaIcons; 701 } 702 this.imagePath_ = opt_options.imagePath || MarkerClusterer.IMAGE_PATH; 703 this.imageExtension_ = opt_options.imageExtension || MarkerClusterer.IMAGE_EXTENSION; 704 this.imageSizes_ = opt_options.imageSizes || MarkerClusterer.IMAGE_SIZES; 705 this.calculator_ = opt_options.calculator || MarkerClusterer.CALCULATOR; 706 this.batchSize_ = opt_options.batchSize || MarkerClusterer.BATCH_SIZE; 707 this.batchSizeIE_ = opt_options.batchSizeIE || MarkerClusterer.BATCH_SIZE_IE; 708 this.clusterClass_ = opt_options.clusterClass || "cluster"; 709 710 if (navigator.userAgent.toLowerCase().indexOf("msie") !== -1) { 711 // Try to avoid IE timeout when processing a huge number of markers: 712 this.batchSize_ = this.batchSizeIE_; 713 } 714 715 this.setupStyles_(); 716 717 this.addMarkers(opt_markers, true);
vendor: 14,418 bytes, lines 718-1302
718 this.setMap(map); // Note: this causes onAdd to be called 719} 720 721 722/** 723 * Implementation of the onAdd interface method. 724 * @ignore 725 */ 726MarkerClusterer.prototype.onAdd = function () { 727 var cMarkerClusterer = this; 728 729 this.activeMap_ = this.getMap(); 730 this.ready_ = true; 731 732 this.repaint(); 733 734 // Add the map event listeners 735 this.listeners_ = [ 736 google.maps.event.addListener(this.getMap(), "zoom_changed", function () { 737 cMarkerClusterer.resetViewport_(false); 738 // Workaround for this Google bug: when map is at level 0 and "-" of 739 // zoom slider is clicked, a "zoom_changed" event is fired even though 740 // the map doesn't zoom out any further. In this situation, no "idle" 741 // event is triggered so the cluster markers that have been removed 742 // do not get redrawn. Same goes for a zoom in at maxZoom. 743 if (this.getZoom() === (this.get("minZoom") || 0) || this.getZoom() === this.get("maxZoom")) { 744 google.maps.event.trigger(this, "idle"); 745 } 746 }), 747 google.maps.event.addListener(this.getMap(), "idle", function () { 748 cMarkerClusterer.redraw_(); 749 }) 750 ]; 751}; 752 753 754/** 755 * Implementation of the onRemove interface method. 756 * Removes map event listeners and all cluster icons from the DOM. 757 * All managed markers are also put back on the map. 758 * @ignore 759 */ 760MarkerClusterer.prototype.onRemove = function () { 761 var i; 762 763 // Put all the managed markers back on the map: 764 for (i = 0; i < this.markers_.length; i++) { 765 if (this.markers_[i].getMap() !== this.activeMap_) { 766 this.markers_[i].setMap(this.activeMap_); 767 } 768 } 769 770 // Remove all clusters: 771 for (i = 0; i < this.clusters_.length; i++) { 772 this.clusters_[i].remove(); 773 } 774 this.clusters_ = []; 775 776 // Remove map event listeners: 777 for (i = 0; i < this.listeners_.length; i++) { 778 google.maps.event.removeListener(this.listeners_[i]); 779 } 780 this.listeners_ = []; 781 782 this.activeMap_ = null; 783 this.ready_ = false; 784}; 785 786 787/** 788 * Implementation of the draw interface method. 789 * @ignore 790 */ 791MarkerClusterer.prototype.draw = function () {}; 792 793 794/** 795 * Sets up the styles object. 796 */ 797MarkerClusterer.prototype.setupStyles_ = function () { 798 var i, size; 799 if (this.styles_.length > 0) { 800 return; 801 } 802 803 for (i = 0; i < this.imageSizes_.length; i++) { 804 size = this.imageSizes_[i]; 805 this.styles_.push({ 806 url: this.imagePath_ + (i + 1) + "." + this.imageExtension_, 807 height: size, 808 width: size 809 }); 810 } 811}; 812 813 814/** 815 * Fits the map to the bounds of the markers managed by the clusterer. 816 */ 817MarkerClusterer.prototype.fitMapToMarkers = function () { 818 var i; 819 var markers = this.getMarkers(); 820 var bounds = new google.maps.LatLngBounds(); 821 for (i = 0; i < markers.length; i++) { 822 bounds.extend(markers[i].getPosition()); 823 } 824 825 this.getMap().fitBounds(bounds); 826}; 827 828 829/** 830 * Returns the value of the <code>gridSize</code> property. 831 * 832 * @return {number} The grid size. 833 */ 834MarkerClusterer.prototype.getGridSize = function () { 835 return this.gridSize_; 836}; 837 838 839/** 840 * Sets the value of the <code>gridSize</code> property. 841 * 842 * @param {number} gridSize The grid size. 843 */ 844MarkerClusterer.prototype.setGridSize = function (gridSize) { 845 this.gridSize_ = gridSize; 846}; 847 848 849/** 850 * Returns the value of the <code>minimumClusterSize</code> property. 851 * 852 * @return {number} The minimum cluster size. 853 */ 854MarkerClusterer.prototype.getMinimumClusterSize = function () { 855 return this.minClusterSize_; 856}; 857 858/** 859 * Sets the value of the <code>minimumClusterSize</code> property. 860 * 861 * @param {number} minimumClusterSize The minimum cluster size. 862 */ 863MarkerClusterer.prototype.setMinimumClusterSize = function (minimumClusterSize) { 864 this.minClusterSize_ = minimumClusterSize; 865}; 866 867 868/** 869 * Returns the value of the <code>maxZoom</code> property. 870 * 871 * @return {number} The maximum zoom level. 872 */ 873MarkerClusterer.prototype.getMaxZoom = function () { 874 return this.maxZoom_; 875}; 876 877 878/** 879 * Sets the value of the <code>maxZoom</code> property. 880 * 881 * @param {number} maxZoom The maximum zoom level. 882 */ 883MarkerClusterer.prototype.setMaxZoom = function (maxZoom) { 884 this.maxZoom_ = maxZoom; 885}; 886 887 888/** 889 * Returns the value of the <code>styles</code> property. 890 * 891 * @return {Array} The array of styles defining the cluster markers to be used. 892 */ 893MarkerClusterer.prototype.getStyles = function () { 894 return this.styles_; 895}; 896 897 898/** 899 * Sets the value of the <code>styles</code> property. 900 * 901 * @param {Array.<ClusterIconStyle>} styles The array of styles to use. 902 */ 903MarkerClusterer.prototype.setStyles = function (styles) { 904 this.styles_ = styles; 905}; 906 907 908/** 909 * Returns the value of the <code>title</code> property. 910 * 911 * @return {string} The content of the title text. 912 */ 913MarkerClusterer.prototype.getTitle = function () { 914 return this.title_; 915}; 916 917 918/** 919 * Sets the value of the <code>title</code> property. 920 * 921 * @param {string} title The value of the title property. 922 */ 923MarkerClusterer.prototype.setTitle = function (title) { 924 this.title_ = title; 925}; 926 927 928/** 929 * Returns the value of the <code>zoomOnClick</code> property. 930 * 931 * @return {boolean} True if zoomOnClick property is set. 932 */ 933MarkerClusterer.prototype.getZoomOnClick = function () { 934 return this.zoomOnClick_; 935}; 936 937 938/** 939 * Sets the value of the <code>zoomOnClick</code> property. 940 * 941 * @param {boolean} zoomOnClick The value of the zoomOnClick property. 942 */ 943MarkerClusterer.prototype.setZoomOnClick = function (zoomOnClick) { 944 this.zoomOnClick_ = zoomOnClick; 945}; 946 947 948/** 949 * Returns the value of the <code>averageCenter</code> property. 950 * 951 * @return {boolean} True if averageCenter property is set. 952 */ 953MarkerClusterer.prototype.getAverageCenter = function () { 954 return this.averageCenter_; 955}; 956 957 958/** 959 * Sets the value of the <code>averageCenter</code> property. 960 * 961 * @param {boolean} averageCenter The value of the averageCenter property. 962 */ 963MarkerClusterer.prototype.setAverageCenter = function (averageCenter) { 964 this.averageCenter_ = averageCenter; 965}; 966 967 968/** 969 * Returns the value of the <code>ignoreHidden</code> property. 970 * 971 * @return {boolean} True if ignoreHidden property is set. 972 */ 973MarkerClusterer.prototype.getIgnoreHidden = function () { 974 return this.ignoreHidden_; 975}; 976 977 978/** 979 * Sets the value of the <code>ignoreHidden</code> property. 980 * 981 * @param {boolean} ignoreHidden The value of the ignoreHidden property. 982 */ 983MarkerClusterer.prototype.setIgnoreHidden = function (ignoreHidden) { 984 this.ignoreHidden_ = ignoreHidden; 985}; 986 987 988/** 989 * Returns the value of the <code>enableRetinaIcons</code> property. 990 * 991 * @return {boolean} True if enableRetinaIcons property is set. 992 */ 993MarkerClusterer.prototype.getEnableRetinaIcons = function () { 994 return this.enableRetinaIcons_; 995}; 996 997 998/** 999 * Sets the value of the <code>enableRetinaIcons</code> property. 1000 * 1001 * @param {boolean} enableRetinaIcons The value of the enableRetinaIcons property. 1002 */ 1003MarkerClusterer.prototype.setEnableRetinaIcons = function (enableRetinaIcons) { 1004 this.enableRetinaIcons_ = enableRetinaIcons; 1005}; 1006 1007 1008/** 1009 * Returns the value of the <code>imageExtension</code> property. 1010 * 1011 * @return {string} The value of the imageExtension property. 1012 */ 1013MarkerClusterer.prototype.getImageExtension = function () { 1014 return this.imageExtension_; 1015}; 1016 1017 1018/** 1019 * Sets the value of the <code>imageExtension</code> property. 1020 * 1021 * @param {string} imageExtension The value of the imageExtension property. 1022 */ 1023MarkerClusterer.prototype.setImageExtension = function (imageExtension) { 1024 this.imageExtension_ = imageExtension; 1025}; 1026 1027 1028/** 1029 * Returns the value of the <code>imagePath</code> property. 1030 * 1031 * @return {string} The value of the imagePath property. 1032 */ 1033MarkerClusterer.prototype.getImagePath = function () { 1034 return this.imagePath_; 1035}; 1036 1037 1038/** 1039 * Sets the value of the <code>imagePath</code> property. 1040 * 1041 * @param {string} imagePath The value of the imagePath property. 1042 */ 1043MarkerClusterer.prototype.setImagePath = function (imagePath) { 1044 this.imagePath_ = imagePath; 1045}; 1046 1047 1048/** 1049 * Returns the value of the <code>imageSizes</code> property. 1050 * 1051 * @return {Array} The value of the imageSizes property. 1052 */ 1053MarkerClusterer.prototype.getImageSizes = function () { 1054 return this.imageSizes_; 1055}; 1056 1057 1058/** 1059 * Sets the value of the <code>imageSizes</code> property. 1060 * 1061 * @param {Array} imageSizes The value of the imageSizes property. 1062 */ 1063MarkerClusterer.prototype.setImageSizes = function (imageSizes) { 1064 this.imageSizes_ = imageSizes; 1065}; 1066 1067 1068/** 1069 * Returns the value of the <code>calculator</code> property. 1070 * 1071 * @return {function} the value of the calculator property. 1072 */ 1073MarkerClusterer.prototype.getCalculator = function () { 1074 return this.calculator_; 1075}; 1076 1077 1078/** 1079 * Sets the value of the <code>calculator</code> property. 1080 * 1081 * @param {function(Array.<google.maps.Marker>, number)} calculator The value 1082 * of the calculator property. 1083 */ 1084MarkerClusterer.prototype.setCalculator = function (calculator) { 1085 this.calculator_ = calculator; 1086}; 1087 1088 1089/** 1090 * Returns the value of the <code>batchSizeIE</code> property. 1091 * 1092 * @return {number} the value of the batchSizeIE property. 1093 */ 1094MarkerClusterer.prototype.getBatchSizeIE = function () { 1095 return this.batchSizeIE_; 1096}; 1097 1098 1099/** 1100 * Sets the value of the <code>batchSizeIE</code> property. 1101 * 1102 * @param {number} batchSizeIE The value of the batchSizeIE property. 1103 */ 1104MarkerClusterer.prototype.setBatchSizeIE = function (batchSizeIE) { 1105 this.batchSizeIE_ = batchSizeIE; 1106}; 1107 1108 1109/** 1110 * Returns the value of the <code>clusterClass</code> property. 1111 * 1112 * @return {string} the value of the clusterClass property. 1113 */ 1114MarkerClusterer.prototype.getClusterClass = function () { 1115 return this.clusterClass_; 1116}; 1117 1118 1119/** 1120 * Sets the value of the <code>clusterClass</code> property. 1121 * 1122 * @param {string} clusterClass The value of the clusterClass property. 1123 */ 1124MarkerClusterer.prototype.setClusterClass = function (clusterClass) { 1125 this.clusterClass_ = clusterClass; 1126}; 1127 1128 1129/** 1130 * Returns the array of markers managed by the clusterer. 1131 * 1132 * @return {Array} The array of markers managed by the clusterer. 1133 */ 1134MarkerClusterer.prototype.getMarkers = function () { 1135 return this.markers_; 1136}; 1137 1138 1139/** 1140 * Returns the number of markers managed by the clusterer. 1141 * 1142 * @return {number} The number of markers. 1143 */ 1144MarkerClusterer.prototype.getTotalMarkers = function () { 1145 return this.markers_.length; 1146}; 1147 1148 1149/** 1150 * Returns the current array of clusters formed by the clusterer. 1151 * 1152 * @return {Array} The array of clusters formed by the clusterer. 1153 */ 1154MarkerClusterer.prototype.getClusters = function () { 1155 return this.clusters_; 1156}; 1157 1158 1159/** 1160 * Returns the number of clusters formed by the clusterer. 1161 * 1162 * @return {number} The number of clusters formed by the clusterer. 1163 */ 1164MarkerClusterer.prototype.getTotalClusters = function () { 1165 return this.clusters_.length; 1166}; 1167 1168 1169/** 1170 * Adds a marker to the clusterer. The clusters are redrawn unless 1171 * <code>opt_nodraw</code> is set to <code>true</code>. 1172 * 1173 * @param {google.maps.Marker} marker The marker to add. 1174 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1175 */ 1176MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) { 1177 this.pushMarkerTo_(marker); 1178 if (!opt_nodraw) { 1179 this.redraw_(); 1180 } 1181}; 1182 1183 1184/** 1185 * Adds an array of markers to the clusterer. The clusters are redrawn unless 1186 * <code>opt_nodraw</code> is set to <code>true</code>. 1187 * 1188 * @param {Array.<google.maps.Marker>} markers The markers to add. 1189 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1190 */ 1191MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) { 1192 var key; 1193 for (key in markers) { 1194 if (markers.hasOwnProperty(key)) { 1195 this.pushMarkerTo_(markers[key]); 1196 } 1197 } 1198 if (!opt_nodraw) { 1199 this.redraw_(); 1200 } 1201}; 1202 1203 1204/** 1205 * Pushes a marker to the clusterer. 1206 * 1207 * @param {google.maps.Marker} marker The marker to add. 1208 */ 1209MarkerClusterer.prototype.pushMarkerTo_ = function (marker) { 1210 // If the marker is draggable add a listener so we can update the clusters on the dragend: 1211 if (marker.getDraggable()) { 1212 var cMarkerClusterer = this; 1213 google.maps.event.addListener(marker, "dragend", function () { 1214 if (cMarkerClusterer.ready_) { 1215 this.isAdded = false; 1216 cMarkerClusterer.repaint(); 1217 } 1218 }); 1219 } 1220 marker.isAdded = false; 1221 this.markers_.push(marker); 1222}; 1223 1224 1225/** 1226 * Removes a marker from the cluster. The clusters are redrawn unless 1227 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if the 1228 * marker was removed from the clusterer. 1229 * 1230 * @param {google.maps.Marker} marker The marker to remove. 1231 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1232 * @return {boolean} True if the marker was removed from the clusterer. 1233 */ 1234MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) { 1235 var removed = this.removeMarker_(marker); 1236 1237 if (!opt_nodraw && removed) { 1238 this.repaint(); 1239 } 1240 1241 return removed; 1242}; 1243 1244 1245/** 1246 * Removes an array of markers from the cluster. The clusters are redrawn unless 1247 * <code>opt_nodraw</code> is set to <code>true</code>. Returns <code>true</code> if markers 1248 * were removed from the clusterer. 1249 * 1250 * @param {Array.<google.maps.Marker>} markers The markers to remove. 1251 * @param {boolean} [opt_nodraw] Set to <code>true</code> to prevent redrawing. 1252 * @return {boolean} True if markers were removed from the clusterer. 1253 */ 1254MarkerClusterer.prototype.wpestate_removeMarkers = function (markers, opt_nodraw) { 1255 var i, r; 1256 var removed = false; 1257 1258 for (i = 0; i < markers.length; i++) { 1259 r = this.removeMarker_(markers[i]); 1260 removed = removed || r; 1261 } 1262 1263 if (!opt_nodraw && removed) { 1264 this.repaint(); 1265 } 1266 1267 return removed; 1268}; 1269 1270 1271/** 1272 * Removes a marker and returns true if removed, false if not. 1273 * 1274 * @param {google.maps.Marker} marker The marker to remove 1275 * @return {boolean} Whether the marker was removed or not 1276 */ 1277MarkerClusterer.prototype.removeMarker_ = function (marker) { 1278 var i; 1279 var index = -1; 1280 if (this.markers_.indexOf) { 1281 index = this.markers_.indexOf(marker); 1282 } else { 1283 for (i = 0; i < this.markers_.length; i++) { 1284 if (marker === this.markers_[i]) { 1285 index = i; 1286 break; 1287 } 1288 } 1289 } 1290 1291 if (index === -1) { 1292 // Marker is not in our list of markers, so do nothing: 1293 return false; 1294 } 1295 1296 marker.setMap(null); 1297 this.markers_.splice(index, 1); // Remove the marker from the list of managed markers 1298 return true; 1299}; 1300 1301 1302/**
1303 * Removes all clusters and markers from the map and also removes all markers 1304 * managed by the clusterer. 1305 */ 1306MarkerClusterer.prototype.clearMarkers = function () { 1307 this.resetViewport_(true); 1308 this.markers_ = []; 1309}; 1310 1311 1312/** 1313 * Recalculates and redraws all the marker clusters from scratch. 1314 * Call this after changing any properties. 1315 */ 1316MarkerClusterer.prototype.repaint = function () { 1317 var oldClusters = this.clusters_.slice(); 1318 this.clusters_ = []; 1319 this.resetViewport_(false); 1320 this.redraw_(); 1321 1322 // Remove the old clusters. 1323 // Do it in a timeout to prevent blinking effect. 1324 setTimeout(function () { 1325 var i; 1326 for (i = 0; i < oldClusters.length; i++) { 1327 oldClusters[i].remove(); 1328 } 1329 }, 0); 1330}; 1331 1332 1333/** 1334 * Returns the current bounds extended by the grid size. 1335 * 1336 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 1337 * @return {google.maps.LatLngBounds} The extended bounds. 1338 * @ignore 1339 */ 1340MarkerClusterer.prototype.getExtendedBounds = function (bounds) { 1341 var projection = this.getProjection(); 1342 1343 // Turn the bounds into latlng. 1344 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 1345 bounds.getNorthEast().lng());
vendor: 8,198 bytes, lines 1346-1644
1346 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 1347 bounds.getSouthWest().lng()); 1348 1349 // Convert the points to pixels and the extend out by the grid size. 1350 var trPix = projection.fromLatLngToDivPixel(tr); 1351 trPix.x += this.gridSize_; 1352 trPix.y -= this.gridSize_; 1353 1354 var blPix = projection.fromLatLngToDivPixel(bl); 1355 blPix.x -= this.gridSize_; 1356 blPix.y += this.gridSize_; 1357 1358 // Convert the pixel points back to LatLng 1359 var ne = projection.fromDivPixelToLatLng(trPix); 1360 var sw = projection.fromDivPixelToLatLng(blPix); 1361 1362 // Extend the bounds to contain the new bounds. 1363 bounds.extend(ne); 1364 bounds.extend(sw); 1365 1366 return bounds; 1367}; 1368 1369 1370/** 1371 * Redraws all the clusters. 1372 */ 1373MarkerClusterer.prototype.redraw_ = function () { 1374 this.createClusters_(0); 1375}; 1376 1377 1378/** 1379 * Removes all clusters from the map. The markers are also removed from the map 1380 * if <code>opt_hide</code> is set to <code>true</code>. 1381 * 1382 * @param {boolean} [opt_hide] Set to <code>true</code> to also remove the markers 1383 * from the map. 1384 */ 1385MarkerClusterer.prototype.resetViewport_ = function (opt_hide) { 1386 var i, marker; 1387 // Remove all the clusters 1388 for (i = 0; i < this.clusters_.length; i++) { 1389 this.clusters_[i].remove(); 1390 } 1391 this.clusters_ = []; 1392 1393 // Reset the markers to not be added and to be removed from the map. 1394 for (i = 0; i < this.markers_.length; i++) { 1395 marker = this.markers_[i]; 1396 marker.isAdded = false; 1397 if (opt_hide) { 1398 marker.setMap(null); 1399 } 1400 } 1401}; 1402 1403 1404/** 1405 * Calculates the distance between two latlng locations in km. 1406 * 1407 * @param {google.maps.LatLng} p1 The first lat lng point. 1408 * @param {google.maps.LatLng} p2 The second lat lng point. 1409 * @return {number} The distance between the two points in km. 1410 * @see http://www.movable-type.co.uk/scripts/latlong.html 1411*/ 1412MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) { 1413 var R = 6371; // Radius of the Earth in km 1414 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 1415 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 1416 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 1417 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 1418 Math.sin(dLon / 2) * Math.sin(dLon / 2); 1419 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 1420 var d = R * c; 1421 return d; 1422}; 1423 1424 1425/** 1426 * Determines if a marker is contained in a bounds. 1427 * 1428 * @param {google.maps.Marker} marker The marker to check. 1429 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 1430 * @return {boolean} True if the marker is in the bounds. 1431 */ 1432MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) { 1433 return bounds.contains(marker.getPosition()); 1434}; 1435 1436 1437/** 1438 * Adds a marker to a cluster, or creates a new cluster. 1439 * 1440 * @param {google.maps.Marker} marker The marker to add. 1441 */ 1442MarkerClusterer.prototype.addToClosestCluster_ = function (marker) { 1443 var i, d, cluster, center; 1444 var distance = 40000; // Some large number 1445 var clusterToAddTo = null; 1446 for (i = 0; i < this.clusters_.length; i++) { 1447 cluster = this.clusters_[i]; 1448 center = cluster.getCenter(); 1449 if (center) { 1450 d = this.distanceBetweenPoints_(center, marker.getPosition()); 1451 if (d < distance) { 1452 distance = d; 1453 clusterToAddTo = cluster; 1454 } 1455 } 1456 } 1457 1458 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 1459 clusterToAddTo.addMarker(marker); 1460 } else { 1461 cluster = new Cluster(this); 1462 cluster.addMarker(marker); 1463 this.clusters_.push(cluster); 1464 } 1465}; 1466 1467 1468/** 1469 * Creates the clusters. This is done in batches to avoid timeout errors 1470 * in some browsers when there is a huge number of markers. 1471 * 1472 * @param {number} iFirst The index of the first marker in the batch of 1473 * markers to be added to clusters. 1474 */ 1475MarkerClusterer.prototype.createClusters_ = function (iFirst) { 1476 var i, marker; 1477 var mapBounds; 1478 var cMarkerClusterer = this; 1479 if (!this.ready_) { 1480 return; 1481 } 1482 1483 // Cancel previous batch processing if we're working on the first batch: 1484 if (iFirst === 0) { 1485 /** 1486 * This event is fired when the <code>MarkerClusterer</code> begins 1487 * clustering markers. 1488 * @name MarkerClusterer#clusteringbegin 1489 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 1490 * @event 1491 */ 1492 google.maps.event.trigger(this, "clusteringbegin", this); 1493 1494 if (typeof this.timerRefStatic !== "undefined") { 1495 clearTimeout(this.timerRefStatic); 1496 delete this.timerRefStatic; 1497 } 1498 } 1499 1500 // Get our current map view bounds. 1501 // Create a new bounds object so we don't affect the map. 1502 // 1503 // See Comments 9 & 11 on Issue 3651 relating to this workaround for a Google Maps bug: 1504 if (this.getMap().getZoom() > 3) { 1505 mapBounds = new google.maps.LatLngBounds(this.getMap().getBounds().getSouthWest(), 1506 this.getMap().getBounds().getNorthEast()); 1507 } else { 1508 mapBounds = new google.maps.LatLngBounds(new google.maps.LatLng(85.02070771743472, -178.48388434375), new google.maps.LatLng(-85.08136444384544, 178.00048865625)); 1509 } 1510 var bounds = this.getExtendedBounds(mapBounds); 1511 1512 var iLast = Math.min(iFirst + this.batchSize_, this.markers_.length); 1513 1514 for (i = iFirst; i < iLast; i++) { 1515 marker = this.markers_[i]; 1516 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 1517 if (!this.ignoreHidden_ || (this.ignoreHidden_ && marker.getVisible())) { 1518 this.addToClosestCluster_(marker); 1519 } 1520 } 1521 } 1522 1523 if (iLast < this.markers_.length) { 1524 this.timerRefStatic = setTimeout(function () { 1525 cMarkerClusterer.createClusters_(iLast); 1526 }, 0); 1527 } else { 1528 delete this.timerRefStatic; 1529 1530 /** 1531 * This event is fired when the <code>MarkerClusterer</code> stops 1532 * clustering markers. 1533 * @name MarkerClusterer#clusteringend 1534 * @param {MarkerClusterer} mc The MarkerClusterer whose markers are being clustered. 1535 * @event 1536 */ 1537 google.maps.event.trigger(this, "clusteringend", this); 1538 } 1539}; 1540 1541 1542/** 1543 * Extends an object's prototype by another's. 1544 * 1545 * @param {Object} obj1 The object to be extended. 1546 * @param {Object} obj2 The object to extend with. 1547 * @return {Object} The new extended object. 1548 * @ignore 1549 */ 1550MarkerClusterer.prototype.extend = function (obj1, obj2) { 1551 return (function (object) { 1552 var property; 1553 for (property in object.prototype) { 1554 this.prototype[property] = object.prototype[property]; 1555 } 1556 return this; 1557 }).apply(obj1, [obj2]); 1558}; 1559 1560 1561/** 1562 * The default function for determining the label text and style 1563 * for a cluster icon. 1564 * 1565 * @param {Array.<google.maps.Marker>} markers The array of markers represented by the cluster. 1566 * @param {number} numStyles The number of marker styles available. 1567 * @return {ClusterIconInfo} The information resource for the cluster. 1568 * @constant 1569 * @ignore 1570 */ 1571MarkerClusterer.CALCULATOR = function (markers, numStyles) { 1572 var index = 0; 1573 var title = ""; 1574 var count = markers.length.toString(); 1575 1576 var dv = count; 1577 while (dv !== 0) { 1578 dv = parseInt(dv / 10, 10); 1579 index++; 1580 } 1581 1582 index = Math.min(index, numStyles); 1583 return { 1584 text: count, 1585 index: index, 1586 title: title 1587 }; 1588}; 1589 1590 1591/** 1592 * The number of markers to process in one batch. 1593 * 1594 * @type {number} 1595 * @constant 1596 */ 1597MarkerClusterer.BATCH_SIZE = 2000; 1598 1599 1600/** 1601 * The number of markers to process in one batch (IE only). 1602 * 1603 * @type {number} 1604 * @constant 1605 */ 1606MarkerClusterer.BATCH_SIZE_IE = 500; 1607 1608 1609/** 1610 * The default root name for the marker cluster images. 1611 * 1612 * @type {string} 1613 * @constant 1614 */ 1615MarkerClusterer.IMAGE_PATH = "http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclustererplus/images/m"; 1616 1617 1618/** 1619 * The default extension name for the marker cluster images. 1620 * 1621 * @type {string} 1622 * @constant 1623 */ 1624MarkerClusterer.IMAGE_EXTENSION = "png"; 1625 1626 1627/** 1628 * The default array of sizes for the marker cluster images. 1629 * 1630 * @type {Array.<number>} 1631 * @constant 1632 */ 1633MarkerClusterer.IMAGE_SIZES = [53, 56, 66, 78, 90]; 1634 1635if (typeof String.prototype.trim !== 'function') { 1636 /** 1637 * IE hack since trim() doesn't exist in all browsers 1638 * @return {string} The string with removed whitespace 1639 */ 1640 String.prototype.trim = function() { 1641 return this.replace(/^\s+|\s+$/g, ''); 1642 } 1643} 1644
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.