Subversion Repositories eFlore/Applications.cel

Rev

Rev 466 | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 466 Rev 500
Line -... Line 1...
-
 
1
// ==ClosureCompiler==
-
 
2
// @compilation_level ADVANCED_OPTIMIZATIONS
-
 
3
// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3.js
-
 
4
// ==/ClosureCompiler==
-
 
5
 
1
/**
6
/**
2
 * @name MarkerClusterer
7
 * @name MarkerClusterer for Google Maps v3
3
 * @version 1.0
8
 * @version version 1.0
4
 * @author Xiaoxi Wu
9
 * @author Luke Mahe
5
 * @copyright (c) 2009 Xiaoxi Wu
-
 
6
 * @fileoverview
10
 * @fileoverview
7
 * This javascript library creates and manages per-zoom-level 
11
 * The library creates and manages per-zoom-level clusters for large amounts of
8
 * clusters for large amounts of markers (hundreds or thousands).
-
 
9
 * This library was inspired by the <a href="http://www.maptimize.com">
-
 
10
 * Maptimize</a> hosted clustering solution.
-
 
11
 * <br /><br/>
12
 * markers.
12
 * <b>How it works</b>:<br/>
13
 * <br/>
13
 * The <code>MarkerClusterer</code> will group markers into clusters according to
-
 
14
 * their distance from a cluster's center. When a marker is added,
-
 
15
 * the marker cluster will find a position in all the clusters, and 
-
 
16
 * if it fails to find one, it will create a new cluster with the marker.
-
 
17
 * The number of markers in a cluster will be displayed
14
 * This is a v3 implementation of the
18
 * on the cluster marker. When the map viewport changes,
-
 
19
 * <code>MarkerClusterer</code> will destroy the clusters in the viewport 
15
 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/"
20
 * and regroup them into new clusters.
16
 * >v2 MarkerClusterer</a>.
21
 *
-
 
22
 */
17
 */
Line 23... Line 18...
23
 
18
 
24
/*
19
/**
25
 * Licensed under the Apache License, Version 2.0 (the "License");
20
 * Licensed under the Apache License, Version 2.0 (the "License");
26
 * you may not use this file except in compliance with the License.
21
 * you may not use this file except in compliance with the License.
27
 * You may obtain a copy of the License at
22
 * You may obtain a copy of the License at
28
 *
23
 *
Line 35... Line 30...
35
 * limitations under the License.
30
 * limitations under the License.
36
 */
31
 */
Line 37... Line 32...
37
 
32
 
38
 
-
 
39
/**
-
 
40
 * @name MarkerClustererOptions
-
 
41
 * @class This class represents optional arguments to the {@link MarkerClusterer}
-
 
42
 * constructor.
-
 
43
 * @property {Number} [maxZoom] The max zoom level monitored by a
-
 
44
 * marker cluster. If not given, the marker cluster assumes the maximum map
-
 
45
 * zoom level. When maxZoom is reached or exceeded all markers will be shown
-
 
46
 * without cluster.
-
 
47
 * @property {Number} [gridSize=60] The grid size of a cluster in pixel. Each
-
 
48
 * cluster will be a square. If you want the algorithm to run faster, you can set
-
 
49
 * this value larger.
33
 
50
 * @property {Array of MarkerStyleOptions} [styles]
-
 
51
 * Custom styles for the cluster markers.
-
 
52
 * The array should be ordered according to increasing cluster size,
-
 
53
 * with the style for the smallest clusters first, and the style for the
-
 
54
 * largest clusters last.
-
 
55
 */
-
 
56
 
-
 
57
/**
-
 
58
 * @name MarkerStyleOptions
-
 
59
 * @class An array of these is passed into the {@link MarkerClustererOptions}
-
 
60
 * styles option.
-
 
61
 * @property {String} [url] Image url.
-
 
62
 * @property {Number} [height] Image height.
-
 
63
 * @property {Number} [height] Image width.
-
 
64
 * @property {Array of Number} [opt_anchor] Anchor for label text, like [24, 12]. 
-
 
65
 *    If not set, the text will align center and middle.
-
 
66
 * @property {String} [opt_textColor="black"] Text color.
-
 
67
 */
-
 
68
 
-
 
69
/**
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
 *     'styles': (object) An object that has style properties:
-
 
49
 *       'url': (string) The image url.
-
 
50
 *       'height': (number) The image height.
-
 
51
 *       'width': (number) The image width.
-
 
52
 *       'anchor': (Array) The anchor position of the label text.
-
 
53
 *       'textColor': (string) The text color.
70
 * Creates a new MarkerClusterer to cluster markers on the map.
54
 *       'textSize': (number) The text size.
71
 *
55
 *       'backgroundPosition': (string) The position of the backgound x, y.
72
 * @constructor
-
 
73
 * @param {GMap2} map The map that the markers should be added to.
-
 
74
 * @param {Array of GMarker} opt_markers Initial set of markers to be clustered.
56
 * @constructor
75
 * @param {MarkerClustererOptions} opt_opts A container for optional arguments.
57
 * @extends google.maps.OverlayView
76
 */
-
 
77
function MarkerClusterer(map, opt_markers, opt_opts) {
-
 
78
  // private members
-
 
79
  var clusters_ = [];
-
 
80
  var map_ = map;
-
 
81
  var maxZoom_ = null;
-
 
82
  var me_ = this;
-
 
83
  var gridSize_ = 60;
-
 
84
  var sizes = [53, 56, 66, 78, 90];
-
 
85
  var styles_ = [];
-
 
86
  var leftMarkers_ = [];
-
 
87
  var mcfn_ = null;
-
 
88
 
-
 
89
  var i = 0;
-
 
90
  for (i = 1; i <= 5; ++i) {
58
 */
91
    styles_.push({
-
 
92
      'url': "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m" + i + ".png",
-
 
93
      'height': sizes[i - 1],
-
 
94
      'width': sizes[i - 1]
-
 
95
    });
-
 
96
  }
-
 
97
 
59
function MarkerClusterer(map, opt_markers, opt_options) {
98
  if (typeof opt_opts === "object" && opt_opts !== null) {
-
 
99
    if (typeof opt_opts.gridSize === "number" && opt_opts.gridSize > 0) {
-
 
100
      gridSize_ = opt_opts.gridSize;
-
 
101
    }
-
 
102
    if (typeof opt_opts.maxZoom === "number") {
-
 
103
      maxZoom_ = opt_opts.maxZoom;
-
 
104
    }
-
 
105
    if (typeof opt_opts.styles === "object" && opt_opts.styles !== null && opt_opts.styles.length !== 0) {
-
 
106
      styles_ = opt_opts.styles;
-
 
107
    }
-
 
108
  }
-
 
109
 
60
  // MarkerClusterer implements google.maps.OverlayView interface. We use the
110
  /**
61
  // extend function to extend MarkerClusterer with google.maps.OverlayView
111
   * When we add a marker, the marker may not in the viewport of map, then we don't deal with it, instead
62
  // because it might not always be available when the code is defined so we
112
   * we add the marker into a array called leftMarkers_. When we reset MarkerClusterer we should add the
-
 
113
   * leftMarkers_ into MarkerClusterer.
-
 
114
   */
-
 
115
  function addLeftMarkers_() {
-
 
116
    if (leftMarkers_.length === 0) {
-
 
117
      return;
-
 
118
    }
-
 
119
    var leftMarkers = [];
63
  // look for it at the last possible moment. If it doesn't exist now then
120
    for (i = 0; i < leftMarkers_.length; ++i) {
-
 
121
      me_.addMarker(leftMarkers_[i], true, null, null, true);
64
  // there is no point going ahead :)
122
    }
-
 
Line 123... Line 65...
123
    leftMarkers_ = leftMarkers;
65
  this.extend(MarkerClusterer, google.maps.OverlayView);
124
  }
66
  this.map_ = map;
125
 
67
 
126
  /**
-
 
127
   * Get cluster marker images of this marker cluster. Mostly used by {@link Cluster}
68
  /**
128
   * @private
69
   * @type {Array.<google.maps.Marker>}
129
   * @return {Array of String}
-
 
130
   */
-
 
Line 131... Line 70...
131
  this.getStyles_ = function () {
70
   * @private
132
    return styles_;
71
   */
133
  };
72
  this.markers_ = [];
134
 
-
 
135
  /**
-
 
136
   * Remove all markers from MarkerClusterer.
-
 
137
   */
-
 
138
  this.clearMarkers = function () {
-
 
139
    for (var i = 0; i < clusters_.length; ++i) {
-
 
140
      if (typeof clusters_[i] !== "undefined" && clusters_[i] !== null) {
73
 
141
        clusters_[i].clearMarkers();
-
 
142
      }
-
 
143
    }
-
 
Line 144... Line -...
144
    clusters_ = [];
-
 
145
    leftMarkers_ = [];
-
 
146
    GEvent.removeListener(mcfn_);
-
 
147
  };
-
 
148
 
-
 
149
  /**
74
  /**
150
   * Check a marker, whether it is in current map viewport.
-
 
151
   * @private
-
 
Line 152... Line 75...
152
   * @return {Boolean} if it is in current map viewport
75
   *  @type {Array.<Cluster>}
153
   */
-
 
154
  function isMarkerInViewport_(marker) {
76
   */
155
    return map_.getBounds().containsLatLng(marker.getLatLng());
-
 
156
  }
77
  this.clusters_ = [];
157
 
-
 
158
  /**
-
 
159
   * When reset MarkerClusterer, there will be some markers get out of its cluster.
78
 
160
   * These markers should be add to new clusters.
-
 
161
   * @param {Array of GMarker} markers Markers to add.
-
 
162
   */
-
 
163
  function reAddMarkers_(markers) {
-
 
164
    var len = markers.length;
-
 
Line 165... Line 79...
165
    var clusters = [];
79
  this.sizes = [53, 56, 66, 78, 90];
166
    for (var i = len - 1; i >= 0; --i) {
80
 
167
      me_.addMarker(markers[i].marker, true, markers[i].isAdded, clusters, true);
81
  /**
168
    }
-
 
169
    addLeftMarkers_();
-
 
170
  }
-
 
171
 
-
 
172
  /**
-
 
173
   * Add a marker.
-
 
174
   * @private
-
 
175
   * @param {GMarker} marker Marker you want to add
-
 
176
   * @param {Boolean} opt_isNodraw Whether redraw the cluster contained the marker
-
 
177
   * @param {Boolean} opt_isAdded Whether the marker is added to map. Never use it.
-
 
178
   * @param {Array of Cluster} opt_clusters Provide a list of clusters, the marker
-
 
179
   *     cluster will only check these cluster where the marker should join.
-
 
180
   */
-
 
181
  this.addMarker = function (marker, opt_isNodraw, opt_isAdded, opt_clusters, opt_isNoCheck) {
-
 
182
    if (opt_isNoCheck !== true) {
-
 
183
      if (!isMarkerInViewport_(marker)) {
-
 
184
        leftMarkers_.push(marker);
-
 
185
        return;
-
 
186
      }
-
 
187
    }
-
 
188
 
-
 
189
    var isAdded = opt_isAdded;
-
 
190
    var clusters = opt_clusters;
-
 
191
    var pos = map_.fromLatLngToDivPixel(marker.getLatLng());
-
 
192
 
-
 
193
    if (typeof isAdded !== "boolean") {
-
 
194
      isAdded = false;
-
 
195
    }
-
 
196
    if (typeof clusters !== "object" || clusters === null) {
-
 
197
      clusters = clusters_;
-
 
198
    }
-
 
199
 
-
 
200
    var length = clusters.length;
-
 
201
    var cluster = null;
-
 
202
    for (var i = length - 1; i >= 0; i--) {
-
 
203
      cluster = clusters[i];
-
 
204
      var center = cluster.getCenter();
-
 
205
      if (center === null) {
-
 
206
        continue;
-
 
207
      }
-
 
208
      center = map_.fromLatLngToDivPixel(center);
-
 
209
 
-
 
210
      // Found a cluster which contains the marker.
-
 
211
      if (pos.x >= center.x - gridSize_ && pos.x <= center.x + gridSize_ &&
-
 
212
          pos.y >= center.y - gridSize_ && pos.y <= center.y + gridSize_) {
-
 
213
        cluster.addMarker({
-
 
214
          'isAdded': isAdded,
-
 
215
          'marker': marker
-
 
216
        });
-
 
217
        if (!opt_isNodraw) {
-
 
218
          cluster.redraw_();
-
 
219
        }
-
 
220
        return;
-
 
221
      }
-
 
222
    }
-
 
223
 
-
 
224
    // No cluster contain the marker, create a new cluster.
-
 
225
    cluster = new Cluster(this, map);
-
 
226
    cluster.addMarker({
-
 
227
      'isAdded': isAdded,
-
 
228
      'marker': marker
-
 
229
    });
-
 
230
    if (!opt_isNodraw) {
-
 
231
      cluster.redraw_();
-
 
232
    }
-
 
233
 
-
 
234
    // Add this cluster both in clusters provided and clusters_
-
 
235
    clusters.push(cluster);
-
 
236
    if (clusters !== clusters_) {
-
 
237
      clusters_.push(cluster);
-
 
238
    }
82
   * @private
-
 
83
   */
Line 239... Line -...
239
  };
-
 
240
 
-
 
241
  /**
-
 
242
   * Remove a marker.
-
 
243
   *
-
 
244
   * @param {GMarker} marker The marker you want to remove.
-
 
245
   */
-
 
246
 
-
 
247
  this.removeMarker = function (marker) {
-
 
248
    for (var i = 0; i < clusters_.length; ++i) {
-
 
249
      if (clusters_[i].remove(marker)) {
-
 
250
        clusters_[i].redraw_();
-
 
251
        return;
84
  this.styles_ = [];
252
      }
-
 
253
    }
-
 
254
  };
-
 
255
 
-
 
256
  /**
-
 
Line 257... Line 85...
257
   * Redraw all clusters in viewport.
85
 
258
   */
86
  /**
259
  this.redraw_ = function () {
87
   * @type {boolean}
260
    var clusters = this.getClustersInViewport_();
88
   * @private
261
    for (var i = 0; i < clusters.length; ++i) {
89
   */
262
      clusters[i].redraw_(true);
-
 
263
    }
-
 
264
  };
-
 
265
 
-
 
266
  /**
-
 
267
   * Get all clusters in viewport.
-
 
268
   * @return {Array of Cluster}
-
 
269
   */
-
 
270
  this.getClustersInViewport_ = function () {
-
 
Line 271... Line 90...
271
    var clusters = [];
90
  this.ready_ = false;
272
    var curBounds = map_.getBounds();
91
 
273
    for (var i = 0; i < clusters_.length; i ++) {
92
  var options = opt_options || {};
274
      if (clusters_[i].isInBounds(curBounds)) {
-
 
275
        clusters.push(clusters_[i]);
93
 
276
      }
94
  /**
277
    }
-
 
278
    return clusters;
95
   * @type {number}
-
 
96
   * @private
Line 279... Line 97...
279
  };
97
   */
280
 
98
  this.gridSize_ = options['gridSize'] || 60;
281
  /**
99
 
282
   * Get max zoom level.
-
 
283
   * @private
100
  /**
284
   * @return {Number}
101
   * @type {?number}
285
   */
102
   * @private
286
  this.getMaxZoom_ = function () {
-
 
Line 287... Line 103...
287
    return maxZoom_;
103
   */
288
  };
104
  this.maxZoom_ = options['maxZoom'] || null;
289
 
105
 
290
  /**
-
 
291
   * Get map object.
106
  this.styles_ = options['styles'] || [];
292
   * @private
107
 
293
   * @return {GMap2}
108
  /**
294
   */
-
 
Line 295... Line 109...
295
  this.getMap_ = function () {
109
   * @type {string}
296
    return map_;
110
   * @private
297
  };
111
   */
298
 
112
  this.imagePath_ = options['imagePath'] ||
299
  /**
113
      this.MARKER_CLUSTER_IMAGE_PATH_;
300
   * Get grid size
-
 
301
   * @private
-
 
302
   * @return {Number}
-
 
303
   */
-
 
304
  this.getGridSize_ = function () {
-
 
305
    return gridSize_;
-
 
Line 306... Line -...
306
  };
-
 
307
 
-
 
308
  /**
-
 
309
   * Get total number of markers.
-
 
310
   * @return {Number}
114
 
311
   */
115
  /**
312
  this.getTotalMarkers = function () {
116
   * @type {string}
Line 313... Line 117...
313
    var result = 0;
117
   * @private
314
    for (var i = 0; i < clusters_.length; ++i) {
118
   */
-
 
119
  this.imageExtension_ = options['imageExtension'] ||
315
      result += clusters_[i].getTotalMarkers();
120
      this.MARKER_CLUSTER_IMAGE_EXTENSION_;
316
    }
121
 
317
    return result;
-
 
318
  };
-
 
319
 
-
 
320
  /**
-
 
321
   * Get total number of clusters.
-
 
322
   * @return {int}
-
 
323
   */
-
 
324
  this.getTotalClusters = function () {
-
 
325
    return clusters_.length;
-
 
326
  };
-
 
327
 
-
 
328
  /**
-
 
Line 329... Line -...
329
   * Collect all markers of clusters in viewport and regroup them.
-
 
330
   */
-
 
331
  this.resetViewport = function () {
-
 
332
    var clusters = this.getClustersInViewport_();
122
  /**
333
    var tmpMarkers = [];
-
 
334
    var removed = 0;
-
 
335
 
-
 
336
    for (var i = 0; i < clusters.length; ++i) {
-
 
337
      var cluster = clusters[i];
-
 
338
      var oldZoom = cluster.getCurrentZoom();
-
 
339
      if (oldZoom === null) {
-
 
340
        continue;
-
 
341
      }
-
 
342
      var curZoom = map_.getZoom();
123
   * @type {boolean}
343
      if (curZoom !== oldZoom) {
-
 
344
 
-
 
345
        // If the cluster zoom level changed then destroy the cluster
-
 
346
        // and collect its markers.
-
 
347
        var mks = cluster.getMarkers();
124
   * @private
Line 348... Line -...
348
        for (var j = 0; j < mks.length; ++j) {
-
 
349
          var newMarker = {
-
 
350
            'isAdded': false,
125
   */
351
            'marker': mks[j].marker
-
 
Line -... Line 126...
-
 
126
  this.zoomOnClick_ = true;
Line 352... Line 127...
352
          };
127
 
353
          tmpMarkers.push(newMarker);
128
  if (options['zoomOnClick'] != undefined) {
-
 
129
    this.zoomOnClick_ = options['zoomOnClick'];
354
        }
130
  }
355
        cluster.clearMarkers();
131
 
356
        removed++;
132
  /**
357
        for (j = 0; j < clusters_.length; ++j) {
133
   * @type {boolean}
-
 
134
   * @private
-
 
135
   */
-
 
136
  this.averageCenter_ = false;
358
          if (cluster === clusters_[j]) {
137
 
359
            clusters_.splice(j, 1);
138
  if (options['averageCenter'] != undefined) {
-
 
139
    this.averageCenter_ = options['averageCenter'];
360
          }
140
  }
361
        }
-
 
362
      }
-
 
Line 363... Line 141...
363
    }
141
 
364
 
142
  this.setupStyles_();
365
    // Add the markers collected into marker cluster to reset
143
 
-
 
144
  this.setMap(map);
366
    reAddMarkers_(tmpMarkers);
145
 
Line 367... Line -...
367
    this.redraw_();
-
 
368
  };
146
  /**
369
 
147
   * @type {number}
370
 
148
   * @private
-
 
149
   */
-
 
150
  this.prevZoom_ = this.map_.getZoom();
-
 
151
 
-
 
152
  // Add the map event listeners
-
 
153
  var that = this;
371
  /**
154
  google.maps.event.addListener(this.map_, 'zoom_changed', function() {
Line -... Line 155...
-
 
155
    var maxZoom = that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom;
372
   * Add a set of markers.
156
    var zoom = that.map_.getZoom();
373
   *
-
 
374
   * @param {Array of GMarker} markers The markers you want to add.
-
 
375
   */
-
 
376
  this.addMarkers = function (markers) {
-
 
377
    for (var i = 0; i < markers.length; ++i) {
157
    if (zoom < 0 || zoom > maxZoom) {
378
      this.addMarker(markers[i], true);
158
      return;
379
    }
159
    }
380
    this.redraw_();
160
 
381
  };
-
 
382
 
161
    if (that.prevZoom_ != zoom) {
383
  // initialize
162
      that.prevZoom_ = that.map_.getZoom();
384
  if (typeof opt_markers === "object" && opt_markers !== null) {
-
 
385
    this.addMarkers(opt_markers);
-
 
386
  }
163
      that.resetViewport();
387
 
-
 
388
  // when map move end, regroup.
-
 
389
  mcfn_ = GEvent.addListener(map_, "moveend", function () {
164
    }
Line 390... Line -...
390
    me_.resetViewport();
-
 
391
  });
-
 
392
}
-
 
393
 
-
 
394
/**
-
 
395
 * Create a cluster to collect markers.
-
 
396
 * A cluster includes some markers which are in a block of area.
-
 
397
 * If there are more than one markers in cluster, the cluster
-
 
Line 398... Line 165...
398
 * will create a {@link ClusterMarker_} and show the total number
165
  });
399
 * of markers in cluster.
166
 
400
 *
167
  google.maps.event.addListener(this.map_, 'idle', function() {
401
 * @constructor
-
 
402
 * @private
-
 
403
 * @param {MarkerClusterer} markerClusterer The marker cluster object
-
 
404
 */
-
 
405
function Cluster(markerClusterer) {
168
    that.redraw();
406
  var center_ = null;
169
  });
407
  var markers_ = [];
170
 
-
 
171
  // Finally, add the markers
Line 408... Line -...
408
  var markerClusterer_ = markerClusterer;
-
 
409
  var map_ = markerClusterer.getMap_();
-
 
410
  var clusterMarker_ = null;
-
 
411
  var zoom_ = map_.getZoom();
-
 
412
 
-
 
Line -... Line 172...
-
 
172
  if (opt_markers && opt_markers.length) {
413
  /**
173
    this.addMarkers(opt_markers, false);
414
   * Get markers of this cluster.
174
  }
415
   *
175
}
416
   * @return {Array of GMarker}
176
 
417
   */
177
 
418
  this.getMarkers = function () {
178
/**
419
    return markers_;
179
 * The marker cluster image path.
420
  };
180
 *
421
 
181
 * @type {string}
422
  /**
-
 
423
   * If this cluster intersects certain bounds.
182
 * @private
424
   *
183
 */
425
   * @param {GLatLngBounds} bounds A bounds to test
184
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ =
426
   * @return {Boolean} Is this cluster intersects the bounds
185
    'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' +
-
 
186
    'images/m';
427
   */
187
 
Line 428... Line -...
428
  this.isInBounds = function (bounds) {
-
 
429
    if (center_ === null) {
-
 
430
      return false;
-
 
431
    }
-
 
432
 
-
 
433
    if (!bounds) {
-
 
434
      bounds = map_.getBounds();
-
 
435
    }
-
 
Line 436... Line 188...
436
    var sw = map_.fromLatLngToDivPixel(bounds.getSouthWest());
188
 
437
    var ne = map_.fromLatLngToDivPixel(bounds.getNorthEast());
189
/**
438
 
190
 * The marker cluster image path.
439
    var centerxy = map_.fromLatLngToDivPixel(center_);
-
 
440
    var inViewport = true;
-
 
441
    var gridSize = markerClusterer.getGridSize_();
-
 
442
    if (zoom_ !== map_.getZoom()) {
191
 *
443
      var dl = map_.getZoom() - zoom_;
192
 * @type {string}
444
      gridSize = Math.pow(2, dl) * gridSize;
-
 
445
    }
-
 
446
    if (ne.x !== sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x)) {
-
 
447
      inViewport = false;
-
 
448
    }
-
 
449
    if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y)) {
-
 
450
      inViewport = false;
-
 
451
    }
-
 
452
    return inViewport;
193
 * @private
453
  };
194
 */
Line 454... Line 195...
454
 
195
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
455
  /**
196
 
456
   * Get cluster center.
197
 
457
   *
-
 
458
   * @return {GLatLng}
-
 
459
   */
198
/**
460
  this.getCenter = function () {
199
 * Extends a objects prototype by anothers.
461
    return center_;
-
 
462
  };
-
 
463
 
-
 
464
  /**
-
 
465
   * Add a marker.
-
 
466
   *
-
 
467
   * @param {Object} marker An object of marker you want to add:
-
 
468
   *   {Boolean} isAdded If the marker is added on map.
-
 
469
   *   {GMarker} marker The marker you want to add.
-
 
470
   */
-
 
471
  this.addMarker = function (marker) {
-
 
Line 472... Line 200...
472
    if (center_ === null) {
200
 *
473
      /*var pos = marker['marker'].getLatLng();
201
 * @param {Object} obj1 The object to be extended.
474
       pos = map.fromLatLngToContainerPixel(pos);
-
 
475
       pos.x = parseInt(pos.x - pos.x % (GRIDWIDTH * 2) + GRIDWIDTH);
202
 * @param {Object} obj2 The object to extend with.
476
       pos.y = parseInt(pos.y - pos.y % (GRIDWIDTH * 2) + GRIDWIDTH);
203
 * @return {Object} The new extended object.
477
       center = map.fromContainerPixelToLatLng(pos);*/
204
 * @ignore
478
      center_ = marker.marker.getLatLng();
205
 */
-
 
206
MarkerClusterer.prototype.extend = function(obj1, obj2) {
479
    }
207
  return (function(object) {
480
    markers_.push(marker);
208
    for (var property in object.prototype) {
Line 481... Line -...
481
  };
-
 
482
 
209
      this.prototype[property] = object.prototype[property];
483
  /**
210
    }
484
   * Remove a marker from cluster.
211
    return this;
485
   *
212
  }).apply(obj1, [obj2]);
486
   * @param {GMarker} marker The marker you want to remove.
-
 
487
   * @return {Boolean} Whether find the marker to be removed.
213
};
488
   */
-
 
489
  this.removeMarker = function (marker) {
214
 
490
    for (var i = 0; i < markers_.length; ++i) {
215
 
-
 
216
/**
Line 491... Line -...
491
      if (marker === markers_[i].marker) {
-
 
492
        if (markers_[i].isAdded) {
-
 
493
          map_.removeOverlay(markers_[i].marker);
-
 
494
        }
-
 
495
        markers_.splice(i, 1);
-
 
496
        return true;
-
 
497
      }
-
 
498
    }
-
 
Line 499... Line -...
499
    return false;
-
 
500
  };
-
 
501
 
-
 
502
  /**
-
 
503
   * Get current zoom level of this cluster.
-
 
504
   * Note: the cluster zoom level and map zoom level not always the same.
-
 
505
   *
217
 * Implementaion of the interface method.
506
   * @return {Number}
218
 * @ignore
507
   */
-
 
508
  this.getCurrentZoom = function () {
-
 
509
    return zoom_;
-
 
510
  };
219
 */
511
 
220
MarkerClusterer.prototype.onAdd = function() {
512
  /**
-
 
513
   * Redraw a cluster.
221
  this.setReady_(true);
514
   * @private
-
 
515
   * @param {Boolean} isForce If redraw by force, no matter if the cluster is
222
};
516
   *     in viewport.
223
 
517
   */
-
 
518
  this.redraw_ = function (isForce) {
-
 
519
    if (!isForce && !this.isInBounds()) {
-
 
520
      return;
-
 
521
    }
-
 
522
 
-
 
523
    // Set cluster zoom level.
-
 
524
    zoom_ = map_.getZoom();
-
 
525
    var i = 0;
-
 
526
    var mz = markerClusterer.getMaxZoom_();
-
 
527
    if (mz === null) {
-
 
528
      mz = map_.getCurrentMapType().getMaximumResolution();
-
 
529
    }
-
 
530
    if (zoom_ >= mz || this.getTotalMarkers() === 1) {
-
 
531
 
-
 
532
      // If current zoom level is beyond the max zoom level or the cluster
224
/**
Line 533... Line -...
533
      // have only one marker, the marker(s) in cluster will be showed on map.
-
 
534
      for (i = 0; i < markers_.length; ++i) {
-
 
535
        if (markers_[i].isAdded) {
-
 
536
          if (markers_[i].marker.isHidden()) {
-
 
537
            markers_[i].marker.show();
-
 
538
          }
-
 
539
        } else {
-
 
540
          map_.addOverlay(markers_[i].marker);
-
 
541
          markers_[i].isAdded = true;
-
 
542
        }
-
 
543
      }
-
 
544
      if (clusterMarker_ !== null) {
-
 
545
        clusterMarker_.hide();
-
 
546
      }
-
 
Line 547... Line 225...
547
    } else {
225
 * Implementaion of the interface method.
548
      // Else add a cluster marker on map to show the number of markers in
226
 * @ignore
-
 
227
 */
549
      // this cluster.
228
MarkerClusterer.prototype.draw = function() {};
550
      for (i = 0; i < markers_.length; ++i) {
229
 
551
        if (markers_[i].isAdded && (!markers_[i].marker.isHidden())) {
230
/**
552
          markers_[i].marker.hide();
231
 * Sets up the styles object.
553
        }
232
 *
554
      }
233
 * @private
Line 555... Line 234...
555
      if (clusterMarker_ === null) {
234
 */
556
        clusterMarker_ = new ClusterMarker_(center_, this.getTotalMarkers(), markerClusterer_.getStyles_(), markerClusterer_.getGridSize_());
-
 
557
        map_.addOverlay(clusterMarker_);
235
MarkerClusterer.prototype.setupStyles_ = function() {
558
      } else {
236
  if (this.styles_.length) {
559
        if (clusterMarker_.isHidden()) {
-
 
560
          clusterMarker_.show();
-
 
561
        }
-
 
562
        clusterMarker_.redraw(true);
237
    return;
563
      }
-
 
564
    }
-
 
565
  };
-
 
566
 
-
 
567
  /**
-
 
568
   * Remove all the markers from this cluster.
-
 
569
   */
-
 
570
  this.clearMarkers = function () {
238
  }
571
    if (clusterMarker_ !== null) {
239
 
572
      map_.removeOverlay(clusterMarker_);
-
 
573
    }
240
  for (var i = 0, size; size = this.sizes[i]; i++) {
574
    for (var i = 0; i < markers_.length; ++i) {
-
 
575
      if (markers_[i].isAdded) {
-
 
576
        map_.removeOverlay(markers_[i].marker);
-
 
577
      }
241
    this.styles_.push({
Line -... Line 242...
-
 
242
      url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,
578
    }
243
      height: size,
579
    markers_ = [];
-
 
580
  };
244
      width: size
581
 
-
 
582
  /**
-
 
583
   * Get number of markers.
-
 
584
   * @return {Number}
245
    });
-
 
246
  }
585
   */
247
};
586
  this.getTotalMarkers = function () {
-
 
587
    return markers_.length;
248
 
588
  };
-
 
589
}
-
 
590
 
-
 
591
/**
249
 
Line 592... Line -...
592
 * ClusterMarker_ creates a marker that shows the number of markers that
-
 
Line 593... Line 250...
593
 * a cluster contains.
250
/**
594
 *
251
 *  Sets the styles.
595
 * @constructor
252
 *
-
 
253
 *  @param {Object} styles The style to set.
596
 * @private
254
 */
597
 * @param {GLatLng} latlng Marker's lat and lng.
255
MarkerClusterer.prototype.setStyles = function(styles) {
598
 * @param {Number} count Number to show.
-
 
599
 * @param {Array of Object} styles The image list to be showed:
-
 
600
 *   {String} url Image url.
256
  this.styles_ = styles;
601
 *   {Number} height Image height.
-
 
602
 *   {Number} width Image width.
-
 
603
 *   {Array of Number} anchor Text anchor of image left and top.
-
 
604
 *   {String} textColor text color.
-
 
605
 * @param {Number} padding Padding of marker center.
-
 
606
 */
-
 
607
function ClusterMarker_(latlng, count, styles, padding) {
-
 
608
  var index = 0;
-
 
609
  var dv = count;
-
 
610
  while (dv !== 0) {
-
 
611
    dv = parseInt(dv / 10, 10);
-
 
612
    index ++;
-
 
613
  }
-
 
614
 
-
 
615
  if (styles.length < index) {
-
 
616
    index = styles.length;
-
 
617
  }
-
 
618
  this.url_ = styles[index - 1].url;
-
 
619
  this.height_ = styles[index - 1].height;
-
 
620
  this.width_ = styles[index - 1].width;
-
 
621
  this.textColor_ = styles[index - 1].opt_textColor;
-
 
622
  this.anchor_ = styles[index - 1].opt_anchor;
-
 
623
  this.latlng_ = latlng;
-
 
624
  this.index_ = index;
257
};
625
  this.styles_ = styles;
-
 
Line 626... Line -...
626
  this.text_ = count;
-
 
627
  this.padding_ = padding;
-
 
628
}
-
 
629
 
-
 
630
ClusterMarker_.prototype = new GOverlay();
-
 
631
 
-
 
632
/**
-
 
-
 
258
 
-
 
259
 
633
 * Initialize cluster marker.
260
/**
634
 * @private
-
 
635
 */
-
 
636
ClusterMarker_.prototype.initialize = function (map) {
-
 
-
 
261
 *  Gets the styles.
637
  this.map_ = map;
262
 *
638
  var div = document.createElement("div");
-
 
639
  var latlng = this.latlng_;
-
 
640
  var pos = map.fromLatLngToDivPixel(latlng);
263
 *  @return {Object} The styles object.
-
 
264
 */
641
  pos.x -= parseInt(this.width_ / 2, 10);
265
MarkerClusterer.prototype.getStyles = function() {
642
  pos.y -= parseInt(this.height_ / 2, 10);
266
  return this.styles_;
Line -... Line 267...
-
 
267
};
643
  var mstyle = "";
268
 
644
  if (document.all) {
269
 
645
    mstyle = 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="' + this.url_ + '");';
270
/**
-
 
271
 * Whether zoom on click is set.
646
  } else {
272
 *
647
    mstyle = "background:url(" + this.url_ + ");";
273
 * @return {boolean} True if zoomOnClick_ is set.
648
  }
274
 */
649
  if (typeof this.anchor_ === "object") {
275
MarkerClusterer.prototype.isZoomOnClick = function() {
Line -... Line 276...
-
 
276
  return this.zoomOnClick_;
650
    if (typeof this.anchor_[0] === "number" && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) {
277
};
651
      mstyle += 'height:' + (this.height_ - this.anchor_[0]) + 'px;padding-top:' + this.anchor_[0] + 'px;';
278
 
652
    } else {
279
/**
-
 
280
 * Whether average center is set.
653
      mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
281
 *
654
    }
282
 * @return {boolean} True if averageCenter_ is set.
655
    if (typeof this.anchor_[1] === "number" && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) {
283
 */
656
      mstyle += 'width:' + (this.width_ - this.anchor_[1]) + 'px;padding-left:' + this.anchor_[1] + 'px;';
284
MarkerClusterer.prototype.isAverageCenter = function() {
Line -... Line 285...
-
 
285
  return this.averageCenter_;
657
    } else {
286
};
658
      mstyle += 'width:' + this.width_ + 'px;text-align:center;';
287
 
-
 
288
 
-
 
289
/**
-
 
290
 *  Returns the array of markers in the clusterer.
-
 
291
 *
659
    }
292
 *  @return {Array.<google.maps.Marker>} The markers.
660
  } else {
293
 */
661
    mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
294
MarkerClusterer.prototype.getMarkers = function() {
-
 
295
  return this.markers_;
-
 
296
};
-
 
297
 
662
    mstyle += 'width:' + this.width_ + 'px;text-align:center;';
298
 
-
 
299
/**
663
  }
300
 *  Returns the number of markers in the clusterer
664
  var txtColor = this.textColor_ ? this.textColor_ : 'black';
301
 *
-
 
302
 *  @return {Number} The number of markers.
665
 
303
 */
-
 
304
MarkerClusterer.prototype.getTotalMarkers = function() {
-
 
305
  return this.markers_.length;
-
 
306
};
-
 
307
 
-
 
308
 
-
 
309
/**
-
 
310
 *  Sets the max zoom for the clusterer.
-
 
311
 *
666
  div.style.cssText = mstyle + 'cursor:pointer;top:' + pos.y + "px;left:" +
312
 *  @param {number} maxZoom The max zoom level.
-
 
313
 */
-
 
314
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
-
 
315
  this.maxZoom_ = maxZoom;
667
      pos.x + "px;color:" + txtColor +  ";position:absolute;font-size:11px;" +
316
};
-
 
317
 
-
 
318
 
668
      'font-family:Arial,sans-serif;font-weight:bold';
319
/**
669
  div.innerHTML = this.text_;
320
 *  Gets the max zoom for the clusterer.
670
  map.getPane(G_MAP_MAP_PANE).appendChild(div);
321
 *
Line -... Line 322...
-
 
322
 *  @return {number} The max zoom level.
671
  var padding = this.padding_;
323
 */
672
  GEvent.addDomListener(div, "click", function () {
324
MarkerClusterer.prototype.getMaxZoom = function() {
-
 
325
  return this.maxZoom_ || this.map_.mapTypes[this.map_.getMapTypeId()].maxZoom;
-
 
326
};
673
    var pos = map.fromLatLngToDivPixel(latlng);
327
 
674
    var sw = new GPoint(pos.x - padding, pos.y + padding);
328
 
675
    sw = map.fromDivPixelToLatLng(sw);
329
/**
676
    var ne = new GPoint(pos.x + padding, pos.y - padding);
330
 *  The function for calculating the cluster icon image.
Line -... Line 331...
-
 
331
 *
677
    ne = map.fromDivPixelToLatLng(ne);
332
 *  @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
678
    var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize());
333
 *  @param {number} numStyles The number of styles available.
-
 
334
 *  @return {Object} A object properties: 'text' (string) and 'index' (number).
-
 
335
 *  @private
-
 
336
 */
679
    map.setCenter(latlng, zoom);
337
MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {
680
  });
338
  var index = 0;
-
 
339
  var count = markers.length;
681
  this.div_ = div;
340
  var dv = count;
-
 
341
  while (dv !== 0) {
-
 
342
    dv = parseInt(dv / 10, 10);
-
 
343
    index++;
-
 
344
  }
682
};
345
 
Line -... Line 346...
-
 
346
  index = Math.min(index, numStyles);
683
 
347
  return {
684
/**
348
    text: count,
-
 
349
    index: index
-
 
350
  };
685
 * Remove this overlay.
351
};
686
 * @private
352
 
687
 */
353
 
-
 
354
/**
-
 
355
 * Set the calculator function.
-
 
356
 *
-
 
357
 * @param {function(Array, number)} calculator The function to set as the
-
 
358
 *     calculator. The function should return a object properties:
-
 
359
 *     'text' (string) and 'index' (number).
-
 
360
 *
-
 
361
 */
688
ClusterMarker_.prototype.remove = function () {
362
MarkerClusterer.prototype.setCalculator = function(calculator) {
-
 
363
  this.calculator_ = calculator;
-
 
364
};
-
 
365
 
-
 
366
 
-
 
367
/**
689
  this.div_.parentNode.removeChild(this.div_);
368
 * Get the calculator function.
-
 
369
 *
-
 
370
 * @return {function(Array, number)} the calculator function.
-
 
371
 */
-
 
372
MarkerClusterer.prototype.getCalculator = function() {
-
 
373
  return this.calculator_;
-
 
374
};
-
 
375
 
-
 
376
 
-
 
377
/**
-
 
378
 * Add an array of markers to the clusterer.
-
 
379
 *
-
 
380
 * @param {Array.<google.maps.Marker>} markers The markers to add.
-
 
381
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
-
 
382
 */
-
 
383
MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
-
 
384
  for (var i = 0, marker; marker = markers[i]; i++) {
-
 
385
    this.pushMarkerTo_(marker);
-
 
386
  }
-
 
387
  if (!opt_nodraw) {
-
 
388
    this.redraw();
-
 
389
  }
-
 
390
};
-
 
391
 
-
 
392
 
-
 
393
/**
-
 
394
 * Pushes a marker to the clusterer.
-
 
395
 *
-
 
396
 * @param {google.maps.Marker} marker The marker to add.
-
 
397
 * @private
-
 
398
 */
-
 
399
MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
-
 
400
  marker.setVisible(false);
-
 
401
  marker.setMap(null);
-
 
402
  marker.isAdded = false;
-
 
403
  if (marker['draggable']) {
-
 
404
    // If the marker is draggable add a listener so we update the clusters on
-
 
405
    // the drag end.
-
 
406
    var that = this;
-
 
407
    google.maps.event.addListener(marker, 'dragend', function() {
-
 
408
      marker.isAdded = false;
-
 
409
      that.resetViewport();
-
 
410
      that.redraw();
-
 
411
    });
-
 
412
  }
-
 
413
  this.markers_.push(marker);
-
 
414
};
-
 
415
 
-
 
416
 
-
 
417
/**
-
 
418
 * Adds a marker to the clusterer and redraws if needed.
-
 
419
 *
-
 
420
 * @param {google.maps.Marker} marker The marker to add.
-
 
421
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
-
 
422
 */
-
 
423
MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
-
 
424
  this.pushMarkerTo_(marker);
-
 
425
  if (!opt_nodraw) {
-
 
426
    this.redraw();
-
 
427
  }
-
 
428
};
-
 
429
 
-
 
430
 
-
 
431
/**
-
 
432
 * Removes a marker and returns true if removed, false if not
-
 
433
 *
-
 
434
 * @param {google.maps.Marker} marker The marker to remove
-
 
435
 * @return {boolean} Whether the marker was removed or not
-
 
436
 * @private
-
 
437
 */
-
 
438
MarkerClusterer.prototype.removeMarker_ = function(marker) {
-
 
439
  var index = -1;
-
 
440
  if (this.markers_.indexOf) {
-
 
441
    index = this.markers_.indexOf(marker);
-
 
442
  } else {
-
 
443
    for (var i = 0, m; m = this.markers_[i]; i++) {
-
 
444
      if (m == marker) {
-
 
445
        index = i;
-
 
446
        break;
-
 
447
      }
-
 
448
    }
-
 
449
  }
-
 
450
 
-
 
451
  if (index == -1) {
-
 
452
    // Marker is not in our list of markers.
-
 
453
    return false;
-
 
454
  }
-
 
455
 
-
 
456
  this.markers_.splice(index, 1);
-
 
457
  marker.setVisible(false);
-
 
458
  marker.setMap(null);
-
 
459
 
-
 
460
  return true;
-
 
461
};
-
 
462
 
-
 
463
 
-
 
464
/**
-
 
465
 * Remove a marker from the cluster.
-
 
466
 *
-
 
467
 * @param {google.maps.Marker} marker The marker to remove.
-
 
468
 * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
-
 
469
 * @return {boolean} True if the marker was removed.
-
 
470
 */
-
 
471
MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {
-
 
472
  var removed = this.removeMarker_(marker);
-
 
473
 
-
 
474
  if (!opt_nodraw && removed) {
-
 
475
    this.resetViewport();
-
 
476
    this.redraw();
-
 
477
    return true;
-
 
478
  } else {
-
 
479
   return false;
-
 
480
  }
-
 
481
};
-
 
482
 
-
 
483
 
-
 
484
/**
-
 
485
 * Removes an array of markers from the cluster.
-
 
486
 *
-
 
487
 * @param {Array.<google.maps.Marker>} markers The markers to remove.
-
 
488
 * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
-
 
489
 */
-
 
490
MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {
-
 
491
  var removed = false;
-
 
492
 
-
 
493
  for (var i = 0, marker; marker = markers[i]; i++) {
-
 
494
    var r = this.removeMarker_(marker);
-
 
495
    removed = removed || r;
-
 
496
  }
-
 
497
 
-
 
498
  if (!opt_nodraw && removed) {
-
 
499
    this.resetViewport();
-
 
500
    this.redraw();
-
 
501
    return true;
-
 
502
  }
-
 
503
};
-
 
504
 
-
 
505
 
-
 
506
/**
-
 
507
 * Sets the clusterer's ready state.
-
 
508
 *
-
 
509
 * @param {boolean} ready The state.
-
 
510
 * @private
-
 
511
 */
-
 
512
MarkerClusterer.prototype.setReady_ = function(ready) {
-
 
513
  if (!this.ready_) {
-
 
514
    this.ready_ = ready;
-
 
515
    this.createClusters_();
-
 
516
  }
-
 
517
};
-
 
518
 
-
 
519
 
-
 
520
/**
-
 
521
 * Returns the number of clusters in the clusterer.
-
 
522
 *
-
 
523
 * @return {number} The number of clusters.
-
 
524
 */
-
 
525
MarkerClusterer.prototype.getTotalClusters = function() {
-
 
526
  return this.clusters_.length;
-
 
527
};
-
 
528
 
-
 
529
 
-
 
530
/**
-
 
531
 * Returns the google map that the clusterer is associated with.
-
 
532
 *
-
 
533
 * @return {google.maps.Map} The map.
-
 
534
 */
-
 
535
MarkerClusterer.prototype.getMap = function() {
-
 
536
  return this.map_;
-
 
537
};
-
 
538
 
-
 
539
 
-
 
540
/**
-
 
541
 * Sets the google map that the clusterer is associated with.
-
 
542
 *
-
 
543
 * @param {google.maps.Map} map The map.
-
 
544
 */
-
 
545
MarkerClusterer.prototype.setMap = function(map) {
-
 
546
  this.map_ = map;
-
 
547
};
-
 
548
 
-
 
549
 
-
 
550
/**
-
 
551
 * Returns the size of the grid.
-
 
552
 *
-
 
553
 * @return {number} The grid size.
-
 
554
 */
-
 
555
MarkerClusterer.prototype.getGridSize = function() {
-
 
556
  return this.gridSize_;
-
 
557
};
-
 
558
 
-
 
559
 
-
 
560
/**
-
 
561
 * Returns the size of the grid.
-
 
562
 *
-
 
563
 * @param {number} size The grid size.
-
 
564
 */
-
 
565
MarkerClusterer.prototype.setGridSize = function(size) {
-
 
566
  this.gridSize_ = size;
-
 
567
};
-
 
568
 
-
 
569
 
-
 
570
/**
-
 
571
 * Extends a bounds object by the grid size.
-
 
572
 *
-
 
573
 * @param {google.maps.LatLngBounds} bounds The bounds to extend.
-
 
574
 * @return {google.maps.LatLngBounds} The extended bounds.
-
 
575
 */
-
 
576
MarkerClusterer.prototype.getExtendedBounds = function(bounds) {
-
 
577
  var projection = this.getProjection();
-
 
578
 
-
 
579
  // Turn the bounds into latlng.
-
 
580
  var tr = new google.maps.LatLng(bounds.getNorthEast().lat(),
-
 
581
      bounds.getNorthEast().lng());
-
 
582
  var bl = new google.maps.LatLng(bounds.getSouthWest().lat(),
-
 
583
      bounds.getSouthWest().lng());
-
 
584
 
-
 
585
  // Convert the points to pixels and the extend out by the grid size.
-
 
586
  var trPix = projection.fromLatLngToDivPixel(tr);
-
 
587
  trPix.x += this.gridSize_;
-
 
588
  trPix.y -= this.gridSize_;
-
 
589
 
-
 
590
  var blPix = projection.fromLatLngToDivPixel(bl);
-
 
591
  blPix.x -= this.gridSize_;
-
 
592
  blPix.y += this.gridSize_;
-
 
593
 
-
 
594
  // Convert the pixel points back to LatLng
-
 
595
  var ne = projection.fromDivPixelToLatLng(trPix);
-
 
596
  var sw = projection.fromDivPixelToLatLng(blPix);
-
 
597
 
-
 
598
  // Extend the bounds to contain the new bounds.
-
 
599
  bounds.extend(ne);
-
 
600
  bounds.extend(sw);
-
 
601
 
-
 
602
  return bounds;
-
 
603
};
-
 
604
 
-
 
605
 
-
 
606
/**
-
 
607
 * Determins if a marker is contained in a bounds.
-
 
608
 *
-
 
609
 * @param {google.maps.Marker} marker The marker to check.
-
 
610
 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
-
 
611
 * @return {boolean} True if the marker is in the bounds.
-
 
612
 * @private
-
 
613
 */
-
 
614
MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
-
 
615
  return bounds.contains(marker.getPosition());
-
 
616
};
-
 
617
 
-
 
618
 
-
 
619
/**
-
 
620
 * Clears all clusters and markers from the clusterer.
-
 
621
 */
-
 
622
MarkerClusterer.prototype.clearMarkers = function() {
-
 
623
  this.resetViewport();
-
 
624
 
-
 
625
  // Set the markers a empty array.
-
 
626
  this.markers_ = [];
-
 
627
};
-
 
628
 
-
 
629
 
-
 
630
/**
-
 
631
 * Clears all existing clusters and recreates them.
-
 
632
 */
-
 
633
MarkerClusterer.prototype.resetViewport = function() {
-
 
634
  // Remove all the clusters
-
 
635
  for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
-
 
636
    cluster.remove();
-
 
637
  }
-
 
638
 
-
 
639
  // Reset the markers to not be added and to be invisible.
-
 
640
  for (var i = 0, marker; marker = this.markers_[i]; i++) {
-
 
641
    marker.isAdded = false;
-
 
642
    marker.setMap(null);
-
 
643
    marker.setVisible(false);
-
 
644
  }
-
 
645
 
-
 
646
  this.clusters_ = [];
-
 
647
};
-
 
648
 
-
 
649
 
-
 
650
/**
-
 
651
 * Redraws the clusters.
-
 
652
 */
-
 
653
MarkerClusterer.prototype.redraw = function() {
-
 
654
  this.createClusters_();
-
 
655
};
-
 
656
 
-
 
657
 
-
 
658
/**
-
 
659
 * Creates the clusters.
-
 
660
 *
-
 
661
 * @private
-
 
662
 */
-
 
663
MarkerClusterer.prototype.createClusters_ = function() {
-
 
664
  if (!this.ready_) {
-
 
665
    return;
-
 
666
  }
-
 
667
 
-
 
668
  // Get our current map view bounds.
-
 
669
  // Create a new bounds object so we don't affect the map.
-
 
670
  var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),
-
 
671
      this.map_.getBounds().getNorthEast());
-
 
672
  var bounds = this.getExtendedBounds(mapBounds);
-
 
673
 
-
 
674
  for (var i = 0, marker; marker = this.markers_[i]; i++) {
-
 
675
    var added = false;
-
 
676
    if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
-
 
677
      for (var j = 0, cluster; cluster = this.clusters_[j]; j++) {
-
 
678
        if (!added && cluster.getCenter() &&
-
 
679
            cluster.isMarkerInClusterBounds(marker)) {
-
 
680
          added = true;
-
 
681
          cluster.addMarker(marker);
-
 
682
          break;
-
 
683
        }
-
 
684
      }
-
 
685
 
-
 
686
      if (!added) {
-
 
687
        // Create a new cluster.
-
 
688
        var cluster = new Cluster(this);
-
 
689
        cluster.addMarker(marker);
-
 
690
        this.clusters_.push(cluster);
-
 
691
      }
-
 
692
    }
-
 
693
  }
-
 
694
};
-
 
695
 
-
 
696
 
-
 
697
/**
-
 
698
 * A cluster that contains markers.
-
 
699
 *
-
 
700
 * @param {MarkerClusterer} markerClusterer The markerclusterer that this
-
 
701
 *     cluster is associated with.
-
 
702
 * @constructor
-
 
703
 * @ignore
-
 
704
 */
-
 
705
function Cluster(markerClusterer) {
-
 
706
  this.markerClusterer_ = markerClusterer;
-
 
707
  this.map_ = markerClusterer.getMap();
-
 
708
  this.gridSize_ = markerClusterer.getGridSize();
-
 
709
  this.averageCenter_ = markerClusterer.isAverageCenter();
-
 
710
  this.center_ = null;
-
 
711
  this.markers_ = [];
-
 
712
  this.bounds_ = null;
-
 
713
  this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),
-
 
714
      markerClusterer.getGridSize());
-
 
715
}
-
 
716
 
-
 
717
/**
-
 
718
 * Determins if a marker is already added to the cluster.
-
 
719
 *
-
 
720
 * @param {google.maps.Marker} marker The marker to check.
-
 
721
 * @return {boolean} True if the marker is already added.
-
 
722
 */
-
 
723
Cluster.prototype.isMarkerAlreadyAdded = function(marker) {
-
 
724
  if (this.markers_.indexOf) {
-
 
725
    return this.markers_.indexOf(marker) != -1;
-
 
726
  } else {
-
 
727
    for (var i = 0, m; m = this.markers_[i]; i++) {
-
 
728
      if (m == marker) {
-
 
729
        return true;
-
 
730
      }
-
 
731
    }
-
 
732
  }
-
 
733
  return false;
-
 
734
};
-
 
735
 
-
 
736
 
-
 
737
/**
-
 
738
 * Add a marker the cluster.
-
 
739
 *
-
 
740
 * @param {google.maps.Marker} marker The marker to add.
-
 
741
 * @return {boolean} True if the marker was added.
-
 
742
 */
-
 
743
Cluster.prototype.addMarker = function(marker) {
-
 
744
  if (this.isMarkerAlreadyAdded(marker)) {
-
 
745
    return false;
-
 
746
  }
-
 
747
 
-
 
748
  if (!this.center_) {
-
 
749
    this.center_ = marker.getPosition();
-
 
750
    this.calculateBounds_();
-
 
751
  } else {
-
 
752
    if (this.averageCenter_) {
-
 
753
      var l = this.markers_.length + 1;
-
 
754
      var latDiff = (this.center_.lat() - marker.getPosition().lat()) / l;
-
 
755
      var lngDiff = (this.center_.lng() - marker.getPosition().lng()) / l;
-
 
756
      var lat = this.center_.lat() + latDiff;
-
 
757
      var lng = this.center_.lng() + lngDiff;
-
 
758
      this.center_ = new google.maps.LatLng(lat, lng);
-
 
759
      this.calculateBounds_();
-
 
760
    }
-
 
761
  }
-
 
762
 
-
 
763
 
-
 
764
  if (this.markers_.length == 0) {
-
 
765
    // Only 1 marker in this cluster so show the marker.
-
 
766
    marker.setMap(this.map_);
-
 
767
    marker.setVisible(true);
-
 
768
  } else if (this.markers_.length == 1) {
-
 
769
    // Hide the 1 marker that was showing.
-
 
770
    this.markers_[0].setMap(null);
-
 
771
    this.markers_[0].setVisible(false);
-
 
772
  }
-
 
773
 
-
 
774
  marker.isAdded = true;
-
 
775
  this.markers_.push(marker);
-
 
776
 
-
 
777
  this.updateIcon();
-
 
778
  return true;
-
 
779
};
-
 
780
 
-
 
781
 
-
 
782
/**
-
 
783
 * Returns the marker clusterer that the cluster is associated with.
-
 
784
 *
-
 
785
 * @return {MarkerClusterer} The associated marker clusterer.
-
 
786
 */
-
 
787
Cluster.prototype.getMarkerClusterer = function() {
-
 
788
  return this.markerClusterer_;
-
 
789
};
-
 
790
 
-
 
791
 
-
 
792
/**
-
 
793
 * Returns the bounds of the cluster.
-
 
794
 *
-
 
795
 * @return {google.maps.LatLngBounds} the cluster bounds.
-
 
796
 */
-
 
797
Cluster.prototype.getBounds = function() {
-
 
798
  this.calculateBounds_();
-
 
799
  return this.bounds_;
-
 
800
};
-
 
801
 
-
 
802
 
-
 
803
/**
-
 
804
 * Removes the cluster
-
 
805
 */
-
 
806
Cluster.prototype.remove = function() {
-
 
807
  this.clusterIcon_.remove();
-
 
808
  this.markers_.length = 0;
-
 
809
  delete this.markers_;
-
 
810
};
-
 
811
 
-
 
812
 
-
 
813
/**
-
 
814
 * Returns the center of the cluster.
-
 
815
 *
-
 
816
 * @return {number} The cluster center.
-
 
817
 */
-
 
818
Cluster.prototype.getSize = function() {
-
 
819
  return this.markers_.length;
-
 
820
};
-
 
821
 
-
 
822
 
-
 
823
/**
-
 
824
 * Returns the center of the cluster.
-
 
825
 *
-
 
826
 * @return {Array.<google.maps.Marker>} The cluster center.
-
 
827
 */
-
 
828
Cluster.prototype.getMarkers = function() {
-
 
829
  return this.markers_;
-
 
830
};
-
 
831
 
-
 
832
 
-
 
833
/**
-
 
834
 * Returns the center of the cluster.
-
 
835
 *
-
 
836
 * @return {google.maps.LatLng} The cluster center.
-
 
837
 */
-
 
838
Cluster.prototype.getCenter = function() {
-
 
839
  return this.center_;
-
 
840
};
-
 
841
 
-
 
842
 
-
 
843
/**
-
 
844
 * Calculated the bounds of the cluster with the grid.
-
 
845
 *
-
 
846
 * @private
-
 
847
 */
-
 
848
Cluster.prototype.calculateBounds_ = function() {
-
 
849
  var bounds = new google.maps.LatLngBounds(this.center_, this.center_);
-
 
850
  this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);
-
 
851
};
-
 
852
 
-
 
853
 
-
 
854
/**
-
 
855
 * Determines if a marker lies in the clusters bounds.
-
 
856
 *
-
 
857
 * @param {google.maps.Marker} marker The marker to check.
-
 
858
 * @return {boolean} True if the marker lies in the bounds.
-
 
859
 */
-
 
860
Cluster.prototype.isMarkerInClusterBounds = function(marker) {
-
 
861
  return this.bounds_.contains(marker.getPosition());
-
 
862
};
-
 
863
 
-
 
864
 
-
 
865
/**
-
 
866
 * Returns the map that the cluster is associated with.
-
 
867
 *
-
 
868
 * @return {google.maps.Map} The map.
-
 
869
 */
-
 
870
Cluster.prototype.getMap = function() {
-
 
871
  return this.map_;
-
 
872
};
-
 
873
 
-
 
874
 
-
 
875
/**
-
 
876
 * Updates the cluster icon
-
 
877
 */
-
 
878
Cluster.prototype.updateIcon = function() {
-
 
879
  var zoom = this.map_.getZoom();
-
 
880
  var mz = this.markerClusterer_.getMaxZoom();
-
 
881
 
-
 
882
  if (zoom > mz) {
-
 
883
    // The zoom is greater than our max zoom so show all the markers in cluster.
-
 
884
    for (var i = 0, marker; marker = this.markers_[i]; i++) {
-
 
885
      marker.setMap(this.map_);
-
 
886
      marker.setVisible(true);
-
 
887
    }
-
 
888
    return;
-
 
889
  }
-
 
890
 
-
 
891
  if (this.markers_.length < 2) {
-
 
892
    // We have 0 or 1 markers so hide the icon.
-
 
893
    this.clusterIcon_.hide();
-
 
894
    return;
-
 
895
  }
-
 
896
 
-
 
897
  var numStyles = this.markerClusterer_.getStyles().length;
-
 
898
  var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);
-
 
899
  this.clusterIcon_.setCenter(this.center_);
-
 
900
  this.clusterIcon_.setSums(sums);
-
 
901
  this.clusterIcon_.show();
-
 
902
};
-
 
903
 
-
 
904
 
-
 
905
/**
-
 
906
 * A cluster icon
-
 
907
 *
-
 
908
 * @param {Cluster} cluster The cluster to be associated with.
-
 
909
 * @param {Object} styles An object that has style properties:
-
 
910
 *     'url': (string) The image url.
-
 
911
 *     'height': (number) The image height.
-
 
912
 *     'width': (number) The image width.
-
 
913
 *     'anchor': (Array) The anchor position of the label text.
-
 
914
 *     'textColor': (string) The text color.
-
 
915
 *     'textSize': (number) The text size.
-
 
916
 *     'backgroundPosition: (string) The background postition x, y.
-
 
917
 * @param {number=} opt_padding Optional padding to apply to the cluster icon.
-
 
918
 * @constructor
-
 
919
 * @extends google.maps.OverlayView
-
 
920
 * @ignore
-
 
921
 */
-
 
922
function ClusterIcon(cluster, styles, opt_padding) {
-
 
923
  cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);
-
 
924
 
-
 
925
  this.styles_ = styles;
-
 
926
  this.padding_ = opt_padding || 0;
-
 
927
  this.cluster_ = cluster;
-
 
928
  this.center_ = null;
-
 
929
  this.map_ = cluster.getMap();
-
 
930
  this.div_ = null;
-
 
931
  this.sums_ = null;
-
 
932
  this.visible_ = false;
-
 
933
 
-
 
934
  this.setMap(this.map_);
-
 
935
}
-
 
936
 
-
 
937
 
-
 
938
/**
-
 
939
 * Triggers the clusterclick event and zoom's if the option is set.
-
 
940
 */
-
 
941
ClusterIcon.prototype.triggerClusterClick = function() {
-
 
942
  var markerClusterer = this.cluster_.getMarkerClusterer();
-
 
943
 
-
 
944
  // Trigger the clusterclick event.
-
 
945
  google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);
-
 
946
 
-
 
947
  if (markerClusterer.isZoomOnClick()) {
-
 
948
    // Center the map on this cluster.
-
 
949
    this.map_.panTo(this.cluster_.getCenter());
-
 
950
 
-
 
951
    // Zoom into the cluster.
-
 
952
    this.map_.fitBounds(this.cluster_.getBounds());
-
 
953
  }
-
 
954
};
-
 
955
 
-
 
956
 
-
 
957
/**
-
 
958
 * Adding the cluster icon to the dom.
-
 
959
 * @ignore
-
 
960
 */
-
 
961
ClusterIcon.prototype.onAdd = function() {
-
 
962
  this.div_ = document.createElement('DIV');
-
 
963
  if (this.visible_) {
-
 
964
    var pos = this.getPosFromLatLng_(this.center_);
-
 
965
    this.div_.style.cssText = this.createCss(pos);
-
 
966
    this.div_.innerHTML = this.sums_.text;
-
 
967
  }
-
 
968
 
-
 
969
  var panes = this.getPanes();
-
 
970
  panes.overlayImage.appendChild(this.div_);
-
 
971
 
-
 
972
  var that = this;
-
 
973
  google.maps.event.addDomListener(this.div_, 'click', function() {
-
 
974
    that.triggerClusterClick();
-
 
975
  });
-
 
976
};
-
 
977
 
-
 
978
 
-
 
979
/**
-
 
980
 * Returns the position to place the div dending on the latlng.
-
 
981
 *
-
 
982
 * @param {google.maps.LatLng} latlng The position in latlng.
-
 
983
 * @return {google.maps.Point} The position in pixels.
-
 
984
 * @private
-
 
985
 */
-
 
986
ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
-
 
987
  var pos = this.getProjection().fromLatLngToDivPixel(latlng);
-
 
988
  pos.x -= parseInt(this.width_ / 2, 10);
-
 
989
  pos.y -= parseInt(this.height_ / 2, 10);
-
 
990
  return pos;
-
 
991
};
-
 
992
 
-
 
993
 
-
 
994
/**
-
 
995
 * Draw the icon.
-
 
996
 * @ignore
-
 
997
 */
-
 
998
ClusterIcon.prototype.draw = function() {
-
 
999
  if (this.visible_) {
-
 
1000
    var pos = this.getPosFromLatLng_(this.center_);
-
 
1001
    this.div_.style.top = pos.y + 'px';
-
 
1002
    this.div_.style.left = pos.x + 'px';
-
 
1003
  }
-
 
1004
};
-
 
1005
 
-
 
1006
 
-
 
1007
/**
-
 
1008
 * Hide the icon.
-
 
1009
 */
-
 
1010
ClusterIcon.prototype.hide = function() {
-
 
1011
  if (this.div_) {
-
 
1012
    this.div_.style.display = 'none';
-
 
1013
  }
-
 
1014
  this.visible_ = false;
-
 
1015
};
-
 
1016
 
-
 
1017
 
-
 
1018
/**
-
 
1019
 * Position and show the icon.
-
 
1020
 */
-
 
1021
ClusterIcon.prototype.show = function() {
-
 
1022
  if (this.div_) {
-
 
1023
    var pos = this.getPosFromLatLng_(this.center_);
-
 
1024
    this.div_.style.cssText = this.createCss(pos);
-
 
1025
    this.div_.style.display = '';
-
 
1026
  }
-
 
1027
  this.visible_ = true;
-
 
1028
};
-
 
1029
 
-
 
1030
 
-
 
1031
/**
-
 
1032
 * Remove the icon from the map
-
 
1033
 */
-
 
1034
ClusterIcon.prototype.remove = function() {
-
 
1035
  this.setMap(null);
-
 
1036
};
-
 
1037
 
-
 
1038
 
-
 
1039
/**
-
 
1040
 * Implementation of the onRemove interface.
-
 
1041
 * @ignore
-
 
1042
 */
-
 
1043
ClusterIcon.prototype.onRemove = function() {
-
 
1044
  if (this.div_ && this.div_.parentNode) {
-
 
1045
    this.hide();
-
 
1046
    this.div_.parentNode.removeChild(this.div_);
-
 
1047
    this.div_ = null;
-
 
1048
  }
-
 
1049
};
-
 
1050
 
-
 
1051
 
-
 
1052
/**
-
 
1053
 * Set the sums of the icon.
-
 
1054
 *
-
 
1055
 * @param {Object} sums The sums containing:
-
 
1056
 *   'text': (string) The text to display in the icon.
-
 
1057
 *   'index': (number) The style index of the icon.
-
 
1058
 */
-
 
1059
ClusterIcon.prototype.setSums = function(sums) {
-
 
1060
  this.sums_ = sums;
-
 
1061
  this.text_ = sums.text;
-
 
1062
  this.index_ = sums.index;
-
 
1063
  if (this.div_) {
-
 
1064
    this.div_.innerHTML = sums.text;
-
 
1065
  }
-
 
1066
 
-
 
1067
  this.useStyle();
-
 
1068
};
-
 
1069
 
-
 
1070
 
-
 
1071
/**
-
 
1072
 * Sets the icon to the the styles.
-
 
1073
 */
-
 
1074
ClusterIcon.prototype.useStyle = function() {
-
 
1075
  var index = Math.max(0, this.sums_.index - 1);
-
 
1076
  index = Math.min(this.styles_.length - 1, index);
-
 
1077
  var style = this.styles_[index];
-
 
1078
  this.url_ = style['url'];
-
 
1079
  this.height_ = style['height'];
-
 
1080
  this.width_ = style['width'];
-
 
1081
  this.textColor_ = style['textColor'];
-
 
1082
  this.anchor_ = style['anchor'];
-
 
1083
  this.textSize_ = style['textSize'];
-
 
1084
  this.backgroundPosition_ = style['backgroundPosition'];
-
 
1085
};
-
 
1086
 
-
 
1087
 
-
 
1088
/**
-
 
1089
 * Sets the center of the icon.
-
 
1090
 *
-
 
1091
 * @param {google.maps.LatLng} center The latlng to set as the center.
-
 
1092
 */
-
 
1093
ClusterIcon.prototype.setCenter = function(center) {
-
 
1094
  this.center_ = center;
-
 
1095
};
-
 
1096
 
-
 
1097
 
-
 
1098
/**
-
 
1099
 * Create the css text based on the position of the icon.
-
 
1100
 *
-
 
1101
 * @param {google.maps.Point} pos The position.
-
 
1102
 * @return {string} The css style text.
-
 
1103
 */
-
 
1104
ClusterIcon.prototype.createCss = function(pos) {
-
 
1105
  var style = [];
-
 
1106
  if (document.all) {
-
 
1107
    style.push('filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(' +
-
 
1108
        'sizingMethod=scale,src="' + this.url_ + '");');
-
 
1109
  } else {
-
 
1110
    style.push('background-image:url(' + this.url_ + ');');
-
 
1111
    var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';
-
 
1112
    style.push('background-position:' + backgroundPosition + ';');
-
 
1113
  }
-
 
1114
 
-
 
1115
  if (typeof this.anchor_ === 'object') {
-
 
1116
    if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&
-
 
1117
        this.anchor_[0] < this.height_) {
-
 
1118
      style.push('height:' + (this.height_ - this.anchor_[0]) +
-
 
1119
          'px; padding-top:' + this.anchor_[0] + 'px;');
-
 
1120
    } else {
-
 
1121
      style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +
-
 
1122
          'px;');
-
 
1123
    }
-
 
1124
    if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&
-
 
1125
        this.anchor_[1] < this.width_) {
-
 
1126
      style.push('width:' + (this.width_ - this.anchor_[1]) +
-
 
1127
          'px; padding-left:' + this.anchor_[1] + 'px;');
-
 
1128
    } else {
-
 
1129
      style.push('width:' + this.width_ + 'px; text-align:center;');
-
 
1130
    }
-
 
1131
  } else {
-
 
1132
    style.push('height:' + this.height_ + 'px; line-height:' +
-
 
1133
        this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');
-
 
1134
  }
-
 
1135
 
-
 
1136
  var txtColor = this.textColor_ ? this.textColor_ : 'black';
-
 
1137
  var txtSize = this.textSize_ ? this.textSize_ : 11;
-
 
1138
 
-
 
1139
  style.push('cursor:pointer; top:' + pos.y + 'px; left:' +
-
 
1140
      pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +
-
 
1141
      txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');
-
 
1142
  return style.join('');