/src/com/google/maps/extras/markerclusterer/Cluster.as

http://gmaps-utility-library-flash.googlecode.com/ · ActionScript · 304 lines · 138 code · 22 blank · 144 comment · 31 complexity · 415ebc226c6d3a98545f30ed191cbebd MD5 · raw file

  1. /**
  2. * @name MarkerClusterer for Flash
  3. * @version 1.0
  4. * @author Xiaoxi Wu
  5. * @copyright (c) 2009 Xiaoxi Wu
  6. * @fileoverview
  7. * Ported from Javascript to Actionscript 3 by Sean Toru
  8. * Ported for use in Flex (removal of fl. libraries) by Ian Watkins
  9. * Reflectored for both Flash and Flex,
  10. * and maintained by Juguang XIAO (juguang@gmail.com)
  11. *
  12. * This actionscript library creates and manages per-zoom-level
  13. * clusters for large amounts of markers (hundreds or thousands).
  14. * This library was inspired by the <a href="http://www.maptimize.com">
  15. * Maptimize</a> hosted clustering solution.
  16. * <br /><br/>
  17. * <b>How it works</b>:<br/>
  18. * The <code>MarkerClusterer</code> will group markers into clusters according to
  19. * their distance from a cluster's center. When a marker is added,
  20. * the marker cluster will find a position in all the clusters, and
  21. * if it fails to find one, it will create a new cluster with the marker.
  22. * The number of markers in a cluster will be displayed
  23. * on the cluster marker. When the map viewport changes,
  24. * <code>MarkerClusterer</code> will destroy the clusters in the viewport
  25. * and regroup them into new clusters.
  26. *
  27. */
  28. /*
  29. * Licensed under the Apache License, Version 2.0 (the "License");
  30. * you may not use this file except in compliance with the License.
  31. * You may obtain a copy of the License at
  32. *
  33. * http://www.apache.org/licenses/LICENSE-2.0
  34. *
  35. * Unless required by applicable law or agreed to in writing, software
  36. * distributed under the License is distributed on an "AS IS" BASIS,
  37. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  38. * See the License for the specific language governing permissions and
  39. * limitations under the License.
  40. */
  41. package com.google.maps.extras.markerclusterer
  42. {
  43. import com.google.maps.LatLng;
  44. import com.google.maps.LatLngBounds;
  45. import com.google.maps.interfaces.IMap;
  46. import com.google.maps.interfaces.IPane;
  47. import com.google.maps.overlays.Marker;
  48. import flash.geom.Point;
  49. import flash.geom.Rectangle;
  50. /**
  51. * class Cluster is a structure to manage markers which fall into a single cluster.
  52. *
  53. */
  54. internal class Cluster
  55. {
  56. private var center_ : LatLng;
  57. private var markers_ : Array;
  58. private var markerClusterer_ : MarkerClusterer;
  59. // private var map_ : IMap;
  60. private var clusterMarker_ : ClusterMarker;
  61. private var zoom_ : Number;
  62. private var _pane:IPane ;
  63. /**
  64. * class Cluster is a structure to manage markers which fall into this cluster.
  65. *
  66. */
  67. public function Cluster (markerClusterer : MarkerClusterer, pane:IPane)
  68. {
  69. center_ = null;
  70. markers_ = new Array();
  71. markerClusterer_ = markerClusterer;
  72. // map_ = markerClusterer.map;
  73. clusterMarker_ = null;
  74. zoom_ = markerClusterer_.zoom; // map_.getZoom();
  75. this._pane = pane;
  76. }
  77. /**
  78. * @return an array of UnitMarker instance.
  79. */
  80. public function getMarkers () : Array
  81. {
  82. return markers_;
  83. }
  84. /**
  85. * Check whether the cluster is in the rectangle in pixels
  86. */
  87. public function isInRectangle(bounds:Rectangle= null):Boolean{
  88. if(bounds == null){
  89. bounds = this._pane.getViewportBounds();
  90. }
  91. var centerxy:Point = this._pane.fromLatLngToPaneCoords(center_);
  92. var gridSize:Number = markerClusterer_.gridSize;
  93. if (zoom_ != _pane.map.getZoom()) // map_.getZoom())
  94. {
  95. var dl:Number = this.markerClusterer_.zoom - zoom_;
  96. gridSize = Math.pow(2, dl) * gridSize;
  97. }
  98. if(centerxy.x + gridSize < bounds.left || centerxy.x - gridSize > bounds.right){
  99. return false;
  100. }
  101. if(centerxy.y + gridSize < bounds.top || centerxy.y - gridSize > bounds.bottom){
  102. return false;
  103. }
  104. return true;
  105. }
  106. /**
  107. * Check whether this cluster is in bounds in LatLngBounds
  108. *
  109. * This method is considered to be replaced with isInRectangle.
  110. * So you may not see it in the later version.
  111. */
  112. /*
  113. public function isInBounds (bounds : LatLngBounds = undefined) : Boolean
  114. {
  115. if (center_ == null) {
  116. return false;
  117. }
  118. if (!bounds) {
  119. bounds = map_.getLatLngBounds();
  120. }
  121. var sw : Point = map_.fromLatLngToViewport(bounds.getSouthWest());
  122. var ne : Point = map_.fromLatLngToViewport(bounds.getNorthEast());
  123. var centerxy : Point = map_.fromLatLngToViewport(center_);
  124. var inViewport :Boolean = true;
  125. var gridSize : Number = markerClusterer_.gridSize;
  126. if (zoom_ != map_.getZoom())
  127. {
  128. var dl : Number = map_.getZoom() - zoom_;
  129. gridSize = Math.pow(2, dl) * gridSize;
  130. }
  131. if (ne.x != sw.x && (centerxy.x + gridSize < sw.x || centerxy.x - gridSize > ne.x))
  132. {
  133. inViewport = false;
  134. }
  135. if (inViewport && (centerxy.y + gridSize < ne.y || centerxy.y - gridSize > sw.y))
  136. {
  137. inViewport = false;
  138. }
  139. return inViewport;
  140. }
  141. */
  142. /* used in MarkerClusterer addMarker */
  143. public function getCenter () : LatLng
  144. {
  145. return center_;
  146. }
  147. public function addMarker (marker : UnitMarker) : void
  148. {
  149. if (center_ == null)
  150. {
  151. center_ = marker.getLatLng();
  152. }
  153. markers_.push(marker);
  154. }
  155. /* private function removeMarker (marker : Marker) : Boolean
  156. {
  157. for (var i:int = 0; i < markers_.length; ++i)
  158. {
  159. if (marker == markers_[i].marker)
  160. {
  161. if (markers_[i].isAdded)
  162. {
  163. this._pane.removeOverlay(markers_[i].marker);
  164. // map_.removeOverlay(markers_[i].marker);
  165. }
  166. markers_.splice(i, 1);
  167. return true;
  168. }
  169. }
  170. return false;
  171. } */
  172. public function getCurrentZoom () : Number
  173. {
  174. return zoom_;
  175. }
  176. /**
  177. * This function causes to redraw the cluster.
  178. * Currently, there are three (3) places to call this function:
  179. * - 2 places in MarkerClusterer.addMarker
  180. * - 1 place in MarkerClusterer.redraw
  181. *
  182. * @param isForced - current not in used
  183. */
  184. public function redraw (isForce : Boolean) : void
  185. {
  186. if (!isForce && ! this.isInRectangle()
  187. //this.isInBounds()
  188. ) {
  189. return;
  190. }
  191. // Set cluster zoom level.
  192. zoom_ = this.markerClusterer_.zoom; // map_.getZoom();
  193. var mz : Number = markerClusterer_.maxZoom;
  194. if (isNaN(mz)) {
  195. mz = this.markerClusterer_.maximumResolution; // map_.getCurrentMapType().getMaximumResolution();
  196. }
  197. var marker:UnitMarker;
  198. if (zoom_ >= mz || this.getTotalMarkers() == 1)
  199. {
  200. // If current zoom level is beyond the max zoom level or the cluster
  201. // have only one marker, the marker(s) in cluster will be showed on map.
  202. for each(marker in markers_)
  203. {
  204. if (marker.isAdded)
  205. {
  206. if(!marker.visible ) marker.visible = true;
  207. }
  208. else
  209. {
  210. this._pane.addOverlay(marker);
  211. // map_.addOverlay(markers_[i]);
  212. marker.isAdded = true;
  213. }
  214. }
  215. if (clusterMarker_ != null)
  216. {
  217. // was: clusterMarker_.hide();
  218. clusterMarker_.visible = false;
  219. }
  220. }
  221. else
  222. {
  223. // Else add a cluster marker on map to show the number of markers in
  224. // this cluster.
  225. for each(marker in markers_)
  226. {
  227. if (marker.isAdded && marker.visible)
  228. {
  229. marker.visible = false;
  230. }
  231. }
  232. if (clusterMarker_ == null)
  233. {
  234. clusterMarker_ = new ClusterMarker(center_, this.getTotalMarkers(),
  235. markerClusterer_.getStyles(), markerClusterer_.gridSize);
  236. // clusterMarker_.initialise(map_);
  237. // map_.addOverlay(clusterMarker_);
  238. this._pane.addOverlay(clusterMarker_);
  239. }
  240. else
  241. {
  242. /* was :
  243. if (clusterMarker_.isHidden())
  244. {
  245. clusterMarker_.show();
  246. }
  247. */
  248. if(!clusterMarker_.visible)
  249. clusterMarker_.visible = true;
  250. clusterMarker_.redraw(true);
  251. }
  252. }
  253. }
  254. public function clearMarkers () : void
  255. {
  256. if (clusterMarker_ != null){
  257. this._pane.removeOverlay(clusterMarker_);
  258. clusterMarker_ = null;
  259. // map_.removeOverlay(clusterMarker_);
  260. }
  261. for (var i:int = 0; i < markers_.length; ++i) {
  262. if (markers_[i].isAdded){
  263. markers_[i].isAdded = false;
  264. // was: map_.removeOverlay(markers_[i]);
  265. this._pane.removeOverlay(markers_[i]);
  266. }
  267. }
  268. markers_ = new Array();
  269. }
  270. internal function getTotalMarkers () : int{
  271. return markers_.length;
  272. }
  273. }
  274. }