Subversion Repositories eFlore/Applications.cel

Rev

Rev 466 | Show entire file | Regard 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
 
33
 
-
 
34
/**
-
 
35
 * A Marker Clusterer that clusters markers.
39
/**
36
 *
40
 * @name MarkerClustererOptions
37
 * @param {google.maps.Map} map The Google map to attach to.
41
 * @class This class represents optional arguments to the {@link MarkerClusterer}
38
 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to
42
 * constructor.
39
 *   the cluster.
43
 * @property {Number} [maxZoom] The max zoom level monitored by a
40
 * @param {Object=} opt_options support the following options:
44
 * marker cluster. If not given, the marker cluster assumes the maximum map
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.
45
 * zoom level. When maxZoom is reached or exceeded all markers will be shown
44
 *     'zoomOnClick': (boolean) Whether the default behaviour of clicking on a
46
 * without cluster.
45
 *                    cluster is to zoom into it.
-
 
46
 *     'averageCenter': (boolean) Wether the center of each cluster should be
47
 * @property {Number} [gridSize=60] The grid size of a cluster in pixel. Each
47
 *                      the average of all markers in the cluster.
48
 * cluster will be a square. If you want the algorithm to run faster, you can set
48
 *     'styles': (object) An object that has style properties:
49
 * this value larger.
49
 *       'url': (string) The image url.
50
 * @property {Array of MarkerStyleOptions} [styles]
50
 *       'height': (number) The image height.
-
 
51
 *       'width': (number) The image width.
-
 
52
 *       'anchor': (Array) The anchor position of the label text.
51
 * Custom styles for the cluster markers.
53
 *       'textColor': (string) The text color.
52
 * The array should be ordered according to increasing cluster size,
54
 *       'textSize': (number) The text size.
-
 
55
 *       'backgroundPosition': (string) The position of the backgound x, y.
53
 * with the style for the smallest clusters first, and the style for the
56
 * @constructor
-
 
57
 * @extends google.maps.OverlayView
-
 
58
 */
-
 
59
function MarkerClusterer(map, opt_markers, opt_options) {
-
 
60
  // MarkerClusterer implements google.maps.OverlayView interface. We use the
-
 
61
  // extend function to extend MarkerClusterer with google.maps.OverlayView
-
 
62
  // because it might not always be available when the code is defined so we
-
 
63
  // look for it at the last possible moment. If it doesn't exist now then
-
 
64
  // there is no point going ahead :)
Line 54... Line 65...
54
 * largest clusters last.
65
  this.extend(MarkerClusterer, google.maps.OverlayView);
55
 */
66
  this.map_ = map;
56
 
-
 
57
/**
67
 
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]. 
68
  /**
-
 
69
   * @type {Array.<google.maps.Marker>}
Line 65... Line 70...
65
 *    If not set, the text will align center and middle.
70
   * @private
66
 * @property {String} [opt_textColor="black"] Text color.
-
 
67
 */
-
 
68
 
71
   */
69
/**
-
 
70
 * Creates a new MarkerClusterer to cluster markers on the map.
-
 
71
 *
-
 
72
 * @constructor
72
  this.markers_ = [];
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.
-
 
75
 * @param {MarkerClustererOptions} opt_opts A container for optional arguments.
73
 
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) {
-
 
91
    styles_.push({
-
 
92
      'url': "http://gmaps-utility-library.googlecode.com/svn/trunk/markerclusterer/images/m" + i + ".png",
-
 
Line 93... Line -...
93
      'height': sizes[i - 1],
-
 
94
      'width': sizes[i - 1]
-
 
95
    });
74
  /**
96
  }
-
 
97
 
-
 
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;
-
 
Line 104... Line 75...
104
    }
75
   *  @type {Array.<Cluster>}
105
    if (typeof opt_opts.styles === "object" && opt_opts.styles !== null && opt_opts.styles.length !== 0) {
-
 
106
      styles_ = opt_opts.styles;
-
 
107
    }
76
   */
108
  }
77
  this.clusters_ = [];
109
 
-
 
110
  /**
-
 
111
   * When we add a marker, the marker may not in the viewport of map, then we don't deal with it, instead
-
 
112
   * we add the marker into a array called leftMarkers_. When we reset MarkerClusterer we should add the
-
 
113
   * leftMarkers_ into MarkerClusterer.
78
 
114
   */
-
 
115
  function addLeftMarkers_() {
-
 
116
    if (leftMarkers_.length === 0) {
-
 
117
      return;
-
 
118
    }
-
 
Line 119... Line 79...
119
    var leftMarkers = [];
79
  this.sizes = [53, 56, 66, 78, 90];
120
    for (i = 0; i < leftMarkers_.length; ++i) {
80
 
121
      me_.addMarker(leftMarkers_[i], true, null, null, true);
81
  /**
122
    }
-
 
123
    leftMarkers_ = leftMarkers;
82
   * @private
124
  }
83
   */
-
 
84
  this.styles_ = [];
125
 
85
 
126
  /**
-
 
Line 127... Line 86...
127
   * Get cluster marker images of this marker cluster. Mostly used by {@link Cluster}
86
  /**
128
   * @private
87
   * @type {boolean}
-
 
88
   * @private
129
   * @return {Array of String}
89
   */
130
   */
90
  this.ready_ = false;
131
  this.getStyles_ = function () {
-
 
132
    return styles_;
-
 
133
  };
-
 
134
 
-
 
135
  /**
-
 
136
   * Remove all markers from MarkerClusterer.
-
 
137
   */
-
 
138
  this.clearMarkers = function () {
-
 
139
    for (var i = 0; i < clusters_.length; ++i) {
-
 
Line 140... Line 91...
140
      if (typeof clusters_[i] !== "undefined" && clusters_[i] !== null) {
91
 
141
        clusters_[i].clearMarkers();
92
  var options = opt_options || {};
142
      }
93
 
143
    }
-
 
144
    clusters_ = [];
94
  /**
145
    leftMarkers_ = [];
-
 
146
    GEvent.removeListener(mcfn_);
95
   * @type {number}
147
  };
96
   * @private
-
 
97
   */
Line 148... Line 98...
148
 
98
  this.gridSize_ = options['gridSize'] || 60;
149
  /**
-
 
150
   * Check a marker, whether it is in current map viewport.
99
 
151
   * @private
100
  /**
152
   * @return {Boolean} if it is in current map viewport
101
   * @type {?number}
-
 
102
   * @private
153
   */
103
   */
-
 
104
  this.maxZoom_ = options['maxZoom'] || null;
-
 
105
 
154
  function isMarkerInViewport_(marker) {
106
  this.styles_ = options['styles'] || [];
-
 
107
 
-
 
108
  /**
-
 
109
   * @type {string}
-
 
110
   * @private
-
 
111
   */
-
 
112
  this.imagePath_ = options['imagePath'] ||
-
 
113
      this.MARKER_CLUSTER_IMAGE_PATH_;
-
 
114
 
-
 
115
  /**
155
    return map_.getBounds().containsLatLng(marker.getLatLng());
116
   * @type {string}
-
 
117
   * @private
156
  }
118
   */
157
 
119
  this.imageExtension_ = options['imageExtension'] ||
158
  /**
120
      this.MARKER_CLUSTER_IMAGE_EXTENSION_;
-
 
121
 
-
 
122
  /**
159
   * When reset MarkerClusterer, there will be some markers get out of its cluster.
123
   * @type {boolean}
-
 
124
   * @private
-
 
125
   */
-
 
126
  this.zoomOnClick_ = true;
-
 
127
 
-
 
128
  if (options['zoomOnClick'] != undefined) {
-
 
129
    this.zoomOnClick_ = options['zoomOnClick'];
160
   * These markers should be add to new clusters.
130
  }
Line -... Line 131...
-
 
131
 
-
 
132
  /**
-
 
133
   * @type {boolean}
-
 
134
   * @private
161
   * @param {Array of GMarker} markers Markers to add.
135
   */
162
   */
136
  this.averageCenter_ = false;
163
  function reAddMarkers_(markers) {
137
 
164
    var len = markers.length;
-
 
165
    var clusters = [];
-
 
166
    for (var i = len - 1; i >= 0; --i) {
-
 
167
      me_.addMarker(markers[i].marker, true, markers[i].isAdded, clusters, true);
-
 
168
    }
-
 
169
    addLeftMarkers_();
138
  if (options['averageCenter'] != undefined) {
170
  }
139
    this.averageCenter_ = options['averageCenter'];
-
 
140
  }
171
 
141
 
-
 
142
  this.setupStyles_();
-
 
143
 
-
 
144
  this.setMap(map);
172
  /**
145
 
173
   * Add a marker.
146
  /**
174
   * @private
147
   * @type {number}
175
   * @param {GMarker} marker Marker you want to add
148
   * @private
-
 
149
   */
-
 
150
  this.prevZoom_ = this.map_.getZoom();
-
 
151
 
-
 
152
  // Add the map event listeners
176
   * @param {Boolean} opt_isNodraw Whether redraw the cluster contained the marker
153
  var that = this;
-
 
154
  google.maps.event.addListener(this.map_, 'zoom_changed', function() {
Line 177... Line -...
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.
155
    var maxZoom = that.map_.mapTypes[that.map_.getMapTypeId()].maxZoom;
180
   */
-
 
181
  this.addMarker = function (marker, opt_isNodraw, opt_isAdded, opt_clusters, opt_isNoCheck) {
-
 
182
    if (opt_isNoCheck !== true) {
156
    var zoom = that.map_.getZoom();
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();
157
    if (zoom < 0 || zoom > maxZoom) {
-
 
158
      return;
205
      if (center === null) {
159
    }
206
        continue;
160
 
207
      }
-
 
208
      center = map_.fromLatLngToDivPixel(center);
161
    if (that.prevZoom_ != zoom) {
209
 
162
      that.prevZoom_ = that.map_.getZoom();
210
      // Found a cluster which contains the marker.
163
      that.resetViewport();
Line 211... Line -...
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
        }
-
 
Line -... Line 164...
-
 
164
    }
-
 
165
  });
-
 
166
 
-
 
167
  google.maps.event.addListener(this.map_, 'idle', function() {
-
 
168
    that.redraw();
-
 
169
  });
-
 
170
 
220
        return;
171
  // Finally, add the markers
-
 
172
  if (opt_markers && opt_markers.length) {
-
 
173
    this.addMarkers(opt_markers, false);
-
 
174
  }
-
 
175
}
221
      }
176
 
-
 
177
 
-
 
178
/**
-
 
179
 * The marker cluster image path.
-
 
180
 *
-
 
181
 * @type {string}
-
 
182
 * @private
-
 
183
 */
-
 
184
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ =
-
 
185
    'http://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' +
-
 
186
    'images/m';
-
 
187
 
-
 
188
 
-
 
189
/**
-
 
190
 * The marker cluster image path.
-
 
191
 *
-
 
192
 * @type {string}
222
    }
193
 * @private
223
 
194
 */
-
 
195
MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';
224
    // No cluster contain the marker, create a new cluster.
196
 
-
 
197
 
-
 
198
/**
225
    cluster = new Cluster(this, map);
199
 * Extends a objects prototype by anothers.
Line -... Line 200...
-
 
200
 *
226
    cluster.addMarker({
201
 * @param {Object} obj1 The object to be extended.
227
      'isAdded': isAdded,
202
 * @param {Object} obj2 The object to extend with.
228
      'marker': marker
203
 * @return {Object} The new extended object.
229
    });
-
 
230
    if (!opt_isNodraw) {
204
 * @ignore
-
 
205
 */
-
 
206
MarkerClusterer.prototype.extend = function(obj1, obj2) {
-
 
207
  return (function(object) {
-
 
208
    for (var property in object.prototype) {
-
 
209
      this.prototype[property] = object.prototype[property];
-
 
210
    }
-
 
211
    return this;
-
 
212
  }).apply(obj1, [obj2]);
-
 
213
};
Line -... Line 214...
-
 
214
 
231
      cluster.redraw_();
215
 
-
 
216
/**
232
    }
217
 * Implementaion of the interface method.
-
 
218
 * @ignore
233
 
219
 */
234
    // Add this cluster both in clusters provided and clusters_
220
MarkerClusterer.prototype.onAdd = function() {
235
    clusters.push(cluster);
221
  this.setReady_(true);
236
    if (clusters !== clusters_) {
222
};
-
 
223
 
-
 
224
/**
-
 
225
 * Implementaion of the interface method.
-
 
226
 * @ignore
-
 
227
 */
-
 
228
MarkerClusterer.prototype.draw = function() {};
-
 
229
 
237
      clusters_.push(cluster);
230
/**
238
    }
231
 * Sets up the styles object.
Line -... Line 232...
-
 
232
 *
239
  };
233
 * @private
-
 
234
 */
-
 
235
MarkerClusterer.prototype.setupStyles_ = function() {
240
 
236
  if (this.styles_.length) {
241
  /**
237
    return;
242
   * Remove a marker.
-
 
243
   *
238
  }
244
   * @param {GMarker} marker The marker you want to remove.
-
 
245
   */
239
 
246
 
-
 
247
  this.removeMarker = function (marker) {
240
  for (var i = 0, size; size = this.sizes[i]; i++) {
Line -... Line 241...
-
 
241
    this.styles_.push({
248
    for (var i = 0; i < clusters_.length; ++i) {
242
      url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,
249
      if (clusters_[i].remove(marker)) {
243
      height: size,
-
 
244
      width: size
250
        clusters_[i].redraw_();
245
    });
251
        return;
246
  }
252
      }
247
};
253
    }
-
 
254
  };
-
 
255
 
-
 
256
  /**
-
 
257
   * Redraw all clusters in viewport.
-
 
258
   */
-
 
259
  this.redraw_ = function () {
-
 
260
    var clusters = this.getClustersInViewport_();
248
 
261
    for (var i = 0; i < clusters.length; ++i) {
249
 
Line -... Line 250...
-
 
250
/**
262
      clusters[i].redraw_(true);
251
 *  Sets the styles.
263
    }
252
 *
264
  };
253
 *  @param {Object} styles The style to set.
265
 
254
 */
266
  /**
255
MarkerClusterer.prototype.setStyles = function(styles) {
267
   * Get all clusters in viewport.
256
  this.styles_ = styles;
268
   * @return {Array of Cluster}
257
};
269
   */
258
 
Line 270... Line 259...
270
  this.getClustersInViewport_ = function () {
259
 
271
    var clusters = [];
260
/**
272
    var curBounds = map_.getBounds();
261
 *  Gets the styles.
273
    for (var i = 0; i < clusters_.length; i ++) {
262
 *
274
      if (clusters_[i].isInBounds(curBounds)) {
263
 *  @return {Object} The styles object.
275
        clusters.push(clusters_[i]);
264
 */
276
      }
265
MarkerClusterer.prototype.getStyles = function() {
277
    }
266
  return this.styles_;
Line -... Line 267...
-
 
267
};
278
    return clusters;
268
 
279
  };
269
 
280
 
270
/**
281
  /**
271
 * Whether zoom on click is set.
282
   * Get max zoom level.
272
 *
283
   * @private
273
 * @return {boolean} True if zoomOnClick_ is set.
284
   * @return {Number}
274
 */
285
   */
275
MarkerClusterer.prototype.isZoomOnClick = function() {
Line -... Line 276...
-
 
276
  return this.zoomOnClick_;
286
  this.getMaxZoom_ = function () {
277
};
287
    return maxZoom_;
278
 
-
 
279
/**
288
  };
280
 * Whether average center is set.
289
 
281
 *
290
  /**
282
 * @return {boolean} True if averageCenter_ is set.
-
 
283
 */
-
 
284
MarkerClusterer.prototype.isAverageCenter = function() {
-
 
285
  return this.averageCenter_;
-
 
286
};
-
 
287
 
-
 
288
 
-
 
289
/**
-
 
290
 *  Returns the array of markers in the clusterer.
-
 
291
 *
-
 
292
 *  @return {Array.<google.maps.Marker>} The markers.
-
 
293
 */
-
 
294
MarkerClusterer.prototype.getMarkers = function() {
-
 
295
  return this.markers_;
-
 
296
};
-
 
297
 
-
 
298
 
-
 
299
/**
-
 
300
 *  Returns the number of markers in the clusterer
-
 
301
 *
-
 
302
 *  @return {Number} The number of markers.
-
 
303
 */
-
 
304
MarkerClusterer.prototype.getTotalMarkers = function() {
-
 
305
  return this.markers_.length;
-
 
306
};
-
 
307
 
-
 
308
 
-
 
309
/**
-
 
310
 *  Sets the max zoom for the clusterer.
-
 
311
 *
-
 
312
 *  @param {number} maxZoom The max zoom level.
-
 
313
 */
-
 
314
MarkerClusterer.prototype.setMaxZoom = function(maxZoom) {
-
 
315
  this.maxZoom_ = maxZoom;
291
   * Get map object.
316
};
292
   * @private
317
 
-
 
318
 
-
 
319
/**
293
   * @return {GMap2}
320
 *  Gets the max zoom for the clusterer.
-
 
321
 *
294
   */
322
 *  @return {number} The max zoom level.
-
 
323
 */
-
 
324
MarkerClusterer.prototype.getMaxZoom = function() {
-
 
325
  return this.maxZoom_ || this.map_.mapTypes[this.map_.getMapTypeId()].maxZoom;
295
  this.getMap_ = function () {
326
};
-
 
327
 
-
 
328
 
296
    return map_;
329
/**
Line -... Line 330...
-
 
330
 *  The function for calculating the cluster icon image.
297
  };
331
 *
298
 
332
 *  @param {Array.<google.maps.Marker>} markers The markers in the clusterer.
-
 
333
 *  @param {number} numStyles The number of styles available.
-
 
334
 *  @return {Object} A object properties: 'text' (string) and 'index' (number).
-
 
335
 *  @private
299
  /**
336
 */
-
 
337
MarkerClusterer.prototype.calculator_ = function(markers, numStyles) {
300
   * Get grid size
338
  var index = 0;
301
   * @private
339
  var count = markers.length;
302
   * @return {Number}
340
  var dv = count;
303
   */
341
  while (dv !== 0) {
Line -... Line 342...
-
 
342
    dv = parseInt(dv / 10, 10);
304
  this.getGridSize_ = function () {
343
    index++;
-
 
344
  }
-
 
345
 
305
    return gridSize_;
346
  index = Math.min(index, numStyles);
306
  };
347
  return {
307
 
348
    text: count,
308
  /**
-
 
309
   * Get total number of markers.
349
    index: index
310
   * @return {Number}
350
  };
-
 
351
};
Line -... Line 352...
-
 
352
 
311
   */
353
 
-
 
354
/**
312
  this.getTotalMarkers = function () {
355
 * Set the calculator function.
313
    var result = 0;
356
 *
-
 
357
 * @param {function(Array, number)} calculator The function to set as the
-
 
358
 *     calculator. The function should return a object properties:
314
    for (var i = 0; i < clusters_.length; ++i) {
359
 *     'text' (string) and 'index' (number).
315
      result += clusters_[i].getTotalMarkers();
360
 *
316
    }
361
 */
317
    return result;
362
MarkerClusterer.prototype.setCalculator = function(calculator) {
318
  };
363
  this.calculator_ = calculator;
-
 
364
};
-
 
365
 
-
 
366
 
Line -... Line 367...
-
 
367
/**
-
 
368
 * Get the calculator function.
-
 
369
 *
319
 
370
 * @return {function(Array, number)} the calculator function.
-
 
371
 */
-
 
372
MarkerClusterer.prototype.getCalculator = function() {
-
 
373
  return this.calculator_;
320
  /**
374
};
321
   * Get total number of clusters.
375
 
-
 
376
 
322
   * @return {int}
377
/**
-
 
378
 * Add an array of markers to the clusterer.
-
 
379
 *
323
   */
380
 * @param {Array.<google.maps.Marker>} markers The markers to add.
-
 
381
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
324
  this.getTotalClusters = function () {
382
 */
-
 
383
MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) {
-
 
384
  for (var i = 0, marker; marker = markers[i]; i++) {
-
 
385
    this.pushMarkerTo_(marker);
-
 
386
  }
325
    return clusters_.length;
387
  if (!opt_nodraw) {
326
  };
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
327
 
398
 */
-
 
399
MarkerClusterer.prototype.pushMarkerTo_ = function(marker) {
-
 
400
  marker.setVisible(false);
328
  /**
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
};
329
   * Collect all markers of clusters in viewport and regroup them.
415
 
330
   */
416
 
331
  this.resetViewport = function () {
417
/**
332
    var clusters = this.getClustersInViewport_();
418
 * Adds a marker to the clusterer and redraws if needed.
333
    var tmpMarkers = [];
419
 *
-
 
420
 * @param {google.maps.Marker} marker The marker to add.
334
    var removed = 0;
421
 * @param {boolean=} opt_nodraw Whether to redraw the clusters.
335
 
422
 */
336
    for (var i = 0; i < clusters.length; ++i) {
423
MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) {
-
 
424
  this.pushMarkerTo_(marker);
-
 
425
  if (!opt_nodraw) {
-
 
426
    this.redraw();
-
 
427
  }
337
      var cluster = clusters[i];
428
};
Line 338... Line 429...
338
      var oldZoom = cluster.getCurrentZoom();
429
 
339
      if (oldZoom === null) {
430
 
340
        continue;
431
/**
-
 
432
 * Removes a marker and returns true if removed, false if not
-
 
433
 *
341
      }
434
 * @param {google.maps.Marker} marker The marker to remove
Line 342... Line 435...
342
      var curZoom = map_.getZoom();
435
 * @return {boolean} Whether the marker was removed or not
343
      if (curZoom !== oldZoom) {
436
 * @private
344
 
437
 */
345
        // If the cluster zoom level changed then destroy the cluster
438
MarkerClusterer.prototype.removeMarker_ = function(marker) {
-
 
439
  var index = -1;
-
 
440
  if (this.markers_.indexOf) {
346
        // and collect its markers.
441
    index = this.markers_.indexOf(marker);
347
        var mks = cluster.getMarkers();
442
  } else {
348
        for (var j = 0; j < mks.length; ++j) {
443
    for (var i = 0, m; m = this.markers_[i]; i++) {
-
 
444
      if (m == marker) {
-
 
445
        index = i;
349
          var newMarker = {
446
        break;
-
 
447
      }
-
 
448
    }
-
 
449
  }
-
 
450
 
350
            'isAdded': false,
451
  if (index == -1) {
351
            'marker': mks[j].marker
-
 
352
          };
452
    // Marker is not in our list of markers.
Line -... Line 453...
-
 
453
    return false;
-
 
454
  }
-
 
455
 
-
 
456
  this.markers_.splice(index, 1);
-
 
457
  marker.setVisible(false);
-
 
458
  marker.setMap(null);
-
 
459
 
-
 
460
  return true;
353
          tmpMarkers.push(newMarker);
461
};
-
 
462
 
354
        }
463
 
355
        cluster.clearMarkers();
464
/**
-
 
465
 * Remove a marker from the cluster.
356
        removed++;
466
 *
Line 357... Line 467...
357
        for (j = 0; j < clusters_.length; ++j) {
467
 * @param {google.maps.Marker} marker The marker to remove.
358
          if (cluster === clusters_[j]) {
468
 * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
359
            clusters_.splice(j, 1);
469
 * @return {boolean} True if the marker was removed.
360
          }
470
 */
361
        }
471
MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) {
-
 
472
  var removed = this.removeMarker_(marker);
-
 
473
 
Line 362... Line 474...
362
      }
474
  if (!opt_nodraw && removed) {
363
    }
475
    this.resetViewport();
364
 
-
 
365
    // Add the markers collected into marker cluster to reset
-
 
366
    reAddMarkers_(tmpMarkers);
-
 
367
    this.redraw_();
-
 
368
  };
476
    this.redraw();
369
 
477
    return true;
370
 
478
  } else {
371
  /**
-
 
372
   * Add a set of markers.
479
   return false;
373
   *
480
  }
374
   * @param {Array of GMarker} markers The markers you want to add.
481
};
375
   */
482
 
376
  this.addMarkers = function (markers) {
-
 
377
    for (var i = 0; i < markers.length; ++i) {
483
 
378
      this.addMarker(markers[i], true);
484
/**
379
    }
-
 
-
 
485
 * Removes an array of markers from the cluster.
-
 
486
 *
Line 380... Line 487...
380
    this.redraw_();
487
 * @param {Array.<google.maps.Marker>} markers The markers to remove.
381
  };
488
 * @param {boolean=} opt_nodraw Optional boolean to force no redraw.
382
 
489
 */
383
  // initialize
490
MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) {
384
  if (typeof opt_markers === "object" && opt_markers !== null) {
491
  var removed = false;
385
    this.addMarkers(opt_markers);
492
 
386
  }
493
  for (var i = 0, marker; marker = markers[i]; i++) {
387
 
494
    var r = this.removeMarker_(marker);
Line -... Line 495...
-
 
495
    removed = removed || r;
388
  // when map move end, regroup.
496
  }
389
  mcfn_ = GEvent.addListener(map_, "moveend", function () {
497
 
390
    me_.resetViewport();
498
  if (!opt_nodraw && removed) {
391
  });
-
 
392
}
499
    this.resetViewport();
393
 
500
    this.redraw();
394
/**
501
    return true;
395
 * Create a cluster to collect markers.
-
 
396
 * A cluster includes some markers which are in a block of area.
502
  }
397
 * If there are more than one markers in cluster, the cluster
503
};
Line 398... Line -...
398
 * will create a {@link ClusterMarker_} and show the total number
-
 
399
 * of markers in cluster.
-
 
400
 *
-
 
401
 * @constructor
-
 
402
 * @private
-
 
Line 403... Line -...
403
 * @param {MarkerClusterer} markerClusterer The marker cluster object
-
 
404
 */
504
 
405
function Cluster(markerClusterer) {
505
 
406
  var center_ = null;
-
 
407
  var markers_ = [];
-
 
408
  var markerClusterer_ = markerClusterer;
-
 
409
  var map_ = markerClusterer.getMap_();
506
/**
410
  var clusterMarker_ = null;
-
 
411
  var zoom_ = map_.getZoom();
507
 * Sets the clusterer's ready state.
412
 
508
 *
413
  /**
509
 * @param {boolean} ready The state.
414
   * Get markers of this cluster.
510
 * @private
415
   *
-
 
416
   * @return {Array of GMarker}
-
 
417
   */
511
 */
Line -... Line 512...
-
 
512
MarkerClusterer.prototype.setReady_ = function(ready) {
418
  this.getMarkers = function () {
513
  if (!this.ready_) {
419
    return markers_;
514
    this.ready_ = ready;
420
  };
515
    this.createClusters_();
421
 
516
  }
422
  /**
517
};
423
   * If this cluster intersects certain bounds.
518
 
424
   *
519
 
425
   * @param {GLatLngBounds} bounds A bounds to test
520
/**
Line -... Line 521...
-
 
521
 * Returns the number of clusters in the clusterer.
426
   * @return {Boolean} Is this cluster intersects the bounds
522
 *
427
   */
523
 * @return {number} The number of clusters.
428
  this.isInBounds = function (bounds) {
524
 */
429
    if (center_ === null) {
-
 
430
      return false;
-
 
431
    }
525
MarkerClusterer.prototype.getTotalClusters = function() {
432
 
526
  return this.clusters_.length;
433
    if (!bounds) {
527
};
434
      bounds = map_.getBounds();
528
 
435
    }
-
 
436
    var sw = map_.fromLatLngToDivPixel(bounds.getSouthWest());
-
 
437
    var ne = map_.fromLatLngToDivPixel(bounds.getNorthEast());
-
 
438
 
-
 
439
    var centerxy = map_.fromLatLngToDivPixel(center_);
-
 
440
    var inViewport = true;
-
 
441
    var gridSize = markerClusterer.getGridSize_();
-
 
442
    if (zoom_ !== map_.getZoom()) {
-
 
443
      var dl = map_.getZoom() - zoom_;
529
 
Line -... Line 530...
-
 
530
/**
444
      gridSize = Math.pow(2, dl) * gridSize;
531
 * Returns the google map that the clusterer is associated with.
445
    }
532
 *
446
    if (ne.x !== sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x)) {
533
 * @return {google.maps.Map} The map.
447
      inViewport = false;
534
 */
448
    }
535
MarkerClusterer.prototype.getMap = function() {
449
    if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y)) {
536
  return this.map_;
450
      inViewport = false;
537
};
451
    }
538
 
-
 
539
 
-
 
540
/**
-
 
541
 * Sets the google map that the clusterer is associated with.
452
    return inViewport;
542
 *
-
 
543
 * @param {google.maps.Map} map The map.
453
  };
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
 
454
 
570
/**
-
 
571
 * Extends a bounds object by the grid size.
-
 
572
 *
455
  /**
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);
456
   * Get cluster center.
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);
457
   *
597
 
458
   * @return {GLatLng}
598
  // Extend the bounds to contain the new bounds.
-
 
599
  bounds.extend(ne);
-
 
600
  bounds.extend(sw);
-
 
601
 
-
 
602
  return bounds;
-
 
603
};
-
 
604
 
459
   */
605
 
-
 
606
/**
460
  this.getCenter = function () {
607
 * Determins if a marker is contained in a bounds.
461
    return center_;
608
 *
Line -... Line 609...
-
 
609
 * @param {google.maps.Marker} marker The marker to check.
462
  };
610
 * @param {google.maps.LatLngBounds} bounds The bounds to check against.
463
 
-
 
464
  /**
-
 
465
   * Add a marker.
-
 
466
   *
611
 * @return {boolean} True if the marker is in the bounds.
467
   * @param {Object} marker An object of marker you want to add:
612
 * @private
468
   *   {Boolean} isAdded If the marker is added on map.
613
 */
469
   *   {GMarker} marker The marker you want to add.
614
MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) {
470
   */
615
  return bounds.contains(marker.getPosition());
Line -... Line 616...
-
 
616
};
471
  this.addMarker = function (marker) {
617
 
472
    if (center_ === null) {
618
 
-
 
619
/**
473
      /*var pos = marker['marker'].getLatLng();
620
 * Clears all clusters and markers from the clusterer.
474
       pos = map.fromLatLngToContainerPixel(pos);
-
 
475
       pos.x = parseInt(pos.x - pos.x % (GRIDWIDTH * 2) + GRIDWIDTH);
-
 
476
       pos.y = parseInt(pos.y - pos.y % (GRIDWIDTH * 2) + GRIDWIDTH);
621
 */
477
       center = map.fromContainerPixelToLatLng(pos);*/
622
MarkerClusterer.prototype.clearMarkers = function() {
478
      center_ = marker.marker.getLatLng();
623
  this.resetViewport();
479
    }
624
 
480
    markers_.push(marker);
625
  // Set the markers a empty array.
Line 481... Line 626...
481
  };
626
  this.markers_ = [];
482
 
627
};
483
  /**
-
 
484
   * Remove a marker from cluster.
628
 
485
   *
-
 
486
   * @param {GMarker} marker The marker you want to remove.
629
 
487
   * @return {Boolean} Whether find the marker to be removed.
-
 
488
   */
630
/**
489
  this.removeMarker = function (marker) {
631
 * Clears all existing clusters and recreates them.
490
    for (var i = 0; i < markers_.length; ++i) {
632
 */
-
 
633
MarkerClusterer.prototype.resetViewport = function() {
491
      if (marker === markers_[i].marker) {
634
  // Remove all the clusters
492
        if (markers_[i].isAdded) {
635
  for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {
493
          map_.removeOverlay(markers_[i].marker);
636
    cluster.remove();
494
        }
637
  }
-
 
638
 
495
        markers_.splice(i, 1);
639
  // Reset the markers to not be added and to be invisible.
-
 
640
  for (var i = 0, marker; marker = this.markers_[i]; i++) {
496
        return true;
641
    marker.isAdded = false;
497
      }
-
 
498
    }
-
 
499
    return false;
-
 
500
  };
642
    marker.setMap(null);
-
 
643
    marker.setVisible(false);
-
 
644
  }
-
 
645
 
-
 
646
  this.clusters_ = [];
-
 
647
};
-
 
648
 
501
 
649
 
502
  /**
-
 
503
   * Get current zoom level of this cluster.
-
 
504
   * Note: the cluster zoom level and map zoom level not always the same.
650
/**
505
   *
-
 
506
   * @return {Number}
-
 
507
   */
-
 
508
  this.getCurrentZoom = function () {
-
 
509
    return zoom_;
-
 
510
  };
-
 
511
 
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);
512
  /**
673
 
-
 
674
  for (var i = 0, marker; marker = this.markers_[i]; i++) {
-
 
675
    var added = false;
513
   * Redraw a cluster.
676
    if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {
-
 
677
      for (var j = 0, cluster; cluster = this.clusters_[j]; j++) {
514
   * @private
678
        if (!added && cluster.getCenter() &&
-
 
679
            cluster.isMarkerInClusterBounds(marker)) {
-
 
680
          added = true;
-
 
681
          cluster.addMarker(marker);
-
 
682
          break;
515
   * @param {Boolean} isForce If redraw by force, no matter if the cluster is
683
        }
516
   *     in viewport.
684
      }
-
 
685
 
517
   */
686
      if (!added) {
518
  this.redraw_ = function (isForce) {
687
        // Create a new cluster.
519
    if (!isForce && !this.isInBounds()) {
688
        var cluster = new Cluster(this);
520
      return;
-
 
521
    }
689
        cluster.addMarker(marker);
522
 
690
        this.clusters_.push(cluster);
-
 
691
      }
523
    // Set cluster zoom level.
692
    }
Line -... Line 693...
-
 
693
  }
524
    zoom_ = map_.getZoom();
694
};
525
    var i = 0;
695
 
-
 
696
 
-
 
697
/**
-
 
698
 * A cluster that contains markers.
526
    var mz = markerClusterer.getMaxZoom_();
699
 *
-
 
700
 * @param {MarkerClusterer} markerClusterer The markerclusterer that this
-
 
701
 *     cluster is associated with.
-
 
702
 * @constructor
-
 
703
 * @ignore
-
 
704
 */
-
 
705
function Cluster(markerClusterer) {
527
    if (mz === null) {
706
  this.markerClusterer_ = markerClusterer;
-
 
707
  this.map_ = markerClusterer.getMap();
-
 
708
  this.gridSize_ = markerClusterer.getGridSize();
528
      mz = map_.getCurrentMapType().getMaximumResolution();
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
}
529
    }
716
 
530
    if (zoom_ >= mz || this.getTotalMarkers() === 1) {
717
/**
531
 
-
 
532
      // If current zoom level is beyond the max zoom level or the cluster
-
 
533
      // have only one marker, the marker(s) in cluster will be showed on map.
-
 
534
      for (i = 0; i < markers_.length; ++i) {
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) {
535
        if (markers_[i].isAdded) {
729
        return true;
-
 
730
      }
536
          if (markers_[i].marker.isHidden()) {
731
    }
-
 
732
  }
-
 
733
  return false;
-
 
734
};
-
 
735
 
537
            markers_[i].marker.show();
736
 
Line -... Line 737...
-
 
737
/**
538
          }
738
 * Add a marker the cluster.
539
        } else {
739
 *
-
 
740
 * @param {google.maps.Marker} marker The marker to add.
540
          map_.addOverlay(markers_[i].marker);
741
 * @return {boolean} True if the marker was added.
541
          markers_[i].isAdded = true;
742
 */
542
        }
743
Cluster.prototype.addMarker = function(marker) {
543
      }
744
  if (this.isMarkerAlreadyAdded(marker)) {
544
      if (clusterMarker_ !== null) {
745
    return false;
545
        clusterMarker_.hide();
746
  }
Line 546... Line 747...
546
      }
747
 
-
 
748
  if (!this.center_) {
-
 
749
    this.center_ = marker.getPosition();
547
    } else {
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
  }
548
      // Else add a cluster marker on map to show the number of markers in
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_;
549
      // this cluster.
800
};
550
      for (i = 0; i < markers_.length; ++i) {
-
 
551
        if (markers_[i].isAdded && (!markers_[i].marker.isHidden())) {
801
 
552
          markers_[i].marker.hide();
-
 
553
        }
-
 
554
      }
-
 
555
      if (clusterMarker_ === null) {
-
 
556
        clusterMarker_ = new ClusterMarker_(center_, this.getTotalMarkers(), markerClusterer_.getStyles_(), markerClusterer_.getGridSize_());
-
 
557
        map_.addOverlay(clusterMarker_);
-
 
558
      } else {
-
 
559
        if (clusterMarker_.isHidden()) {
-
 
560
          clusterMarker_.show();
-
 
561
        }
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.
562
        clusterMarker_.redraw(true);
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
 *
563
      }
826
 * @return {Array.<google.maps.Marker>} The cluster center.
-
 
827
 */
-
 
828
Cluster.prototype.getMarkers = function() {
-
 
829
  return this.markers_;
-
 
830
};
-
 
831
 
-
 
832
 
-
 
833
/**
564
    }
834
 * Returns the center of the cluster.
-
 
835
 *
-
 
836
 * @return {google.maps.LatLng} The cluster center.
565
  };
837
 */
-
 
838
Cluster.prototype.getCenter = function() {
-
 
839
  return this.center_;
-
 
840
};
566
 
841
 
-
 
842
 
567
  /**
843
/**
568
   * Remove all the markers from this cluster.
844
 * Calculated the bounds of the cluster with the grid.
Line 569... Line 845...
569
   */
845
 *
-
 
846
 * @private
570
  this.clearMarkers = function () {
847
 */
-
 
848
Cluster.prototype.calculateBounds_ = function() {
571
    if (clusterMarker_ !== null) {
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
 
572
      map_.removeOverlay(clusterMarker_);
864
 
573
    }
865
/**
574
    for (var i = 0; i < markers_.length; ++i) {
866
 * Returns the map that the cluster is associated with.
-
 
867
 *
575
      if (markers_[i].isAdded) {
868
 * @return {google.maps.Map} The map.
-
 
869
 */
576
        map_.removeOverlay(markers_[i].marker);
870
Cluster.prototype.getMap = function() {
-
 
871
  return this.map_;
-
 
872
};
577
      }
873
 
578
    }
874
 
-
 
875
/**
-
 
876
 * Updates the cluster icon
-
 
877
 */
-
 
878
Cluster.prototype.updateIcon = function() {
579
    markers_ = [];
879
  var zoom = this.map_.getZoom();
-
 
880
  var mz = this.markerClusterer_.getMaxZoom();
-
 
881
 
580
  };
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_);
581
 
886
      marker.setVisible(true);
-
 
887
    }
-
 
888
    return;
582
  /**
889
  }
Line 583... Line -...
583
   * Get number of markers.
-
 
Line 584... Line 890...
584
   * @return {Number}
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;
585
   */
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
}
586
  this.getTotalMarkers = function () {
936
 
587
    return markers_.length;
937
 
588
  };
938
/**
589
}
-
 
590
 
-
 
591
/**
-
 
592
 * ClusterMarker_ creates a marker that shows the number of markers that
939
 * Triggers the clusterclick event and zoom's if the option is set.
593
 * a cluster contains.
940
 */
594
 *
941
ClusterIcon.prototype.triggerClusterClick = function() {
595
 * @constructor
942
  var markerClusterer = this.cluster_.getMarkerClusterer();
-
 
943
 
-
 
944
  // Trigger the clusterclick event.
-
 
945
  google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_);
-
 
946
 
596
 * @private
947
  if (markerClusterer.isZoomOnClick()) {
597
 * @param {GLatLng} latlng Marker's lat and lng.
-
 
598
 * @param {Number} count Number to show.
948
    // Center the map on this cluster.
599
 * @param {Array of Object} styles The image list to be showed:
-
 
600
 *   {String} url Image url.
949
    this.map_.panTo(this.cluster_.getCenter());
601
 *   {Number} height Image height.
950
 
602
 *   {Number} width Image width.
-
 
603
 *   {Array of Number} anchor Text anchor of image left and top.
-
 
604
 *   {String} textColor text color.
951
    // Zoom into the cluster.
605
 * @param {Number} padding Padding of marker center.
952
    this.map_.fitBounds(this.cluster_.getBounds());
606
 */
-
 
607
function ClusterMarker_(latlng, count, styles, padding) {
-
 
608
  var index = 0;
953
  }
609
  var dv = count;
-
 
610
  while (dv !== 0) {
954
};
611
    dv = parseInt(dv / 10, 10);
955
 
-
 
956
 
-
 
957
/**
-
 
958
 * Adding the cluster icon to the dom.
-
 
959
 * @ignore
612
    index ++;
960
 */
-
 
961
ClusterIcon.prototype.onAdd = function() {
613
  }
962
  this.div_ = document.createElement('DIV');
-
 
963
  if (this.visible_) {
614
 
964
    var pos = this.getPosFromLatLng_(this.center_);
615
  if (styles.length < index) {
965
    this.div_.style.cssText = this.createCss(pos);
616
    index = styles.length;
966
    this.div_.innerHTML = this.sums_.text;
-
 
967
  }
Line 617... Line -...
617
  }
-
 
618
  this.url_ = styles[index - 1].url;
-
 
619
  this.height_ = styles[index - 1].height;
-
 
-
 
968
 
-
 
969
  var panes = this.getPanes();
620
  this.width_ = styles[index - 1].width;
970
  panes.overlayImage.appendChild(this.div_);
-
 
971
 
621
  this.textColor_ = styles[index - 1].opt_textColor;
972
  var that = this;
622
  this.anchor_ = styles[index - 1].opt_anchor;
973
  google.maps.event.addDomListener(this.div_, 'click', function() {
623
  this.latlng_ = latlng;
-
 
624
  this.index_ = index;
974
    that.triggerClusterClick();
625
  this.styles_ = styles;
975
  });
626
  this.text_ = count;
-
 
627
  this.padding_ = padding;
-
 
628
}
-
 
629
 
-
 
630
ClusterMarker_.prototype = new GOverlay();
976
};
631
 
977
 
632
/**
978
 
633
 * Initialize cluster marker.
979
/**
Line -... Line 980...
-
 
980
 * Returns the position to place the div dending on the latlng.
634
 * @private
981
 *
635
 */
982
 * @param {google.maps.LatLng} latlng The position in latlng.
636
ClusterMarker_.prototype.initialize = function (map) {
-
 
637
  this.map_ = map;
983
 * @return {google.maps.Point} The position in pixels.
638
  var div = document.createElement("div");
984
 * @private
639
  var latlng = this.latlng_;
985
 */
640
  var pos = map.fromLatLngToDivPixel(latlng);
986
ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) {
Line -... Line 987...
-
 
987
  var pos = this.getProjection().fromLatLngToDivPixel(latlng);
641
  pos.x -= parseInt(this.width_ / 2, 10);
988
  pos.x -= parseInt(this.width_ / 2, 10);
642
  pos.y -= parseInt(this.height_ / 2, 10);
989
  pos.y -= parseInt(this.height_ / 2, 10);
643
  var mstyle = "";
990
  return pos;
644
  if (document.all) {
991
};
645
    mstyle = 'filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="' + this.url_ + '");';
992
 
-
 
993
 
-
 
994
/**
646
  } else {
995
 * Draw the icon.
-
 
996
 * @ignore
-
 
997
 */
647
    mstyle = "background:url(" + this.url_ + ");";
998
ClusterIcon.prototype.draw = function() {
Line -... Line 999...
-
 
999
  if (this.visible_) {
648
  }
1000
    var pos = this.getPosFromLatLng_(this.center_);
649
  if (typeof this.anchor_ === "object") {
1001
    this.div_.style.top = pos.y + 'px';
650
    if (typeof this.anchor_[0] === "number" && this.anchor_[0] > 0 && this.anchor_[0] < this.height_) {
1002
    this.div_.style.left = pos.x + 'px';
-
 
1003
  }
-
 
1004
};
-
 
1005
 
651
      mstyle += 'height:' + (this.height_ - this.anchor_[0]) + 'px;padding-top:' + this.anchor_[0] + 'px;';
1006
 
652
    } else {
1007
/**
-
 
1008
 * Hide the icon.
-
 
1009
 */
-
 
1010
ClusterIcon.prototype.hide = function() {
653
      mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
1011
  if (this.div_) {
654
    }
1012
    this.div_.style.display = 'none';
655
    if (typeof this.anchor_[1] === "number" && this.anchor_[1] > 0 && this.anchor_[1] < this.width_) {
1013
  }
656
      mstyle += 'width:' + (this.width_ - this.anchor_[1]) + 'px;padding-left:' + this.anchor_[1] + 'px;';
-
 
657
    } else {
-
 
658
      mstyle += 'width:' + this.width_ + 'px;text-align:center;';
-
 
659
    }
-
 
-
 
1014
  this.visible_ = false;
660
  } else {
1015
};
661
    mstyle += 'height:' + this.height_ + 'px;line-height:' + this.height_ + 'px;';
1016
 
Line -... Line 1017...
-
 
1017
 
662
    mstyle += 'width:' + this.width_ + 'px;text-align:center;';
1018
/**
663
  }
1019
 * Position and show the icon.
664
  var txtColor = this.textColor_ ? this.textColor_ : 'black';
1020
 */
665
 
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
};
666
  div.style.cssText = mstyle + 'cursor:pointer;top:' + pos.y + "px;left:" +
1029
 
-
 
1030
 
-
 
1031
/**
667
      pos.x + "px;color:" + txtColor +  ";position:absolute;font-size:11px;" +
1032
 * Remove the icon from the map
Line -... Line 1033...
-
 
1033
 */
668
      'font-family:Arial,sans-serif;font-weight:bold';
1034
ClusterIcon.prototype.remove = function() {
669
  div.innerHTML = this.text_;
1035
  this.setMap(null);
-
 
1036
};
-
 
1037
 
670
  map.getPane(G_MAP_MAP_PANE).appendChild(div);
1038
 
671
  var padding = this.padding_;
1039
/**
672
  GEvent.addDomListener(div, "click", function () {
1040
 * Implementation of the onRemove interface.
673
    var pos = map.fromLatLngToDivPixel(latlng);
1041
 * @ignore
Line -... Line 1042...
-
 
1042
 */
674
    var sw = new GPoint(pos.x - padding, pos.y + padding);
1043
ClusterIcon.prototype.onRemove = function() {
675
    sw = map.fromDivPixelToLatLng(sw);
1044
  if (this.div_ && this.div_.parentNode) {
-
 
1045
    this.hide();
-
 
1046
    this.div_.parentNode.removeChild(this.div_);
676
    var ne = new GPoint(pos.x + padding, pos.y - padding);
1047
    this.div_ = null;
677
    ne = map.fromDivPixelToLatLng(ne);
1048
  }
678
    var zoom = map.getBoundsZoomLevel(new GLatLngBounds(sw, ne), map.getSize());
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
 
679
    map.setCenter(latlng, zoom);
1087
 
680
  });
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;');