PageSourceSearch

https://reseau-cti.com/wp-content/plugins/gmap-point-list-lch/js/markermanager.js?ver=1.0

js reseau-cti.com collected 2026-10-02 06:07:47 UTC 29,264 bytes, 978 lines download raw bytes

1/**
2 * @name MarkerManager v3
3 * @version 1.0
4 * @copyright (c) 2007 Google Inc.
5 * @author Doug Ricket, Bjorn Brala (port to v3), others,
6 *
7 * @fileoverview Marker manager is an interface between the map and the user,
8 * designed to manage adding and removing many points when the viewport changes.
9 * <br /><br />
10 * <b>How it Works</b>:<br/>
11 * The MarkerManager places its markers onto a grid, similar to the map tiles.
12 * When the user moves the viewport, it computes which grid cells have
13 * entered or left the viewport, and shows or hides all the markers in those
14 * cells.
15 * (If the users scrolls the viewport beyond the markers that are loaded,
16 * no markers will be visible until the <code>EVENT_moveend</code>
17 * triggers an update.)
18 * In practical consequences, this allows 10,000 markers to be distributed over
19 * a large area, and as long as only 100-200 are visible in any given viewport,
20 * the user will see good performance corresponding to the 100 visible markers,
21 * rather than poor performance corresponding to the total 10,000 markers.
22 * Note that some code is optimized for speed over space,
23 * with the goal of accommodating thousands of markers.
24 */
25
26/*
27 * Licensed under the Apache License, Version 2.0 (the "License");
28 * you may not use this file except in compliance with the License.
29 * You may obtain a copy of the License at
30 *
31 *     http://www.apache.org/licenses/LICENSE-2.0
32 *
33 * Unless required by applicable law or agreed to in writing, software
34 * distributed under the License is distributed on an "AS IS" BASIS,
35 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36 * See the License for the specific language governing permissions and
37 * limitations under the License.
38 */
39
40/**
41 * @name MarkerManagerOptions
42 * @class This class represents optional arguments to the {@link MarkerManager}
43 *     constructor.
44 * @property {Number} maxZoom Sets the maximum zoom level monitored by a
45 *     marker manager. If not given, the manager assumes the maximum map zoom
46 *     level. This value is also used when markers are added to the manager
47 *     without the optional {@link maxZoom} parameter.
48 * @property {Number} borderPadding Specifies, in pixels, the extra padding
49 *     outside the map's current viewport monitored by a manager. Markers that
50 *     fall within this padding are added to the map, even if they are not fully
51 *     visible.
52 * @property {Boolean} trackMarkers=false Indicates whether or not a marker
53 *     manager should track markers' movements. If you wish to move managed
54 *     markers using the {@link setPoint}/{@link setLatLng} methods,
55 *     this option should be set to {@link true}.
56 */
57
58/**
59 * Creates a new MarkerManager that will show/hide markers on a map.
60 *
61 * Events:
62 * @event changed (Parameters: shown bounds, shown markers) Notify listeners when the state of what is displayed changes.
63 * @event loaded MarkerManager has succesfully been initialized.
64 *
65 * @constructor
66 * @param {Map} map The map to manage.
67 * @param {Object} opt_opts A container for optional arguments:
68 *   {Number} maxZoom The maximum zoom level for which to create tiles.
69 *   {Number} borderPadding The width in pixels beyond the map border,
70 *                   where markers should be display.
71 *   {Boolean} trackMarkers Whether or not this manager should track marker
72 *                   movements.
73 */
74function MarkerManager(map, opt_opts) {
75  var me = this;
76  me.map_ = map;
77  me.mapZoom_ = map.getZoom();
78
79  me.projectionHelper_ = new ProjectionHelperOverlay(map);
80  google.maps.event.addListener(me.projectionHelper_, 'ready', function () {
81    me.projection_ = this.getProjection();
82    me.initialize(map, opt_opts);
83  });
84}
85
86
87MarkerManager.prototype.initialize = function (map, opt_opts) {
88  var me = this;
89
90  opt_opts = opt_opts || {};
91  me.tileSize_ = MarkerManager.DEFAULT_TILE_SIZE_;
92
93  var mapTypes = map.mapTypes;
94
95  // Find max zoom level
96  var mapMaxZoom = 1;
97  for (var sType in mapTypes ) {
98    if (typeof map.mapTypes.get(sType) === 'object' && typeof map.mapTypes.get(sType).maxZoom === 'number') {
99      var mapTypeMaxZoom = map.mapTypes.get(sType).maxZoom;
100      if (mapTypeMaxZoom >
100 mapMaxZoom) {
101        mapMaxZoom = mapTypeMaxZoom;
102      }
103    }
104  }
105
106  me.maxZoom_  = opt_opts.maxZoom || 19;
107
108  me.trackMarkers_ = opt_opts.trackMarkers;
109  me.show_ = opt_opts.show || true;
110
111  var padding;
112  if (typeof opt_opts.borderPadding === 'number') {
113    padding = opt_opts.borderPadding;
114  } else {
115    padding = MarkerManager.DEFAULT_BORDER_PADDING_;
116  }
117  // The padding in pixels beyond the viewport, where we will pre-load markers.
118  me.swPadding_ = new google.maps.Size(-padding, padding);
119  me.nePadding_ = new google.maps.Size(padding, -padding);
120  me.borderPadding_ = padding;
121
122  me.gridWidth_ = {};
123
124  me.grid_ = {};
125  me.grid_[me.maxZoom_] = {};
126  me.numMarkers_ = {};
127  me.numMarkers_[me.maxZoom_] = 0;
128
129
130  google.maps.event.addListener(map, 'dragend', function () {
131    me.onMapMoveEnd_();
132  });
133  google.maps.event.addListener(map, 'zoom_changed', function () {
134    me.onMapMoveEnd_();
135  });
136
137
138
139  /**
140   * This closure provide easy access to the map.
141   * They are used as callbacks, not as methods.
142   * @param GMarker marker Marker to be removed from the map
143   * @private
144   */
145  me.removeOverlay_ = function (marker) {
146    marker.setMap(null);
147    me.shownMarkers_--;
148  };
149
150  /**
151   * This closure provide easy access to the map.
152   * They are used as callbacks, not as methods.
153   * @param GMarker marker Marker to be added to the map
154   * @private
155   */
156  me.addOverlay_ = function (marker) {
157    if (me.show_) {
158      marker.setMap(me.map_);
159      me.shownMarkers_++;
160    }
161  };
162
163  me.resetManager_();
164  me.shownMarkers_ = 0;
165
166  me.shownBounds_ = me.getMapGridBounds_();
167
168  google.maps.event.trigger(me, 'loaded');
169
170};
171
172/**
173 *  Default tile size used for deviding the map into a grid.
174 */
175MarkerManager.DEFAULT_TILE_SIZE_ = 1024;
176
177/*
178 *  How much extra space to show around the map border so
179 *  dragging doesn't result in an empty place.
180 */
181MarkerManager.DEFAULT_BORDER_PADDING_ = 100;
182
183/**
184 *  Default tilesize of single tile world.
185 */
186MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE = 256;
187
188
189/**
190 * Initializes MarkerManager arrays for all zoom levels
191 * Called by constructor and by clearAllMarkers
192 */
193MarkerManager.prototype.resetManager_ = function () {
194  var mapWidth = MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE;
195  for (var zoom = 0; zoom <= this.maxZoom_; ++zoom) {
196    this.grid_[zoom] = {};
197    this.numMarkers_[zoom] = 0;
198    this.gridWidth_[zoom] = Math.ceil(mapWidth / this.tileSize_);
199    mapWidth <<= 1;
200  }
201
202};
203
204/**
205 * Removes all markers in the manager, and
206 * removes any visible markers from the map.
207 */
208MarkerManager.prototype.clearMarkers = function () {
209  this.processAll_(this.shownBounds_, this.removeOverlay_);
210  this.resetManager_();
211};
212
213
214/**
215 * Gets the tile coordinate for a given latlng point.
216 *
217 * @param {LatLng} latlng The geographical point.
218 * @param {Number} zoom The zoom level.
219 * @param {google.maps.Size} padding The padding used to shift the pixel coordinate.
220 *               Used for expanding a bounds to include an extra padding
221 *               of pixels surrounding the bounds.
222 * @return {GPoint} The point in tile coordinates.
223 *
224 */
225MarkerManager.prototype.getTilePoint_ = function (latlng, zoom, padding) {
226
227  var pixelPoint = this.projectionHelper_.LatLngToPixel(latlng, zoom);
228
229  var point = new google.maps.Point(
230    Math.floor((pixelPoint.x + padding.width) / this.tileSize_),
231    Math.floor((pixelPoint.y + padding.height) / this.tileSize_)
232  );
233
234  return point;
235};
236
237
238/**
239 * Finds the appropriate place to add the marker to the grid.
240 * Optimized for speed; does not actually add the marker to the map.
241 * Designed for batch-processing thousands of markers.
242 *
243 * @param {Marker} marker The marker to add.
244 * @param {Number} minZoom The minimum zoom for displaying the marker.
245 * @param {Number} maxZoom The maximum zoom for displaying the marker.
246 */
247MarkerManager.prototype.addMarkerBatch_ = function (marker, minZoom, maxZoom) {
248  var me = this;
249
250  var mPoint = marker.getPosition();
251  marker.MarkerManager_minZoom = minZoom;
252
253
254  // Tracking markers is expensive, so we do this only if the
255  // user explicitly requested it when creating marker manager.
256  if (this.trackMarkers_) {
257    google.maps.event.addListener(marker, 'changed', function (a, b, c) {
258      me.onMarkerMoved_(a, b, c);
259    });
260  }
261
262  var gridPoint = this.getTilePoint_(mPoint, maxZoom, new google.maps.Size(0, 0, 0, 0));
263
264  for (var zoom = maxZoom; zoom >= minZoom; zoom--) {
265    var cell = this.getGridCellCreate_(gridPoint.x, gridPoint.y, zoom);
266    cell.push(marker);
267
268    gridPoint.x = gridPoint.x >> 1;
269    gridPoint.y = gridPoint.y >> 1;
270  }
271};
272
273
274/**
275 * Returns whether or not the given point is visible in the shown boun
275ds. This
276 * is a helper method that takes care of the corner case, when shownBounds have
277 * negative minX value.
278 *
279 * @param {Point} point a point on a grid.
280 * @return {Boolean} Whether or not the given point is visible in the currently
281 * shown bounds.
282 */
283MarkerManager.prototype.isGridPointVisible_ = function (point) {
284  var vertical = this.shownBounds_.minY <= point.y &&
285      point.y <= this.shownBounds_.maxY;
286  var minX = this.shownBounds_.minX;
287  var horizontal = minX <= point.x && point.x <= this.shownBounds_.maxX;
288  if (!horizontal && minX < 0) {
289    // Shifts the negative part of the rectangle. As point.x is always less
290    // than grid width, only test shifted minX .. 0 part of the shown bounds.
291    var width = this.gridWidth_[this.shownBounds_.z];
292    horizontal = minX + width <= point.x && point.x <= width - 1;
293  }
294  return vertical && horizontal;
295};
296
297
298/**
299 * Reacts to a notification from a marker that it has moved to a new location.
300 * It scans the grid all all zoom levels and moves the marker from the old grid
301 * location to a new grid location.
302 *
303 * @param {Marker} marker The marker that moved.
304 * @param {LatLng} oldPoint The old position of the marker.
305 * @param {LatLng} newPoint The new position of the marker.
306 */
307MarkerManager.prototype.onMarkerMoved_ = function (marker, oldPoint, newPoint) {
308  // NOTE: We do not know the minimum or maximum zoom the marker was
309  // added at, so we start at the absolute maximum. Whenever we successfully
310  // remove a marker at a given zoom, we add it at the new grid coordinates.
311  var zoom = this.maxZoom_;
312  var changed = false;
313  var oldGrid = this.getTilePoint_(oldPoint, zoom, new google.maps.Size(0, 0, 0, 0));
314  var newGrid = this.getTilePoint_(newPoint, zoom, new google.maps.Size(0, 0, 0, 0));
315  while (zoom >= 0 && (oldGrid.x !== newGrid.x || oldGrid.y !== newGrid.y)) {
316    var cell = this.getGridCellNoCreate_(oldGrid.x, oldGrid.y, zoom);
317    if (cell) {
318      if (this.removeFromArray_(cell, marker)) {
319        this.getGridCellCreate_(newGrid.x, newGrid.y, zoom).push(marker);
320      }
321    }
322    // For the current zoom we also need to update the map. Markers that no
323    // longer are visible are removed from the map. Markers that moved into
324    // the shown bounds are added to the map. This also lets us keep the count
325    // of visible markers up to date.
326    if (zoom === this.mapZoom_) {
327      if (this.isGridPointVisible_(oldGrid)) {
328        if (!this.isGridPointVisible_(newGrid)) {
329          this.removeOverlay_(marker);
330          changed = true;
331        }
332      } else {
333        if (this.isGridPointVisible_(newGrid)) {
334          this.addOverlay_(marker);
335          changed = true;
336        }
337      }
338    }
339    oldGrid.x = oldGrid.x >> 1;
340    oldGrid.y = oldGrid.y >> 1;
341    newGrid.x = newGrid.x >> 1;
342    newGrid.y = newGrid.y >> 1;
343    --zoom;
344  }
345  if (changed) {
346    this.notifyListeners_();
347  }
348};
349
350
351/**
352 * Removes marker from the manager and from the map
353 * (if it's currently visible).
354 * @param {GMarker} marker The marker to delete.
355 */
356MarkerManager.prototype.removeMarker = function (marker) {
357  var zoom = this.maxZoom_;
358  var changed = false;
359  var point = marker.getPosition();
360  var grid = this.getTilePoint_(point, zoom, new google.maps.Size(0, 0, 0, 0));
361  while (zoom >= 0) {
362    var cell = this.getGridCellNoCreate_(grid.x, grid.y, zoom);
363
364    if (cell) {
365      this.removeFromArray_(cell, marker);
366    }
367    // For the current zoom we also need to update the map. Markers that no
368    // longer are visible are removed from the map. This also lets us keep the count
369    // of visible markers up to date.
370    if (zoom === this.mapZoom_) {
371      if (this.isGridPointVisible_(grid)) {
372        this.removeOverlay_(marker);
373        changed = true;
374      }
375    }
376    grid.x = grid.x >> 1;
377    grid.y = grid.y >> 1;
378    --zoom;
379  }
380  if (changed) {
381    this.notifyListeners_();
382  }
383  this.numMarkers_[marker.MarkerManager_minZoom]--;
384};
385
386
387/**
388 * Add many markers at once.
389 * Does not actually update the map, just the internal grid.
390 *
391 * @param {Array of Marker} markers The markers to add.
392 * @param {Number} minZoom The minimum zoom level to display the markers.
393 * @param {Number} opt_maxZoom The maximum zoom level to display the markers.
394 */
395MarkerManager.prototype.addMarkers = function (markers, minZoom, opt_maxZoom) {
396  var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
397  for (var i = markers.length - 1; i >= 0; i--) {
398    this.addMarkerBatch_(markers[i], minZoom, maxZoom);
399  }
400
401  this.numMarkers_[minZoom] += markers.length;
402};
403
404
405/**
406 * Returns the value of the optional maximum zoom. This method is defined so
407 * that we have just one place where optional maximum zoom is calculated.
408 *
409 * @param {Number} opt_maxZoom The optinal maximum zoom.
410 * @return The maximum zoom.
411 */
412MarkerManager.prototype.getOptMaxZoom_ = function (opt_maxZoom) {
413  return opt_maxZoom || this.maxZoom_;
414};
415
416
417/**
418 * Calculates the total number of markers potentially visible at a given
419 * zoom level.
420 *
421 * @param {Number} zoom The zoom level to check.
422 */
423MarkerManager.prototype.getMarkerCount = function (zoom) {
424  var total = 0;
425  for (var z = 0; z <= zoom; z++) {
426    total += this.numMarkers_[z];
427  }
428  return total;
429};
430
431/**
432 * Returns a marker given latitude, longitude and zoom. If the marker does not
433 * exist, the method will return a new marker. If a new marker is created,
434 * it will NOT be added to the manager.
435 *
436 * @param {Number} lat - the latitude of a marker.
437 * @param {Number} lng - the longitude of a marker.
438 * @param {Number} zoom - the zoom level
439 * @return {GMarker} marker - the marker found at lat and lng
440 */
441MarkerManager.prototype.getMarker = function (lat, lng, zoom) {
442  var mPoint = new google.maps.LatLng(lat, lng);
443  var gridPoint = this.getTilePoint_(mPoint, zoom, new google.maps.Size(0, 0, 0, 0));
444
445  var marker = new google.maps.Marker({position: mPoint});
446
447  var cellArray = this.getGridCellNoCreate_(gridPoint.x, gridPoint.y, zoom);
448  if (cellArray !== undefined) {
449    for (var i = 0; i < cellArray.length; i++)
450    {
451      if (lat === cellArray[i].getLatLng().lat() && lng === cellArray[i].getLatLng().lng()) {
452        marker = cellArray[i];
453      }
454    }
455  }
456  return marker;
457};
458
459/**
460 * Add a single marker to the map.
461 *
462 * @param {Marker} marker The marker to add.
463 * @param {Number} minZoom The minimum zoom level to display the marker.
464 * @param {Number} opt_maxZoom The maximum zoom level to display the marker.
465 */
466MarkerManager.prototype.addMarker = function (marker, minZoom, opt_maxZoom) {
467  var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
468  this.addMarkerBatch_(marker, minZoom, maxZoom);
469  var gridPoint = this.getTilePoint_(marker.getPosition(), this.mapZoom_, new google.maps.Size(0, 0, 0, 0));
470  if (this.isGridPointVisible_(gridPoint) &&
471      minZoom <= this.shownBounds_.z &&
472      this.shownBounds_.z <= maxZoom) {
473    this.addOverlay_(marker);
474    this.notifyListeners_();
475  }
476  this.numMarkers_[minZoom]++;
477};
478
479
480/**
481 * Helper class to create a bounds of INT ranges.
482 * @param bounds Array.<Object.<string, number>> Bounds object.
483 * @constructor
484 */
485function GridBounds(bounds) {
486  // [sw, ne]
487
488  this.minX = Math.min(bounds[0].x, bounds[1].x);
489  this.maxX = Math.max(bounds[0].x, bounds[1].x);
490  this.minY = Math.min(bounds[0].y, bounds[1].y);
491  this.maxY = Math.max(bounds[0].y, bounds[1].y);
492
493}
494
495/**
496 * Returns true if this bounds equal the given bounds.
497 * @param {GridBounds} gridBounds GridBounds The bounds to test.
498 * @return {Boolean} This Bounds equals the given GridBounds.
499 */
500GridBounds.prototype.equals = function (gridBounds) {
501  if (this.maxX === gridBounds.maxX && this.maxY === gridBounds.maxY && this.minX === gridBounds.minX && this.minY === gridBounds.minY) {
502    return true;
503  } else {
504    return false;
505  }
506};
507
508/**
509 * Returns true if this bounds (inclusively) contains the given point.
510 * @param {Point} point  The point to test.
511 * @return {Boolean} This Bounds contains the given Point.
512 */
513GridBounds.prototype.containsPoint = function (point) {
514  var outer = this;
515  return (outer.minX <= point.x && outer.maxX >= point.x && outer.minY <= point.y && outer.maxY >= point.y);
516};
517
518/**
519 * Get a cell in the grid, creating it first if necessary.
520 *
521 * Optimization candidate
522 *
523 * @param {Number} x The x coordinate of the cell.
524 * @param {Number} y The y coordinate of the cell.
525 * @param {Number} z The z coordinate of the cell.
526 * @return {Array} The cell in the array.
527 */
528MarkerManager.prototype.getGridCellCreate_ = function (x, y, z) {
529  var grid = this.grid_[z];
530  if (x < 0) {
531    x += this.gridWidth_[z];
532  }
533  var gridCol = grid[x];
534  if (!gridCol) {
535    gridCol = grid[x] = [];
536    return (gridCol[y] = []);
537  }
538  var gridCell = gridCol[y];
539  if (!gridCell) {
540    return (gridCol[y] = []);
541  }
542  return gridCell;
543};
544
545
546/**
547 * Get a cell in the grid, returning undefined if it does not exist.
548 *
549 * NOTE: Optimized for speed -- otherwise could combine with getGridCellCreate_.
550 *
551 * @param {Number} x The x coordinate of the cell.
552 * @param {Number} y The y coordinate of the cell.
553 * @param {Number} z The z coordinate of the cell.
554 * @return {Array} The cell in the array.
555 */
556MarkerManager.prototype.getGridCellNoCreate_ = function (x, y, z) {
557  var grid = this.grid_[z];
558
559  if (x < 0) {
560    x += this.gridWidth_[z];
561  }
562  if (grid !== Array) {
563    return undefined;
564  }
565  var gridCol = grid[x];
566  return gridCol ? gridCol[y] : undefined;
567};
568
569
570/**
571 * Turns at geographical bounds into a grid-space bounds.
572 *
573 * @param {LatLngBounds} bounds The geographical bounds.
574 * @param {Number} zoom The zoom level of the bounds.
575 * @param {google.maps.Size} swPadding The padding in pixels to extend beyond the
576 * given bounds.
577 * @param {google.maps.Size} nePadding The padding in pixels to extend beyond the
578 * given bounds.
579 * @return {GridBounds} The bounds in grid space.
580 */
581MarkerManager.prototype.getGridBounds_ = function (bounds, zoom, swPadding, nePadding) {
582  zoom = Math.min(zoom, this.maxZoom_);
583
584  var bl = bounds.getSouthWest();
585  var tr = bounds.getNorthEast();
586  var sw = this.getTilePoint_(bl, zoom, swPadding);
587
588  var ne = this.getTilePoint_(tr, zoom, nePadding);
589  var gw = this.gridWidth_[zoom];
590
591  // Crossing the prime meridian requires correction of bounds.
592  if (tr.lng() < bl.lng() || ne.x < sw.x) {
593    sw.x -= gw;
594  }
595  if (ne.x - sw.x  + 1 >= gw) {
596    // Computed grid bounds are larger than the world; truncate.
597    sw.x = 0;
598    ne.x = gw - 1;
599  }
600
601  var gridBounds = new GridBounds([sw, ne]);
602  gridBounds.z = zoom;
603
604  return gridBounds;
605};
606
607
608/**
609 * Gets the grid-space bounds for the current map viewport.
610 *
611 * @return {Bounds} The bounds in grid space.
612 */
613MarkerManager.prototype.getMapGridBounds_ = function () {
614  return this.getGridBounds_(this.map_.getBounds(), this.mapZoom_, this.swPadding_, this.nePadding_);
615};
616
617
618/**
619 * Event listener for map:movend.
620 * NOTE: Use a timeout so that the user is not blocked
621 * from moving the map.
622 *
623 * Removed this because a a lack of a scopy override/callback function on events.
624 */
625MarkerManager.prototype.onMapMoveEnd_ = function () {
626  this.objectSetTimeout_(this, this.updateMarkers_, 0);
627};
628
629
630/**
631 * Call a function or evaluate an expression after a specified number of
632 * milliseconds.
633 *
634 * Equivalent to the standard window.setTimeout function, but the given
635 * function executes as a method of this instance. So the function passed to
636 * objectSetTimeout can contain references to this.
637 *    objectSetTimeout(this, function () { alert(this.x) }, 1000);
638 *
639 * @param {Object} object  The target object.
640 * @param {Function} command  The command to run.
641 * @param {Number} milliseconds  The delay.
642 * @return {Boolean}  Success.
643 */
644MarkerManager.prototype.objectSetTimeout_ = function (object, command, milliseconds) {
645  return window.setTimeout(function () {
646    command.call(object);
647  }, milliseconds);
648};
649
650
651/**
652 * Is this layer visible?
653 *
654 * Returns visibility setting
655 *
656 * @return {Boolean} Visible
657 */
658MarkerManager.prototype.visible = function () {
659  return this.show_ ? true : false;
660};
661
662
663/**
664 * Returns true if the manager is hidden.
665 * Otherwise returns false.
666 * @return {Boolean} Hidden
667 */
668MarkerManager.prototype.isHidden = function () {
669  return !this.show_;
670};
671
672
673/**
674 * Shows the manager if it's currently hidden.
675 */
676MarkerManager.prototype.show = function () {
677  this.show_ = true;
678  this.refresh();
679};
680
681
682/**
683 * Hides the manager if it's currently visible
684 */
685MarkerManager.prototype.hide = function () {
686  this.show_ = false;
687  this.refresh();
688};
689
690
691/**
692 * Toggles the visibility of the manager.
693 */
694MarkerManager.prototype.toggle = function () {
695  this.show_ = !this.show_;
696  this.refresh();
697};
698
699
700/**
701 * Refresh forces the marker-manager into a good state.
702 * <ol>
703 *   <li>If never before initialized, shows all the markers.</li>
704 *   <li>If previously initialized, removes and re-adds all markers.</li>
705 * </ol>
706 */
707MarkerManager.prototype.refresh = function () {
708  if (this.shownMarkers_ > 0) {
709    this.processAll_(this.shownBounds_, this.removeOverlay_);
710  }
711  // An extra check on this.show_ to increase performance (no need to processAll_)
712  if (this.show_) {
713    this.processAll_(this.shownBounds_, this.addOverlay_);
714  }
715  this.notifyListeners_();
716};
717
718
719/**
720 * After the viewport may have changed, add or remove markers as needed.
721 */
722MarkerManager.prototype.updateMarkers_ = function () {
723  this.mapZoom_ = this.map_.getZoom();
724  var newBounds = this.getMapGridBounds_();
725
726  // If the move does not include new grid sections,
727  // we have no work to do:
728  if (newBounds.equals(this.shownBounds_) && newBounds.z === this.shownBounds_.z) {
729    return;
730  }
731
732  if (newBounds.z !== this.shownBounds_.z) {
733    this.processAll_(this.shownBounds_, this.removeOverlay_);
734    if (this.show_) { // performance
735      this.processAll_(newBounds, this.addOverlay_);
736    }
737  } else {
738    // Remove markers:
739    this.rectangleDiff_(this.shownBounds_, newBounds, this.removeCellMarkers_);
740
741    // Add markers:
742    if (this.show_) { // performance
743      this.rectangleDiff_(newBounds, this.shownBounds_, this.addCellMarkers_);
744    }
745  }
746  this.shownBounds_ = newBounds;
747
748  this.notifyListeners_();
749};
750
751
752/**
753 * Notify listeners when the state of what is displayed changes.
754 */
755MarkerManager.prototype.notifyListeners_ = function () {
756  google.maps.event.trigger(this, 'changed', this.shownBounds_, this.shownMarkers_);
757};
758
759
760/**
761 * Process all markers in the bounds provided, using a callback.
762 *
763 * @param {Bounds} bounds The bounds in grid space.
764 * @param {Function} callback The function to call for each marker.
765 */
766MarkerManager.prototype.processAll_ = function (bounds, callback) {
767  for (var x = bounds.minX; x <= bounds.maxX; x++) {
768    for (var y = bounds.minY; y <= bounds.maxY; y++) {
769      this.processCellMarkers_(x, y,  bounds.z, callback);
770    }
771  }
772};
773
774
775/**
776 * Process all markers in the grid cell, using a callback.
777 *
778 * @param {Number} x The x coordinate of the cell.
779 * @param {Number} y The y coordinate of the cell.
780 * @param {Number} z The z coordinate of the cell.
781 * @param {Function} callback The function to call for each marker.
782 */
783MarkerManager.prototype.processCellMarkers_ = function (x, y, z, callback) {
784  var cell = this.getGridCellNoCreate_(x, y, z);
785  if (cell) {
786    for (var i = cell.length - 1; i >= 0; i--) {
787      callback(cell[i]);
788    }
789  }
790};
791
792
793/**
794 * Remove all markers in a grid cell.
795 *
796 * @param {Number} x The x coordinate of the cell.
797 * @param {Number} y The y coordinate of the cell.
798 * @param {Number} z The z coordinate of the cell.
799 */
800MarkerManager.prototype.removeCellMarkers_ = function (x, y, z) {
801  this.processCellMarkers_(x, y, z, this.removeOverlay_);
802};
803
804
805/**
806 * Add all markers in a grid cell.
807 *
808 * @param {Number} x The x coordinate of the cell.
809 * @param {Number} y The y coordinate of the cell.
810 * @param {Number} z The z coordinate of the cell.
811 */
812MarkerManager.prototype.addCellMarkers_ = function (x, y, z) {
813  this.processCellMarkers_(x, y, z, this.addOverlay_);
814};
815
816
817/**
818 * Use the rectangleDiffCoords_ function to process all grid cells
819 * that are in bounds1 but not bounds2, using a callback, and using
820 * the current MarkerManager object as the instance.
821 *
822 * Pass the z parameter to the callback in addition to x and y.
823 *
824 * @param {Bounds} bounds1 The bounds of all points we may process.
825 * @param {Bounds} bounds2 The bounds of points to exclude.
826 * @param {Function} callback The callback function to call
827 *                   for each grid coordinate (x, y, z).
828 */
829MarkerManager.prototype.rectangleDiff_ = function (bounds1, bounds2, callback) {
830  var me = this;
831  me.rectangleDiffCoords_(bounds1, bounds2, function (x, y) {
832    callback.apply(me, [x, y, bounds1.z]);
833  });
834};
835
836
837/**
838 * Calls the function for all points in bounds1, not in bounds2
839 *
840 * @param {Bounds} bounds1 The bounds of all points we may process.
841 * @param {Bounds} bounds2 The bounds of points to exclude.
842 * @param {Function} callback The callback function to call
843 *                   for each grid coordinate.
844 */
845MarkerManager.prototype.rectangleDiffCoords_ = function (bounds1, bounds2, callback) {
846  var minX1 = bounds1.minX;
847  var minY1 = bounds1.minY;
848  var maxX1 = bounds1.maxX;
849  var maxY1 = bounds1.maxY;
850  var minX2 = bounds2.minX;
851  var minY2 = bounds2.minY;
852  var maxX2 = bounds2.maxX;
853  var maxY2 = bounds2.maxY;
854
855  var x, y;
856  for (x = minX1; x <= maxX1; x++) {  // All x in R1
857    // All above:
858    for (y = minY1; y <= maxY1 && y < minY2; y++) {  // y in R1 above R2
859      callback(x, y);
860    }
861    // All below:
862    for (y = Math.max(maxY2 + 1, minY1);  // y in R1 below R2
863         y <= maxY1; y++) {
864      callback(x, y);
865    }
866  }
867
868  for (y = Math.max(minY1, minY2);
869       y <= Math.min(maxY1, maxY2); y++) {  // All y in R2 and in R1
870    // Strictly left:
871    for (x = Math.min(maxX1 + 1, minX2) - 1;
872         x >= minX1; x--) {  // x in R1 left of R2
873      callback(x, y);
874    }
875    // Strictly right:
876    for (x = Math.max(minX1, maxX2 + 1);  // x in R1 right of R2
877         x <= maxX1; x++) {
878      callback(x, y);
879    }
880  }
881};
882
883
884/**
885 * Removes value from array. O(N).
886 *
887 * @param {Array} array  The array to modify.
888 * @param {any} value  The value to remove.
889 * @param {Boolean} opt_notype  Flag to disable type checking in equality.
890 * @return {Number}  The number of instances of value that were removed.
891 */
892MarkerManager.prototype.removeFromArray_ = function (array, value, opt_notype) {
893  var shift = 0;
894  for (var i = 0; i < array.length; ++i) {
895    if (array[i] === value || (opt_notype && array[i] === value)) {
896      array.splice(i--, 1);
897      shift++;
898    }
899  }
900  return shift;
901};
902
903
904
905
906
907
908
909/**
910*   Projection overlay helper. Helps in calculating
911*   that markers get into the right grid.
912*   @constructor
913*   @param {Map} map The map to manage.
914**/
915function ProjectionHelperOverlay(map) {
916
917  this.setMap(map);
918
919  var TILEFACTOR = 8;
920  var TILESIDE = 1 << TILEFACTOR;
921  var RADIUS = 7;
922
923  this._map = map;
924  this._zoom = -1;
925  this._X0 =
926  this._Y0 =
927  this._X1 =
928  this._Y1 = -1;
929
930
931}
932
933ProjectionHelperOverlay.prototype = new google.maps.OverlayView();
934
935/**
936 *  Helper function to convert Lng to X
937 *  @private
938 *  @param {float} lng
939 **/
940ProjectionHelperOverlay.prototype.LngToX_ = function (lng) {
941  return (1 + lng / 180);
942};
943
944/**
945 *  Helper function to convert Lat to Y
946 *  @private
947 *  @param {float} lat
948 **/
949ProjectionHelperOverlay.prototype.LatToY_ = function (lat) {
950  var sinofphi = Math.sin(lat * Math.PI / 180);
951  return (1 - 0.5 / Math.PI * Math.log((1 + sinofphi) / (1 - sinofphi)));
952};
953
954/**
955*   Old school LatLngToPixel
956*   @param {LatLng} latlng google.maps.LatLng object
957*   @param {Number} zoom Zoom level
958*   @return {position} {x: pixelPositionX, y: pixelPositionY}
959**/
960ProjectionHelperOverlay.prototype.LatLngToPixel = function (latlng, zoom) {
961  var map = this._map;
962  var div = this.getProjection().fromLatLngToDivPixel(latlng);
963  var abs = {x: ~~(0.5 + this.LngToX_(latlng.lng()) * (2 << (zoom + 6))), y: ~~(0.5 + this.LatToY_(latlng.lat()) * (2 << (zoom + 6)))};
964  return abs;
965};
966
967
968/**
969 * Draw function only triggers a ready event for
970 * MarkerManager to know projection can proceed to
971 * initialize.
972 */
973ProjectionHelperOverlay.prototype.draw = function () {
974  if (!this.ready) {
975    this.ready = true;
976    google.maps.event.trigger(this, 'ready');
977  }
978};

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.