/**
 * @name MarkerClusterer
 * @version 1.0
 * @author Xiaoxi Wu
 * @copyright (c) 2009 Xiaoxi Wu
 */

/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

function MarkerClusterer(map,opt_markers,opt_opts){
var clusters_=[]
var map_=map
var maxZoom_=null
var me_=this
var gridSize_=60
var sizes=[53,56,66,78,90]
var styles_=[]
var leftMarkers_=[]
var mcfn_=null
var i=0
for(i=1;i<=5;++i){
styles_.push({
'url': "m"+i+".png",
'height': sizes[i-1],
'width': sizes[i-1]
})}
if(typeof opt_opts==="object"&&opt_opts !==null){
if(typeof opt_opts.gridSize==="number"&&opt_opts.gridSize>0){
gridSize_=opt_opts.gridSize}
if(typeof opt_opts.maxZoom==="number"){
maxZoom_=opt_opts.maxZoom}
if(typeof opt_opts.styles==="object"&&opt_opts.styles !==null&&opt_opts.styles.length !==0){
styles_=opt_opts.styles}}
function addLeftMarkers_(){
if(leftMarkers_.length===0){
return}
var leftMarkers=[]
for(i=0;i<leftMarkers_.length;++i){
me_.addMarker(leftMarkers_[i],true,null,null,true)}
leftMarkers_=leftMarkers}
this.getStyles_=function(){
return styles_}
this.clearMarkers=function(){
for(var i=0;i<clusters_.length;++i){
if(typeof clusters_[i] !=="undefined"&&clusters_[i] !==null){
clusters_[i].clearMarkers()}}
clusters_=[]
leftMarkers_=[]
GEvent.removeListener(mcfn_)}
function isMarkerInViewport_(marker){
return map_.getBounds().containsLatLng(marker.getLatLng())}
function reAddMarkers_(markers){
var len=markers.length
var clusters=[]
for(var i=len-1;i>=0;--i){
me_.addMarker(markers[i].marker,true,markers[i].isAdded,clusters,true)}
addLeftMarkers_()}
this.addMarker=function(marker,opt_isNodraw,opt_isAdded,opt_clusters,opt_isNoCheck){
opt_isNoCheck=true
if(opt_isNoCheck !==true){
if(!isMarkerInViewport_(marker)){
leftMarkers_.push(marker)
return}}
var isAdded=opt_isAdded
var clusters=opt_clusters
var pos=map_.fromLatLngToDivPixel(marker.getLatLng())
if(typeof isAdded !=="boolean"){
isAdded=false}
if(typeof clusters !=="object" || clusters===null){
clusters=clusters_}
var length=clusters.length
var cluster=null
for(var i=length-1;i>=0;i--){
cluster=clusters[i]
var center=cluster.getCenter()
if(center===null){
continue}
center=map_.fromLatLngToDivPixel(center)
if(pos.x>=center.x-gridSize_&&pos.x<=center.x+gridSize_&&
pos.y>=center.y-gridSize_&&pos.y<=center.y+gridSize_){
cluster.addMarker({
'isAdded': isAdded,
'marker': marker
})
if(!opt_isNodraw){
cluster.redraw_()}
return}}
cluster=new Cluster(this,map)
cluster.addMarker({
'isAdded': isAdded,
'marker': marker
})
if(!opt_isNodraw){
cluster.redraw_()}
clusters.push(cluster)
if(clusters !==clusters_){
clusters_.push(cluster)}}
this.removeMarker=function(marker){
for(var i=0;i<clusters_.length;++i){
if(clusters_[i].remove(marker)){
clusters_[i].redraw_()
return}}}
this.redraw_=function(){
var clusters=this.getClustersInViewport_()
for(var i=0;i<clusters.length;++i){
clusters[i].redraw_(true)}}
this.getClustersInViewport_=function(){
var clusters=[]
var curBounds=map_.getBounds()
for(var i=0;i<clusters_.length;i++){
if(clusters_[i].isInBounds(curBounds)){
clusters.push(clusters_[i])}}
return clusters}
this.getMaxZoom_=function(){
return maxZoom_}
this.getMap_=function(){
return map_}
this.getGridSize_=function(){
return gridSize_}
this.getTotalMarkers=function(){
var result=0
for(var i=0;i<clusters_.length;++i){
result+=clusters_[i].getTotalMarkers()}
return result}
this.getTotalClusters=function(){
return clusters_.length}
this.resetViewport=function(){
var clusters=this.getClustersInViewport_()
var tmpMarkers=[]
var removed=0
for(var i=0;i<clusters.length;++i){
var cluster=clusters[i]
var oldZoom=cluster.getCurrentZoom()
if(oldZoom===null){
continue}
var curZoom=map_.getZoom()
if(curZoom !==oldZoom){
var mks=cluster.getMarkers()
for(var j=0;j<mks.length;++j){
var newMarker={
'isAdded': false,
'marker': mks[j].marker}
tmpMarkers.push(newMarker)}
cluster.clearMarkers()
removed++
for(j=0;j<clusters_.length;++j){
if(cluster===clusters_[j]){
clusters_.splice(j,1)}}}}
reAddMarkers_(tmpMarkers)
this.redraw_()}
this.addMarkers=function(markers){
for(var i=0;i<markers.length;++i){
this.addMarker(markers[i],true)}
this.redraw_()}
if(typeof opt_markers==="object"&&opt_markers !==null){
this.addMarkers(opt_markers)}
mcfn_=GEvent.addListener(map_,"moveend",function(){
me_.resetViewport()
})}
function Cluster(markerClusterer){
var center_=null
var markers_=[]
var markerClusterer_=markerClusterer
var map_=markerClusterer.getMap_()
var clusterMarker_=null
var zoom_=map_.getZoom()
this.getMarkers=function(){
return markers_}
this.isInBounds=function(bounds){
if(center_===null){
return false}
if(!bounds){
bounds=map_.getBounds()}
var sw=map_.fromLatLngToDivPixel(bounds.getSouthWest())
var ne=map_.fromLatLngToDivPixel(bounds.getNorthEast())
var centerxy=map_.fromLatLngToDivPixel(center_)
var inViewport=true
var gridSize=markerClusterer.getGridSize_()
if(zoom_ !==map_.getZoom()){
var dl=map_.getZoom()-zoom_
gridSize=Math.pow(2,dl)*gridSize}
if(ne.x !==sw.x&&(centerxy.x+gridSize<sw.x || centerxy.x-gridSize>ne.x)){
inViewport=false}
if(inViewport&&(centerxy.y+gridSize<ne.y || centerxy.y-gridSize>sw.y)){
inViewport=false}
return inViewport}
this.getCenter=function(){
return center_}
this.addMarker=function(marker){
if(center_===null){
center_=marker.marker.getLatLng()}
markers_.push(marker)}
this.removeMarker=function(marker){
for(var i=0;i<markers_.length;++i){
if(marker===markers_[i].marker){
if(markers_[i].isAdded){
map_.removeOverlay(markers_[i].marker)}
markers_.splice(i,1)
return true}}
return false}
this.getCurrentZoom=function(){
return zoom_}
this.redraw_=function(isForce){
if(!isForce&&!this.isInBounds()){
return}
zoom_=map_.getZoom()
var i=0
var mz=markerClusterer.getMaxZoom_()
if(mz===null){
mz=map_.getCurrentMapType().getMaximumResolution()}
if(zoom_>=mz || this.getTotalMarkers()===1){
for(i=0;i<markers_.length;++i){
if(markers_[i].isAdded){
if(markers_[i].marker.isHidden()){
markers_[i].marker.show()}
}else{
map_.addOverlay(markers_[i].marker)
markers_[i].isAdded=true}}
if(clusterMarker_ !==null){
clusterMarker_.hide()}
}else{
for(i=0;i<markers_.length;++i){
if(markers_[i].isAdded&&(!markers_[i].marker.isHidden())){
markers_[i].marker.hide()}}
if(clusterMarker_===null){
clusterMarker_=new ClusterMarker_(center_,this.getTotalMarkers(),markerClusterer_.getStyles_(),markerClusterer_.getGridSize_())
map_.addOverlay(clusterMarker_)
}else{
if(clusterMarker_.isHidden()){
clusterMarker_.show()}
clusterMarker_.redraw(true)}}}
this.clearMarkers=function(){
if(clusterMarker_ !==null){
map_.removeOverlay(clusterMarker_)}
for(var i=0;i<markers_.length;++i){
if(markers_[i].isAdded){
map_.removeOverlay(markers_[i].marker)}}
markers_=[]}
this.getTotalMarkers=function(){
return markers_.length}}
function ClusterMarker_(latlng,count,styles,padding){
var index=0
var dv=count
while(dv !==0){
dv=parseInt(dv/10,10)
index++}
if(styles.length<index){
index=styles.length}
this.url_=styles[index-1].url
this.height_=styles[index-1].height
this.width_=styles[index-1].width
this.textColor_=styles[index-1].opt_textColor
this.anchor_=styles[index-1].opt_anchor
this.latlng_=latlng
this.index_=index
this.styles_=styles
this.text_=count
this.padding_=padding}
ClusterMarker_.prototype=new GOverlay()
ClusterMarker_.prototype.initialize=function(map){
this.map_=map
var div=document.createElement("div")
var latlng=this.latlng_
var pos=map.fromLatLngToDivPixel(latlng)
pos.x-=parseInt(this.width_/2,10)
pos.y-=parseInt(this.height_/2,10)
var mstyle=""
if(document.all){
mstyle='filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod=scale,src="'+this.url_+'");'
}else{
mstyle="background:url("+this.url_+");"}
if(typeof this.anchor_==="object"){
if(typeof this.anchor_[0]==="number"&&this.anchor_[0]>0&&this.anchor_[0]<this.height_){
mstyle+='height:'+(this.height_-this.anchor_[0])+'px;padding-top:'+this.anchor_[0]+'px;'
}else{
mstyle+='height:'+this.height_+'px;line-height:'+this.height_+'px;'}
if(typeof this.anchor_[1]==="number"&&this.anchor_[1]>0&&this.anchor_[1]<this.width_){
mstyle+='width:'+(this.width_-this.anchor_[1])+'px;padding-left:'+this.anchor_[1]+'px;'
}else{
mstyle+='width:'+this.width_+'px;text-align:center;'}
}else{
mstyle+='height:'+this.height_+'px;line-height:'+this.height_+'px;'
mstyle+='width:'+this.width_+'px;text-align:center;'}
var txtColor=this.textColor_ ? this.textColor_ : 'white'
div.style.cssText=mstyle+'cursor:pointer;top:'+pos.y+"px;left:"+
pos.x+"px;color:"+txtColor+";position:absolute;font-size:11px;"+
'font-family:Arial,sans-serif;font-weight:bold'
div.innerHTML=this.text_
map.getPane(G_MAP_MAP_PANE).appendChild(div)
var padding=this.padding_
GEvent.addDomListener(div,"click",function(){
var pos=map.fromLatLngToDivPixel(latlng)
var sw=new GPoint(pos.x-padding,pos.y+padding)
sw=map.fromDivPixelToLatLng(sw)
var ne=new GPoint(pos.x+padding,pos.y-padding)
ne=map.fromDivPixelToLatLng(ne)
var zoom=map.getBoundsZoomLevel(new GLatLngBounds(sw,ne),map.getSize())
map.setCenter(latlng,zoom)
})
this.div_=div}
ClusterMarker_.prototype.remove=function(){
this.div_.parentNode.removeChild(this.div_)}
ClusterMarker_.prototype.copy=function(){
return new ClusterMarker_(this.latlng_,this.index_,this.text_,this.styles_,this.padding_)}
ClusterMarker_.prototype.redraw=function(force){
if(!force){
return}
var pos=this.map_.fromLatLngToDivPixel(this.latlng_)
pos.x-=parseInt(this.width_/2,10)
pos.y-=parseInt(this.height_/2,10)
this.div_.style.top=pos.y+"px"
this.div_.style.left=pos.x+"px"}
ClusterMarker_.prototype.hide=function(){
this.div_.style.display="none"}
ClusterMarker_.prototype.show=function(){
this.div_.style.display=""}
ClusterMarker_.prototype.isHidden=function(){
return this.div_.style.display==="none"}

