Clusterer=function(map)
{this.map=map;
this.markers=[];
this.clusters=[];
this.timeout=null;
this.currentZoomLevel=map.getZoom();
this.maxVisibleMarkers=Clusterer.defaultMaxVisibleMarkers;
this.gridSize=Clusterer.defaultGridSize;
this.minMarkersPerCluster=Clusterer.defaultMinMarkersPerCluster;
this.maxLinesPerInfoBox=Clusterer.defaultMaxLinesPerInfoBox;
this.icon=Clusterer.defaultIcon;
	GEvent.addListener(map,'zoomend',Clusterer.MakeCaller(Clusterer.Display,this));
	GEvent.addListener(map,'moveend',Clusterer.MakeCaller(Clusterer.Display,this));
	GEvent.addListener(map,'infowindowclose',Clusterer.MakeCaller(Clusterer.PopDown,this));
};
Clusterer.defaultMaxVisibleMarkers=6;
Clusterer.defaultGridSize=3;
Clusterer.defaultMinMarkersPerCluster=2;
Clusterer.defaultMaxLinesPerInfoBox=10;
Clusterer.defaultIcon=new GIcon();
Clusterer.defaultIcon.image='/images/maps/icon_default_group.png';
Clusterer.defaultIcon.iconSize=new GSize(30, 34);
Clusterer.defaultIcon.iconAnchor=new GPoint(12, 34);
Clusterer.defaultIcon.infoWindowAnchor=new GPoint(12, 0);
Clusterer.prototype.SetIcon=function(icon)
{this.icon=icon;
};
Clusterer.prototype.SetMaxVisibleMarkers=function(n)
{this.maxVisibleMarkers=n;
};
Clusterer.prototype.SetMinMarkersPerCluster=function(n)
{this.minMarkersPerCluster=n;
};
Clusterer.prototype.SetMaxLinesPerInfoBox=function(n)
{this.maxLinesPerInfoBox=n;
};
Clusterer.prototype.AddMarker=function(marker,title)
{if(marker.setMap!=null)
marker.setMap(this.map);
marker.title=title;
marker.onMap=false;

this.markers.push(marker);

this.DisplayLater();
};
Clusterer.prototype.RemoveMarker=function(marker)
{for(var i=0;i<this.markers.length;++i)
if(this.markers[i]==marker)
{if(marker.onMap)
this.map.removeOverlay(marker);
for(var j=0;j<this.clusters.length;++j)
{var cluster=this.clusters[j];
if(cluster!=null)
{for(var k=0;k<cluster.markers.length;++k)
if(cluster.markers[k]==marker)
{cluster.markers[k]=null;
--cluster.markerCount;
break;
}
if(cluster.markerCount==0)
{this.ClearCluster(cluster);

this.clusters[j]=null;
}
else if(cluster==this.poppedUpCluster)
Clusterer.RePop(this);
}}
this.markers[i]=null;
break;
}
this.DisplayLater();
};
Clusterer.prototype.DisplayLater=function()
{if(this.timeout!=null)
clearTimeout(this.timeout);

this.timeout=setTimeout(Clusterer.MakeCaller(Clusterer.Display,this),50);
};
Clusterer.Display=function(clusterer)
{var i,j,marker,cluster;
clearTimeout(clusterer.timeout);
var newZoomLevel=clusterer.map.getZoom();
if(newZoomLevel!=clusterer.currentZoomLevel)
{for(i=0;i<clusterer.clusters.length;++i)
if(clusterer.clusters[i]!=null)
{clusterer.ClearCluster(clusterer.clusters[i]);
clusterer.clusters[i]=null;
}
clusterer.clusters.length=0;
clusterer.currentZoomLevel=newZoomLevel;
}
var bounds=clusterer.map.getBounds();
var sw=bounds.getSouthWest();
var ne=bounds.getNorthEast();
var dx=ne.lng()-sw.lng();
var dy=ne.lat()-sw.lat();
if(dx<300&&dy<150)
{dx*=0.10;
dy*=0.10;
bounds=new GLatLngBounds(new GLatLng(sw.lat()-dy,sw.lng()-dx),new GLatLng(ne.lat()+dy,ne.lng()+dx));
}
var visibleMarkers=[];
var nonvisibleMarkers=[];
for(i=0;i<clusterer.markers.length;++i)
{marker=clusterer.markers[i];
if(marker!=null)
if(bounds.contains(marker.getPoint()))
visibleMarkers.push(marker);
else
nonvisibleMarkers.push(marker);
}
for(i=0;i<nonvisibleMarkers.length;++i)
{marker=nonvisibleMarkers[i];
if(marker.onMap)
{clusterer.map.removeOverlay(marker);
marker.onMap=false;
}}
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null&&!bounds.contains(cluster.marker.getPoint())&&cluster.onMap)
{clusterer.map.removeOverlay(cluster.marker);
cluster.onMap=false;
}}
if(visibleMarkers.length>clusterer.maxVisibleMarkers)
{var latRange=bounds.getNorthEast().lat()-bounds.getSouthWest().lat();
var latInc=latRange/clusterer.gridSize;
var lngInc=latInc/Math.cos((bounds.getNorthEast().lat()+bounds.getSouthWest().lat())/2.0*Math.PI/180.0);
for(var lat=bounds.getSouthWest().lat();lat<=bounds.getNorthEast().lat();lat+=latInc)
for(var lng=bounds.getSouthWest().lng();lng<=bounds.getNorthEast().lng();lng+=lngInc)
{cluster=new Object();
cluster.clusterer=clusterer;
cluster.bounds=new GLatLngBounds(new GLatLng(lat,lng),new GLatLng(lat+latInc,lng+lngInc));
cluster.markers=[];
cluster.markerCount=0;
cluster.onMap=false;
cluster.marker=null;
clusterer.clusters.push(cluster);
}
for(i=0;i<visibleMarkers.length;++i)
{marker=visibleMarkers[i];
if(marker!=null&&!marker.inCluster)
{for(j=0;j<clusterer.clusters.length;++j)
{cluster=clusterer.clusters[j];
if(cluster!=null&&cluster.bounds.contains(marker.getPoint()))
{cluster.markers.push(marker);
++cluster.markerCount;
marker.inCluster=true;
}}}}
for(i=0;i<clusterer.clusters.length;++i)
if(clusterer.clusters[i]!=null&&clusterer.clusters[i].markerCount<clusterer.minMarkersPerCluster)
{clusterer.ClearCluster(clusterer.clusters[i]);
clusterer.clusters[i]=null;
}
for(i=clusterer.clusters.length-1;i>=0;--i)
if(clusterer.clusters[i]!=null)
break;
else
--clusterer.clusters.length;
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null)
{for(j=0;j<cluster.markers.length;++j)
{marker=cluster.markers[j];
if(marker!=null&&marker.onMap)
{clusterer.map.removeOverlay(marker);
marker.onMap=false;
}}}}
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null&&cluster.marker==null)
{var xTotal=0.0,yTotal=0.0;
for(j=0;j<cluster.markers.length;++j)
{marker=cluster.markers[j];
if(marker!=null)
{xTotal+=(+marker.getPoint().lng());
yTotal+=(+marker.getPoint().lat());
}}
var location=new GLatLng(yTotal/cluster.markerCount,xTotal/cluster.markerCount);
opts = {"icon": clusterer.icon,"labelText": cluster.markerCount,"labelOffset": new GSize(-16, -22)};

marker=new LabeledMarker(location,opts);
cluster.marker=marker;

}}}
for(i=0;i<visibleMarkers.length;++i)
{marker=visibleMarkers[i];
if(marker!=null&&!marker.onMap&&!marker.inCluster)
{clusterer.map.addOverlay(marker);
if(marker.addedToMap!=null)
marker.addedToMap();
marker.onMap=true;
}}
for(i=0;i<clusterer.clusters.length;++i)
{cluster=clusterer.clusters[i];
if(cluster!=null&&!cluster.onMap&&bounds.contains(cluster.marker.getPoint()))
{clusterer.map.addOverlay(cluster.marker);
cluster.onMap=true;
}}
Clusterer.RePop(clusterer);
};
Clusterer.PopUp=function(cluster){
	var clusterer=cluster.clusterer;
	var html='<table width="30">';
	
html+='</table>';clusterer.map.closeInfoWindow();clusterer.map.openInfoWindowHtml(cluster.marker.getLatLng(),html,{pixelOffset: new GSize(15,-30)});clusterer.poppedUpCluster=cluster;};Clusterer.RePop=function(clusterer)
{if(clusterer.poppedUpCluster!=null)
Clusterer.PopUp(clusterer.poppedUpCluster);};Clusterer.PopDown=function(clusterer)
{clusterer.poppedUpCluster=null;};Clusterer.prototype.ClearCluster=function(cluster)
{var i,marker;for(i=0;i<cluster.markers.length;++i)
if(cluster.markers[i]!=null)
{cluster.markers[i].inCluster=false;cluster.markers[i]=null;}
cluster.markers.length=0;cluster.markerCount=0;if(cluster==this.poppedUpCluster)
this.map.closeInfoWindow();if(cluster.onMap)
{this.map.removeOverlay(cluster.marker);cluster.onMap=false;}};Clusterer.MakeCaller=function(func,arg)
{return function(){func(arg);};};GMarker.prototype.setMap=function(map)
{this.map=map;};GMarker.prototype.addedToMap=function()
{this.map=null;};GMarker.prototype.origOpenInfoWindow=GMarker.prototype.openInfoWindow;GMarker.prototype.openInfoWindow=function(node,opts)
{if(this.map!=null)
return this.map.openInfoWindow(this.getPoint(),node,opts);else
return this.origOpenInfoWindow(node,opts);};GMarker.prototype.origOpenInfoWindowHtml=GMarker.prototype.openInfoWindowHtml;GMarker.prototype.openInfoWindowHtml=function(html,opts)
{if(this.map!=null)
return this.map.openInfoWindowHtml(this.getPoint(),html,opts);else
return this.origOpenInfoWindowHtml(html,opts);};GMarker.prototype.origOpenInfoWindowTabs=GMarker.prototype.openInfoWindowTabs;GMarker.prototype.openInfoWindowTabs=function(tabNodes,opts)
{if(this.map!=null)
return this.map.openInfoWindowTabs(this.getPoint(),tabNodes,opts);else
return this.origOpenInfoWindowTabs(tabNodes,opts);};GMarker.prototype.origOpenInfoWindowTabsHtml=GMarker.prototype.openInfoWindowTabsHtml;GMarker.prototype.openInfoWindowTabsHtml=function(tabHtmls,opts)
{if(this.map!=null)
return this.map.openInfoWindowTabsHtml(this.getPoint(),tabHtmls,opts);else
return this.origOpenInfoWindowTabsHtml(tabHtmls,opts);};GMarker.prototype.origShowMapBlowup=GMarker.prototype.showMapBlowup;GMarker.prototype.showMapBlowup=function(opts)
{if(this.map!=null)
return this.map.showMapBlowup(this.getPoint(),opts);else
return this.origShowMapBlowup(opts);};