https://keurti.com/wp-content/plugins/directorist/assets/vendor-js/markerclusterer.js?ver=1790902278
vendor: 4,530 bytes, lines 1-153
1// ==ClosureCompiler== 2// @compilation_level ADVANCED_OPTIMIZATIONS 3// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js 4// ==/ClosureCompiler== 5 6/** 7 * @name MarkerClusterer for Google Maps v3 8 * @version version 1.0 9 * @author Luke Mahe 10 * @fileoverview 11 * The library creates and manages per-zoom-level clusters for large amounts of 12 * markers. 13 * <br/> 14 * This is a v3 implementation of the 15 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 16 * >v2 MarkerClusterer</a>. 17 */ 18 19/** 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33 34/** 35 * A Marker Clusterer that clusters markers. 36 * 37 * @param {google.maps.Map} map The Google map to attach to. 38 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to 39 * the cluster. 40 * @param {Object=} opt_options support the following options: 41 * 'gridSize': (number) The grid size of a cluster in pixels. 42 * 'maxZoom': (number) The maximum zoom level that a marker can be part of a 43 * cluster. 44 * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a 45 * cluster is to zoom into it. 46 * 'averageCenter': (boolean) Wether the center of each cluster should be 47 * the average of all markers in the cluster. 48 * 'minimumClusterSize': (number) The minimum number of markers to be in a 49 * cluster before the markers are hidden and a count 50 * is shown. 51 * 'styles': (object) An object that has style properties: 52 * 'url': (string) The image url. 53 * 'height': (number) The image height. 54 * 'width': (number) The image width. 55 * 'anchor': (Array) The anchor position of the label text. 56 * 'textColor': (string) The text color. 57 * 'textSize': (number) The text size. 58 * 'backgroundPosition': (string) The position of the background x, y. 59 * 'cssClass': (string) One or more CSS class for styling this marker. 60 * @constructor 61 * @extends google.maps.OverlayView 62 */ 63function MarkerClusterer(map, opt_markers, opt_options) { 64 // MarkerClusterer implements google.maps.OverlayView interface. We use the 65 // extend function to extend MarkerClusterer with google.maps.OverlayView 66 // because it might not always be available when the code is defined so we 67 // look for it at the last possible moment. If it doesn't exist now then 68 // there is no point going ahead :) 69 this.extend(MarkerClusterer, google.maps.OverlayView); 70 this.map_ = map; 71 72 /** 73 * @type {Array.<google.maps.Marker>} 74 * @private 75 */ 76 this.markers_ = []; 77 78 /** 79 * @type {Array.<Cluster>} 80 */ 81 this.clusters_ = []; 82 83 this.sizes = [53, 56, 66, 78, 90]; 84 85 /** 86 * @private 87 */ 88 this.styles_ = []; 89 90 /** 91 * @private 92 */ 93 this.cssClass_ = ''; 94 95 /** 96 * @type {boolean} 97 * @private 98 */ 99 this.ready_ = false; 100 101 var options = opt_options || {}; 102 103 /** 104 * @type {number} 105 * @private 106 */ 107 this.gridSize_ = options['gridSize'] || 60; 108 109 /** 110 * @private 111 */ 112 this.minClusterSize_ = options['minimumClusterSize'] || 2; 113 114 115 /** 116 * @type {?number} 117 * @private 118 */ 119 this.maxZoom_ = options['maxZoom'] || null; 120 121 this.styles_ = options['styles'] || []; 122 123 this.cssClass_ = options['cssClass'] || null; 124 125 /** 126 * @type {string} 127 * @private 128 */ 129 this.imagePath_ = options['imagePath'] || 130 this.MARKER_CLUSTER_IMAGE_PATH_; 131 132 /** 133 * @type {string} 134 * @private 135 */ 136 this.imageExtension_ = options['imageExtension'] || 137 this.MARKER_CLUSTER_IMAGE_EXTENSION_; 138 139 /** 140 * @type {boolean} 141 * @private 142 */ 143 this.zoomOnClick_ = true; 144 145 if (options['zoomOnClick'] != undefined) { 146 this.zoomOnClick_ = options['zoomOnClick']; 147 } 148 149 /** 150 * @type {boolean} 151 * @private 152 */ 153 this.averageCenter_ = false;
154 155 if (options['averageCenter'] != undefined) { 156 this.averageCenter_ = options['averageCenter']; 157 } 158 159 this.setupStyles_(); 160 161 this.setMap(map); 162 163 /** 164 * @type {number} 165 * @private 166 */ 167 this.prevZoom_ = this.map_.getZoom(); 168 169 // Add the map event listeners 170 var that = this; 171 google.maps.event.addListener(this.map_, 'zoom_changed', function() { 172 var zoom = that.map_.getZoom(); 173 174 if (that.prevZoom_ != zoom) { 175 that.prevZoom_ = zoom; 176 that.resetViewport(); 177 } 178 }); 179 180 google.maps.event.addListener(this.map_, 'idle', function() { 181 that.redraw(); 182 }); 183 184 // Finally, add the markers 185 if (opt_markers && opt_markers.length) { 186 this.addMarkers(opt_markers, false); 187 } 188 } 189 190 191 /** 192 * The marker cluster image path. 193 * 194 * @type {string} 195 * @private 196 */ 197 MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = 198 'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + 199 'images/m'; 200 201 202 /** 203 * The marker cluster image path. 204 * 205 * @type {string} 206 * @private 207 */ 208 MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; 209 210 211 /** 212 * Extends a objects prototype by anothers. 213 * 214 * @param {Object} obj1 The object to be extended. 215 * @param {Object} obj2 The object to extend with. 216 * @return {Object} The new extended object. 217 * @ignore 218 */ 219 MarkerClusterer.prototype.extend = function(obj1, obj2) { 220 return (function(object) { 221 for (var property in object.prototype) { 222 this.prototype[property] = object.prototype[property]; 223 } 224 return this; 225 }).apply(obj1, [obj2]); 226 }; 227 228 229 /** 230 * Implementaion of the interface method. 231 * @ignore 232 */ 233 MarkerClusterer.prototype.onAdd = function() { 234 this.setReady_(true); 235 }; 236 237 /** 238 * Implementaion of the interface method. 239 * @ignore 240 */ 241 MarkerClusterer.prototype.draw = function() {}; 242 243 /** 244 * Sets up the styles object. 245 * 246 * @private 247 */ 248 MarkerClusterer.prototype.setupStyles_ = function() { 249 if (this.styles_.length) { 250 return; 251 } 252 253 for (var i = 0, size; size = this.sizes[i]; i++) { 254 this.styles_.push({ 255 url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, 256 height: size, 257 width: size 258 }); 259 } 260 }; 261 262 /** 263 * Fit the map to the bounds of the markers in the clusterer. 264 */ 265 MarkerClusterer.prototype.fitMapToMarkers = function() { 266 var markers = this.getMarkers(); 267 var bounds = new google.maps.LatLngBounds(); 268 for (var i = 0, marker; marker = markers[i]; i++) { 269 bounds.extend(marker.getPosition()); 270 } 271 272 this.map_.fitBounds(bounds); 273 }; 274 275 276 /** 277 * Sets the styles. 278 * 279 * @param {Object} styles The style to set. 280 */ 281 MarkerClusterer.prototype.setStyles = function(styles) { 282 this.styles_ = styles; 283 }; 284 285 286 /** 287 * Gets the styles. 288 * 289 * @return {Object} The styles object. 290 */ 291 MarkerClusterer.prototype.getStyles = function() { 292 return this.styles_; 293 }; 294 295 296 /** 297 * Whether zoom on click is set. 298 * 299 * @return {boolean} True if zoomOnClick_ is set. 300 */ 301 MarkerClusterer.prototype.isZoomOnClick = function() { 302 return this.zoomOnClick_; 303 }; 304 305 /** 306 * Whether average center is set. 307 * 308 * @return {boolean} True if averageCenter_ is set. 309 */ 310 MarkerClusterer.prototype.isAverageCenter = function() { 311 return this.averageCenter_; 312 }; 313 314 315 /** 316 * Returns the array of markers in the clusterer. 317 * 318 * @return {Array.<google.maps.Marker>} The markers. 319 */ 320 MarkerClusterer.prototype.getMarkers = function() { 321 return this.markers_; 322 }; 323 324 325 /** 326 * Returns the number of markers in the clusterer 327 * 328 * @return {Number} The number of markers. 329 */ 330 MarkerClusterer.prototype.getTotalMarkers = function() { 331 return this.markers_.length; 332 }; 333 334 335 /** 336 * Sets the max zoom for the clusterer. 337 * 338 * @param {number} maxZoom The max zoom level. 339 */ 340 MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { 341 this.maxZoom_ = maxZoom; 342 }; 343 344 345 /** 346 * Gets the max zoom for the clusterer. 347 * 348 * @return {number} The max zoom level. 349 */ 350 MarkerClusterer.prototype.getMaxZoom = function() { 351 return this.maxZoom_; 352 }; 353 354 355 /** 356 * The function for calculating the cluster icon image. 357 * 358 * @param {Array.<google.maps.Marker>} markers The markers in the clusterer. 359 * @param {number} numStyles The number of styles available. 360 * @return {Object} A object properties: 'text' (string) and 'index' (number). 361 * @private 362 */ 363 MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { 364 var index = 0; 365 var count = markers.length; 366 var dv = count; 367 while (dv !== 0) { 368 dv = parseInt(dv / 10, 10); 369 index++; 370 } 371 372 index = Math.min(index, numStyles); 373 return { 374 text: count, 375 index: index 376 }; 377 }; 378 379 380 /** 381 * Set the calculator function. 382 * 383 * @param {function(Array, number)} calculator The function to set as the 384 * calculator. The function should return a object properties: 385 * 'text' (string) and 'index' (number). 386 * 387 */ 388 MarkerClusterer.prototype.setCalculator = function(calculator) { 389 this.calculator_ = calculator; 390 }; 391 392 393 /** 394 * Get the calculator function. 395 * 396 * @return {function(Array, number)} the calculator function. 397 */ 398 MarkerClusterer.prototype.getCalculator = function() { 399 return this.calculator_; 400 }; 401 402 403 /** 404 * Add an array of markers to the clusterer. 405 * 406 * @param {Array.<google.maps.Marker>} markers The markers to add. 407 * @param {boolean=}
407 opt_nodraw Whether to redraw the clusters. 408 */ 409 MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { 410 for (var i = 0, marker; marker = markers[i]; i++) { 411 this.pushMarkerTo_(marker); 412 } 413 if (!opt_nodraw) { 414 this.redraw(); 415 } 416 }; 417 418 419 /** 420 * Pushes a marker to the clusterer. 421 * 422 * @param {google.maps.Marker} marker The marker to add. 423 * @private 424 */ 425 MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { 426 marker.isAdded = false; 427 if (marker['draggable']) { 428 // If the marker is draggable add a listener so we update the clusters on 429 // the drag end. 430 var that = this; 431 google.maps.event.addListener(marker, 'dragend', function() { 432 marker.isAdded = false; 433 that.repaint(); 434 }); 435 } 436 this.markers_.push(marker); 437 }; 438 439 440 /** 441 * Adds a marker to the clusterer and redraws if needed. 442 * 443 * @param {google.maps.Marker} marker The marker to add. 444 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 445 */ 446 MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { 447 this.pushMarkerTo_(marker); 448 if (!opt_nodraw) { 449 this.redraw(); 450 } 451 }; 452 453 454 /** 455 * Removes a marker and returns true if removed, false if not 456 * 457 * @param {google.maps.Marker} marker The marker to remove 458 * @return {boolean} Whether the marker was removed or not 459 * @private 460 */ 461 MarkerClusterer.prototype.removeMarker_ = function(marker) { 462 var index = -1; 463 if (this.markers_.indexOf) { 464 index = this.markers_.indexOf(marker); 465 } else { 466 for (var i = 0, m; m = this.markers_[i]; i++) { 467 if (m == marker) { 468 index = i; 469 break; 470 } 471 } 472 } 473 474 if (index == -1) { 475 // Marker is not in our list of markers. 476 return false; 477 } 478 479 marker.setMap(null); 480 481 this.markers_.splice(index, 1); 482 483 return true; 484 }; 485 486 487 /** 488 * Remove a marker from the cluster. 489 * 490 * @param {google.maps.Marker} marker The marker to remove. 491 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 492 * @return {boolean} True if the marker was removed. 493 */ 494 MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { 495 var removed = this.removeMarker_(marker); 496 497 if (!opt_nodraw && removed) { 498 this.resetViewport(); 499 this.redraw(); 500 return true; 501 } else { 502 return false; 503 } 504 }; 505 506 507 /** 508 * Removes an array of markers from the cluster. 509 * 510 * @param {Array.<google.maps.Marker>} markers The markers to remove. 511 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 512 */ 513 MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { 514 var removed = false; 515 516 for (var i = 0, marker; marker = markers[i]; i++) { 517 var r = this.removeMarker_(marker); 518 removed = removed || r; 519 } 520 521 if (!opt_nodraw && removed) { 522 this.resetViewport(); 523 this.redraw(); 524 return true; 525 } 526 }; 527 528 529 /** 530 * Sets the clusterer's ready state. 531 * 532 * @param {boolean} ready The state. 533 * @private 534 */ 535 MarkerClusterer.prototype.setReady_ = function(ready) { 536 if (!this.ready_) { 537 this.ready_ = ready; 538 this.createClusters_(); 539 } 540 }; 541 542 543 /** 544 * Returns the number of clusters in the clusterer. 545 * 546 * @return {number} The number of clusters. 547 */ 548 MarkerClusterer.prototype.getTotalClusters = function() { 549 return this.clusters_.length; 550 }; 551 552 553 /** 554 * Returns the google map that the clusterer is associated with. 555 * 556 * @return {google.maps.Map} The map. 557 */ 558 MarkerClusterer.prototype.getMap = function() { 559 return this.map_; 560 }; 561 562 563 /** 564 * Sets the google map that the clusterer is associated with. 565 * 566 * @param {google.maps.Map} map The map. 567 */ 568 MarkerClusterer.prototype.setMap = function(map) { 569 this.map_ = map; 570 }; 571 572 573 /** 574 * Returns the size of the grid. 575 * 576 * @return {number} The grid size. 577 */ 578 MarkerClusterer.prototype.getGridSize = function() { 579 return this.gridSize_; 580 }; 581 582 583 /** 584 * Sets the size of the grid. 585 * 586 * @param {number} size The grid size. 587 */ 588 MarkerClusterer.prototype.setGridSize = function(size) { 589 this.gridSize_ = size; 590 }; 591 592 593 /** 594 * Returns the min cluster size. 595 * 596 * @return {number} The grid size. 597 */ 598 MarkerClusterer.prototype.getMinClusterSize = function() { 599 return this.minClusterSize_; 600 }; 601 602 /** 603 * Sets the min cluster size. 604 * 605 * @param {number} size The grid size. 606 */ 607 MarkerClusterer.prototype.setMinClusterSize = function(size) { 608 this.minClusterSize_ = size; 609 }; 610 611 612 /** 613 * Extends a bounds object by the grid size. 614 * 615 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 616 * @return {google.maps.LatLngBounds}
616 The extended bounds. 617 */ 618 MarkerClusterer.prototype.getExtendedBounds = function(bounds) { 619 var projection = this.getProjection(); 620 621 // Turn the bounds into latlng. 622 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 623 bounds.getNorthEast().lng()); 624 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 625 bounds.getSouthWest().lng()); 626 627 // Convert the points to pixels and the extend out by the grid size. 628 var trPix = projection.fromLatLngToDivPixel(tr); 629 trPix.x += this.gridSize_; 630 trPix.y -= this.gridSize_; 631 632 var blPix = projection.fromLatLngToDivPixel(bl); 633 blPix.x -= this.gridSize_; 634 blPix.y += this.gridSize_; 635 636 // Convert the pixel points back to LatLng 637 var ne = projection.fromDivPixelToLatLng(trPix); 638 var sw = projection.fromDivPixelToLatLng(blPix); 639 640 // Extend the bounds to contain the new bounds. 641 bounds.extend(ne); 642 bounds.extend(sw); 643 644 return bounds; 645 }; 646 647 648 /** 649 * Determins if a marker is contained in a bounds. 650 * 651 * @param {google.maps.Marker} marker The marker to check. 652 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 653 * @return {boolean} True if the marker is in the bounds. 654 * @private 655 */ 656 MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { 657 return bounds.contains(marker.getPosition()); 658 }; 659 660 661 /** 662 * Clears all clusters and markers from the clusterer. 663 */ 664 MarkerClusterer.prototype.clearMarkers = function() { 665 this.resetViewport(true); 666 667 // Set the markers a empty array. 668 this.markers_ = []; 669 }; 670 671 672 /** 673 * Clears all existing clusters and recreates them. 674 * @param {boolean} opt_hide To also hide the marker. 675 */ 676 MarkerClusterer.prototype.resetViewport = function(opt_hide) { 677 // Remove all the clusters 678 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 679 cluster.remove(); 680 } 681 682 // Reset the markers to not be added and to be invisible. 683 for (var i = 0, marker; marker = this.markers_[i]; i++) { 684 marker.isAdded = false; 685 if (opt_hide) { 686 marker.setMap(null); 687 } 688 } 689 690 this.clusters_ = []; 691 }; 692 693 /** 694 * 695 */ 696 MarkerClusterer.prototype.repaint = function() { 697 var oldClusters = this.clusters_.slice(); 698 this.clusters_.length = 0; 699 this.resetViewport(); 700 this.redraw(); 701 702 // Remove the old clusters. 703 // Do it in a timeout so the other clusters have been drawn first. 704 window.setTimeout(function() { 705 for (var i = 0, cluster; cluster = oldClusters[i]; i++) { 706 cluster.remove(); 707 } 708 }, 0); 709 }; 710 711 712 /** 713 * Redraws the clusters. 714 */ 715 MarkerClusterer.prototype.redraw = function() { 716 this.createClusters_(); 717 }; 718 719 720 /** 721 * Calculates the distance between two latlng locations in km. 722 * @see http://www.movable-type.co.uk/scripts/latlong.html 723 * 724 * @param {google.maps.LatLng} p1 The first lat lng point. 725 * @param {google.maps.LatLng} p2 The second lat lng point. 726 * @return {number} The distance between the two points in km. 727 * @private 728 */ 729 MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { 730 if (!p1 || !p2) { 731 return 0; 732 } 733 734 var R = 6371; // Radius of the Earth in km 735 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 736 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 737 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 738 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 739 Math.sin(dLon / 2) * Math.sin(dLon / 2); 740 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 741 var d = R * c; 742 return d; 743 }; 744 745 746 /** 747 * Add a marker to a cluster, or creates a new cluster. 748 * 749 * @param {google.maps.Marker} marker The marker to add. 750 * @private 751 */ 752 MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { 753 var distance = 40000; // Some large number 754 var clusterToAddTo = null; 755 var pos = marker.getPosition(); 756 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 757 var center = cluster.getCenter(); 758 if (center) { 759 var d = this.distanceBetweenPoints_(center, marker.getPosition()); 760 if (d < distance) { 761 distance = d; 762 clusterToAddTo = cluster; 763 } 764 } 765 } 766 767 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 768 clusterToAddTo.addMarker(marker); 769 } else { 770 var cluster = new Cluster(this); 771 cluster.addMarker(marker);
772 this.clusters_.push(cluster); 773 } 774 }; 775 776 777 /** 778 * Creates the clusters. 779 * 780 * @private 781 */ 782 MarkerClusterer.prototype.createClusters_ = function() { 783 if (!this.ready_) { 784 return; 785 } 786 787 // Get our current map view bounds. 788 // Create a new bounds object so we don't affect the map. 789 var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), 790 this.map_.getBounds().getNorthEast()); 791 var bounds = this.getExtendedBounds(mapBounds); 792 793 for (var i = 0, marker; marker = this.markers_[i]; i++) { 794 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 795 this.addToClosestCluster_(marker); 796 } 797 } 798 }; 799 800 801 /** 802 * A cluster that contains markers. 803 * 804 * @param {MarkerClusterer} markerClusterer The markerclusterer that this 805 * cluster is associated with. 806 * @constructor 807 * @ignore 808 */ 809 function Cluster(markerClusterer) { 810 this.markerClusterer_ = markerClusterer; 811 this.map_ = markerClusterer.getMap(); 812 this.gridSize_ = markerClusterer.getGridSize(); 813 this.minClusterSize_ = markerClusterer.getMinClusterSize(); 814 this.averageCenter_ = markerClusterer.isAverageCenter(); 815 this.center_ = null; 816 this.markers_ = []; 817 this.bounds_ = null; 818 this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), 819 markerClusterer.getGridSize()); 820 } 821 822 /** 823 * Determins if a marker is already added to the cluster. 824 * 825 * @param {google.maps.Marker} marker The marker to check. 826 * @return {boolean} True if the marker is already added. 827 */ 828 Cluster.prototype.isMarkerAlreadyAdded = function(marker) { 829 if (this.markers_.indexOf) { 830 return this.markers_.indexOf(marker) != -1; 831 } else { 832 for (var i = 0, m; m = this.markers_[i]; i++) { 833 if (m == marker) { 834 return true; 835 } 836 } 837 } 838 return false; 839 }; 840 841 842 /** 843 * Add a marker the cluster. 844 * 845 * @param {google.maps.Marker} marker The marker to add. 846 * @return {boolean} True if the marker was added. 847 */ 848 Cluster.prototype.addMarker = function(marker) { 849 if (this.isMarkerAlreadyAdded(marker)) { 850 return false; 851 } 852 853 if (!this.center_) { 854 this.center_ = marker.getPosition(); 855 this.calculateBounds_(); 856 } else { 857 if (this.averageCenter_) { 858 var l = this.markers_.length + 1; 859 var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; 860 var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; 861 this.center_ = new google.maps.LatLng(lat, lng); 862 this.calculateBounds_(); 863 } 864 } 865 866 marker.isAdded = true; 867 this.markers_.push(marker); 868 869 var len = this.markers_.length; 870 if (len < this.minClusterSize_ && marker.getMap() != this.map_) { 871 // Min cluster size not reached so show the marker. 872 marker.setMap(this.map_); 873 } 874 875 if (len == this.minClusterSize_) { 876 // Hide the markers that were showing. 877 for (var i = 0; i < len; i++) { 878 this.markers_[i].setMap(null); 879 } 880 } 881 882 if (len >= this.minClusterSize_) { 883 marker.setMap(null); 884 } 885 886 this.updateIcon(); 887 return true; 888 }; 889 890 891 /** 892 * Returns the marker clusterer that the cluster is associated with. 893 * 894 * @return {MarkerClusterer} The associated marker clusterer. 895 */ 896 Cluster.prototype.getMarkerClusterer = function() { 897 return this.markerClusterer_; 898 }; 899 900 901 /** 902 * Returns the bounds of the cluster. 903 * 904 * @return {google.maps.LatLngBounds} the cluster bounds. 905 */ 906 Cluster.prototype.getBounds = function() { 907 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 908 var markers = this.getMarkers(); 909 for (var i = 0, marker; marker = markers[i]; i++) { 910 bounds.extend(marker.getPosition()); 911 } 912 return bounds; 913 }; 914 915 916 /** 917 * Removes the cluster 918 */ 919 Cluster.prototype.remove = function() { 920 this.clusterIcon_.remove(); 921 this.markers_.length = 0; 922 delete this.markers_; 923 }; 924 925 926 /** 927 * Returns the center of the cluster. 928 * 929 * @return {number} The cluster center. 930 */ 931 Cluster.prototype.getSize = function() { 932 return this.markers_.length; 933 }; 934 935 936 /** 937 * Returns the center of the cluster. 938 * 939 * @return {Array.<google.maps.Marker>} The cluster center. 940 */ 941 Cluster.prototype.getMarkers = function() { 942 return this.markers_; 943 }; 944 945 946 /** 947 * Returns the center of the cluster. 948 * 949 * @return {google.maps.LatLng} The cluster center. 950 */ 951 Cluster.prototype.getCenter = function() { 952 return this.center_; 953 }; 954 955 956 /**
957 * Calculated the extended bounds of the cluster with the grid. 958 * 959 * @private 960 */ 961 Cluster.prototype.calculateBounds_ = function() { 962 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 963 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 964 }; 965 966 967 /** 968 * Determines if a marker lies in the clusters bounds. 969 * 970 * @param {google.maps.Marker} marker The marker to check. 971 * @return {boolean} True if the marker lies in the bounds. 972 */ 973 Cluster.prototype.isMarkerInClusterBounds = function(marker) { 974 return this.bounds_.contains(marker.getPosition()); 975 }; 976 977 978 /** 979 * Returns the map that the cluster is associated with. 980 * 981 * @return {google.maps.Map} The map. 982 */ 983 Cluster.prototype.getMap = function() { 984 return this.map_; 985 }; 986 987 988 /** 989 * Updates the cluster icon 990 */ 991 Cluster.prototype.updateIcon = function() { 992 var zoom = this.map_.getZoom(); 993 var mz = this.markerClusterer_.getMaxZoom(); 994 995 if (mz && zoom > mz) { 996 // The zoom is greater than our max zoom so show all the markers in cluster. 997 for (var i = 0, marker; marker = this.markers_[i]; i++) { 998 marker.setMap(this.map_); 999 } 1000 return; 1001 } 1002 1003 if (this.markers_.length < this.minClusterSize_) { 1004 // Min cluster size not yet reached. 1005 this.clusterIcon_.hide(); 1006 return; 1007 } 1008 1009 var numStyles = this.markerClusterer_.getStyles().length; 1010 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 1011 this.clusterIcon_.setCenter(this.center_); 1012 this.clusterIcon_.setSums(sums); 1013 this.clusterIcon_.show(); 1014 }; 1015 1016 1017 /** 1018 * A cluster icon 1019 * 1020 * @param {Cluster} cluster The cluster to be associated with. 1021 * @param {Object} styles An object that has style properties: 1022 * 'url': (string) The image url. 1023 * 'height': (number) The image height. 1024 * 'width': (number) The image width. 1025 * 'anchor': (Array) The anchor position of the label text. 1026 * 'textColor': (string) The text color. 1027 * 'textSize': (number) The text size. 1028 * 'backgroundPosition: (string) The background postition x, y. 1029 * @param {number=} opt_padding Optional padding to apply to the cluster icon. 1030 * @constructor 1031 * @extends google.maps.OverlayView 1032 * @ignore 1033 */ 1034 function ClusterIcon(cluster, styles, opt_padding) { 1035 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 1036 1037 this.styles_ = styles; 1038 this.padding_ = opt_padding || 0; 1039 this.cluster_ = cluster; 1040 this.center_ = null; 1041 this.map_ = cluster.getMap(); 1042 this.div_ = null; 1043 this.sums_ = null; 1044 this.visible_ = false; 1045 1046 this.setMap(this.map_); 1047 } 1048 1049 1050 /** 1051 * Triggers the clusterclick event and zoom's if the option is set. 1052 */ 1053 ClusterIcon.prototype.triggerClusterClick = function() { 1054 var markerClusterer = this.cluster_.getMarkerClusterer(); 1055 1056 // Trigger the clusterclick event. 1057 google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); 1058 1059 if (markerClusterer.isZoomOnClick()) { 1060 // Zoom into the cluster. 1061 this.map_.fitBounds(this.cluster_.getBounds()); 1062 } 1063 }; 1064 1065 1066 /** 1067 * Adding the cluster icon to the dom. 1068 * @ignore 1069 */ 1070 ClusterIcon.prototype.onAdd = function() { 1071 this.div_ = document.createElement('DIV'); 1072 if (this.visible_) { 1073 var pos = this.getPosFromLatLng_(this.center_); 1074 this.div_.style.cssText = this.createCss(pos); 1075 this.div_.innerHTML = this.sums_.text; 1076 var markerClusterer = this.cluster_.getMarkerClusterer(); 1077 if (markerClusterer.cssClass_) { 1078 this.div_.className = markerClusterer.cssClass_; 1079 } 1080 } 1081 1082 var panes = this.getPanes(); 1083 panes.overlayMouseTarget.appendChild(this.div_); 1084 1085 var that = this; 1086 google.maps.event.addDomListener(this.div_, 'click', function() { 1087 that.triggerClusterClick(); 1088 }); 1089 }; 1090 1091 1092 /** 1093 * Returns the position to place the div dending on the latlng. 1094 * 1095 * @param {google.maps.LatLng} latlng The position in latlng. 1096 * @return {google.maps.Point} The position in pixels. 1097 * @private 1098 */ 1099 ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { 1100 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 1101 pos.x -= parseInt(this.width_ / 2, 10); 1102 pos.y -= parseInt(this.height_ / 2, 10); 1103 return pos; 1104 }; 1105 1106 1107 /** 1108 * Draw the icon. 1109 * @ignore 1110 */ 1111 ClusterIcon.prototype.draw = function() { 1112 if (this.visible_) { 1113 var pos = this.getPosFromLatLng_(this.center_); 1114 this.div_.style.top = pos.y + 'px'; 1115 this.div_.style.left = pos.x + 'px'; 1116 } 1117 }; 1118 1119 1120 /** 1121 * Hide the icon. 1122 */ 1123 ClusterIcon.prototype.hide = function() { 1124 if (this.div_) { 1125 this.div_.style.display = 'none'; 1126 } 1127 this.visible_ = false;
1128 }; 1129 1130 1131 /** 1132 * Position and show the icon. 1133 */ 1134 ClusterIcon.prototype.show = function() { 1135 if (this.div_) { 1136 var pos = this.getPosFromLatLng_(this.center_); 1137 this.div_.style.cssText = this.createCss(pos); 1138 this.div_.style.display = ''; 1139 } 1140 this.visible_ = true; 1141 }; 1142 1143 1144 /** 1145 * Remove the icon from the map 1146 */ 1147 ClusterIcon.prototype.remove = function() { 1148 this.setMap(null); 1149 }; 1150 1151 1152 /** 1153 * Implementation of the onRemove interface. 1154 * @ignore 1155 */ 1156 ClusterIcon.prototype.onRemove = function() { 1157 if (this.div_ && this.div_.parentNode) { 1158 this.hide(); 1159 this.div_.parentNode.removeChild(this.div_); 1160 this.div_ = null; 1161 } 1162 }; 1163 1164 1165 /** 1166 * Set the sums of the icon. 1167 * 1168 * @param {Object} sums The sums containing: 1169 * 'text': (string) The text to display in the icon. 1170 * 'index': (number) The style index of the icon. 1171 */ 1172 ClusterIcon.prototype.setSums = function(sums) { 1173 this.sums_ = sums; 1174 this.text_ = sums.text; 1175 this.index_ = sums.index; 1176 if (this.div_) { 1177 this.div_.innerHTML = sums.text; 1178 } 1179 1180 this.useStyle(); 1181 }; 1182 1183 1184 /** 1185 * Sets the icon to the the styles. 1186 */ 1187 ClusterIcon.prototype.useStyle = function() { 1188 var index = Math.max(0, this.sums_.index - 1); 1189 index = Math.min(this.styles_.length - 1, index); 1190 var style = this.styles_[index]; 1191 this.url_ = style['url']; 1192 this.height_ = style['height']; 1193 this.width_ = style['width']; 1194 this.textColor_ = style['textColor']; 1195 this.anchor_ = style['anchor']; 1196 this.textSize_ = style['textSize']; 1197 this.backgroundPosition_ = style['backgroundPosition']; 1198 }; 1199 1200 1201 /** 1202 * Sets the center of the icon. 1203 * 1204 * @param {google.maps.LatLng} center The latlng to set as the center. 1205 */ 1206 ClusterIcon.prototype.setCenter = function(center) { 1207 this.center_ = center; 1208 }; 1209 1210 1211 /** 1212 * Create the css text based on the position of the icon. 1213 * 1214 * @param {google.maps.Point} pos The position. 1215 * @return {string} The css style text. 1216 */ 1217 ClusterIcon.prototype.createCss = function(pos) { 1218 var style = []; 1219 var markerClusterer = this.cluster_.getMarkerClusterer(); 1220 if (!markerClusterer.cssClass_) { 1221 style.push('background-image:url(' + this.url_ + ');'); 1222 var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; 1223 style.push('background-position:' + backgroundPosition + ';'); 1224 1225 if (typeof this.anchor_ === 'object') { 1226 if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && 1227 this.anchor_[0] < this.height_) { 1228 style.push('height:' + (this.height_ - this.anchor_[0]) + 1229 'px; padding-top:' + this.anchor_[0] + 'px;'); 1230 } else { 1231 style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 'px;'); 1232 } 1233 if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && 1234 this.anchor_[1] < this.width_) { 1235 style.push('width:' + (this.width_ - this.anchor_[1]) + 1236 'px; padding-left:' + this.anchor_[1] + 'px;'); 1237 } else { 1238 style.push('width:' + this.width_ + 'px; text-align:center;'); 1239 } 1240 } else { 1241 style.push('height:' + this.height_ + 'px; line-height:' + 1242 this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); 1243 } 1244 1245 var txtColor = this.textColor_ ? this.textColor_ : 'black'; 1246 var txtSize = this.textSize_ ? this.textSize_ : 11; 1247 1248 style.push('cursor:pointer; color:' + txtColor + '; position:absolute; font-size:' + 1249 txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); 1250 } 1251 else { 1252 style.push('top:' + pos.y + 'px; left:' + 1253 pos.x + 'px;'); 1254 } 1255 return style.join(''); 1256 }; 1257 1258 1259 // Export Symbols for Closure 1260 // If you are not going to compile with closure then you can remove the 1261 // code below. 1262 window['MarkerClusterer'] = MarkerClusterer; 1263 MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; 1264 MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; 1265 MarkerClusterer.prototype['clearMarkers'] = 1266 MarkerClusterer.prototype.clearMarkers; 1267 MarkerClusterer.prototype['fitMapToMarkers'] = 1268 MarkerClusterer.prototype.fitMapToMarkers; 1269 MarkerClusterer.prototype['getCalculator'] = 1270 MarkerClusterer.prototype.getCalculator; 1271 MarkerClusterer.prototype['getGridSize'] = 1272 MarkerClusterer.prototype.getGridSize; 1273 MarkerClusterer.prototype['getExtendedBounds'] = 1274 MarkerClusterer.prototype.getExtendedBounds;
1275 MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; 1276 MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; 1277 MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; 1278 MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; 1279 MarkerClusterer.prototype['getTotalClusters'] = 1280 MarkerClusterer.prototype.getTotalClusters; 1281 MarkerClusterer.prototype['getTotalMarkers'] = 1282 MarkerClusterer.prototype.getTotalMarkers; 1283 MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; 1284 MarkerClusterer.prototype['removeMarker'] = 1285 MarkerClusterer.prototype.removeMarker; 1286 MarkerClusterer.prototype['removeMarkers'] = 1287 MarkerClusterer.prototype.removeMarkers; 1288 MarkerClusterer.prototype['resetViewport'] = 1289 MarkerClusterer.prototype.resetViewport; 1290 MarkerClusterer.prototype['repaint'] = 1291 MarkerClusterer.prototype.repaint; 1292 MarkerClusterer.prototype['setCalculator'] = 1293 MarkerClusterer.prototype.setCalculator; 1294 MarkerClusterer.prototype['setGridSize'] = 1295 MarkerClusterer.prototype.setGridSize; 1296 MarkerClusterer.prototype['setMaxZoom'] = 1297 MarkerClusterer.prototype.setMaxZoom; 1298 MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; 1299 MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; 1300 1301 Cluster.prototype['getCenter'] = Cluster.prototype.getCenter; 1302 Cluster.prototype['getSize'] = Cluster.prototype.getSize; 1303 Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers; 1304 1305 ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd; 1306 ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw; 1307 ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;
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.