function ClusterManager(_map) {
  // parameters
  var maxZoom=11;
  var zoomStep=2;
  var radius=32;
  // local vars
  var markers=new Array();
  var visibleMarkers=new Array();
  var clusters=null;
  var me=this;

  this.addMarker=function(_marker){
    markers.push(_marker);
  }

  this.load=function(){
    if(_map.getZoom()>=maxZoom) {
      visibleMarkers=markers;
      for(var i=0;i<markers.length;i++){
        map.addOverlay(markers[i]);      
      }
    } else {
      var temp=markers.slice(0);
      var m;
      while((m=temp.shift())!=null) {
        var cluster=new Cluster(m);
        for(var j=0;j<temp.length;j++){
          if(cluster.isInRange(temp[j])) {
            cluster.addMarker(temp[j]);
            temp.splice(j,1)
            j--;
          } 
        }
        if(cluster.size()>1) {
          clusters.push(cluster);
        } else {
          visibleMarkers.push(m);
        }
      }
      for(var i=0;i<visibleMarkers.length;i++){
        map.addOverlay(visibleMarkers[i]);
      }
      for(var i=0;i<clusters.length;i++){
        var m=new GMarker(clusters[i].getLatLng(), {icon:getIcon(clusters[i].size()),title:'click to zoom in'});
        GEvent.addListener(m,'click',function(){
          map.setCenter(this.getLatLng(),map.getZoom()+zoomStep);
        });
        map.addOverlay(m);
      }
    }
  }

  this.getAllMarkers=function(){
    return markers;
  }
  this.getVisibleMarkers=function(){
    return visibleMarkers;
  }
  this.getClusters=function(){
    return clusters;
  }

  this.clear=function(){
    markers=new Array();
    clusters=new Array();
    visibleMarkers=new Array();
    _map.clearOverlays();
  }

  function Cluster(_marker){
    var markers=new Array(_marker);
    var ctr=_map.fromLatLngToDivPixel(_marker.getLatLng());

    this.getLatLng=function(){
      return _map.fromDivPixelToLatLng(ctr);
    }
    this.size=function(){
      return markers.length;
    }
    this.isInRange=function(_m){
      var pos=_map.fromLatLngToDivPixel(_m.getLatLng());
      if(Math.pow(pos.x-ctr.x,2)+Math.pow(pos.y-ctr.y,2) <= Math.pow(radius,2)) {
        return true;
      } else {
        return false;
      }
    }
    this.addMarker=function(_m){
      var pos=_map.fromLatLngToDivPixel(_m.getLatLng());
      var sz=this.size();
      ctr.x=(pos.x+(ctr.x*sz))/(sz+1);
      ctr.y=(pos.y+(ctr.y*sz))/(sz+1);
      markers.push(_m);
    }
    this.getMarkers=function(){
      return markers;
    }
  }

  function getIcon(_n) {
    var i=new GIcon();
    i.image='/common/actions/cluster.php?i=0&n='+_n
    i.iconSize=new GSize(32,32);
    i.iconAnchor=new GPoint(16,16);
    return i;
  }
}

