/* //////////////////////////////////////////////////////////////////////////////////////////////////////////// */
/* --------------------------------------------------------------------------------------------------------------------------------------- */
/*                                                                          CLASSE MyMapManager                                                                         */
/* --------------------------------------------------------------------------------------------------------------------------------------- */
/*  Détails:
     Description : Permet de gérer les markers sur une map selon une liste de point, et en les regroupant si ils sont trop proches
     Auteur                           : Brice
     Créée le                         : 22 Juillet 2009
     Dernière modification  le : 24 Août 2009
     --------------------------------------------------------         ATTRIBUTS        --------------------------------------------------------
     _map = objet googlemap GMap2 qui est la map global qui gère tout
     _eventMarker = liste des évenements à associer au marker classique[nom,fonction]  par exemple [onclick,markerCliked]
     _eventMarkerGrp = liste des évenements à associer au marker de regroupement
     _points = liste des points récupérer qui vont être affichés ou regroupés
     _distanceMinX = distance horizontale en pixel minimale entre deux markers non regroupés
     _distanceMinY = distance verticale en pixel minimale entre deux markers non regroupés
     iconOptionsGrp = options des icones de regroupement, du type createFlatIcon,  plus d'info --> http://gmaps-utility-library.googlecode.com/svn/trunk/mapiconmaker/1.1/docs/reference.html
     _markerOptionClassique = option pour l'icone d'un simple point
	 --------------------------------------------------------         METHODE         -------------------------------------------------------- 
     addPoint(point) : ajoute "point" au tableau de points.
     getPoints() : Retourne le tableau de point, dont certain sont null si ils sont regroupés sous un autre point.
     clearPoints() : vide le tableau de points.
     setDistanceMinEntreMarker(x,y) : défini la distance en horizontale(x) et en verticale(y) entre les markers, en pixel.
     addMapEvent(event,fonction) : Ajoute un listener à la map, event est ke nom du listener (exemple : "moveend") et la fonction, la fonction a réaliser lorsque l'évènement est déclenché
     addMarkerEvent(event,fonction) : même chose pour les markers simples
     addMarkerGrpEvent(event, fonction) : même chose pour les markers de regroupement
     recalculPoint() : Recalcul tous les points pour savoir si certains doivent être regroupés
     refreshMap() : met à jour les points affichés en vidant les markers affichés préalablement
     createMarkerFromPoint(point) : créer un marker a partir d'un point on y incrustant les informations nécessaire
     deplacerMap(lat,lng,accuracy) : déplace la map vers les coordonnées voulues, et selon un zoom correspondant a une recherhe
     pointIsProche(pointA,pointB) : vérifie si les deux points sont suffisement proches pour être regroupés
*/
/* //////////////////////////////////////////////////////////////////////////////////////////////////////////// */
MyMapManager=function(map) {
    this._map = map;
    this._eventMarker = new Array();
    this._eventMarkerGrp = new Array();
    this._points = new Array();
    this._distanceMinX = map.distanceMinX || 20;
    this._distanceMinY = map.distanceMinY || 20;
    
	// CREATION DE L'ICON CLASSIQUE POUR LES SIMPLES POINTS
	if(map.iconPointOptions)
	{
		this._markerOptionClassique = map.iconPointOptions;
	}else
	{
		var iconPoint = new GIcon(G_DEFAULT_ICON);
		// définir les options d'un icon personnalisé ici
		// --> http://code.google.com/intl/fr/apis/maps/documentation/overlays.html#Icons_overview
		this._markerOptionClassique = { icon : iconPoint };
	}

	// PREPARATION DES ICONES POUR LE GROUPEMENT DE POINTS, l'attribut .label est défini à la création du marker dans la méthode "createMarkerFromPoint"
	// pour modifier le marker de regroupement en visuel:
	// --> http://gmaps-utility-library.googlecode.com/svn/trunk/mapiconmaker/1.1/examples/markericonoptions-wizard.html
	this.iconOptionsGrp = {};
	this.iconOptionsGrp.width = 25;
	this.iconOptionsGrp.height = 25;
	this.iconOptionsGrp.primaryColor = "#000000";
	this.iconOptionsGrp.labelSize = 10;
	this.iconOptionsGrp.labelColor = "#ffffff";
	this.iconOptionsGrp.shape = "roundrect";
	
	this.tabMarker = new Array();
};
MyMapManager.prototype.addPoint = function(point)
{ 
    this._points.push(point);
};
MyMapManager.prototype.getPoints = function()
{
    return this._points;
};
MyMapManager.prototype.clearPoints = function()
{
    this._points = new Array();//on vide le tableau de points
};
MyMapManager.prototype.setDistanceMinEntreMarker = function(x,y)
{
    this._distanceMinX = x;
    this._distanceMinY = y;
};
MyMapManager.prototype.addMapEvent = function(event,fonction)
{
    GEvent.addListener(this._map, event, fonction);
};
MyMapManager.prototype.addMarkerEvent = function(event,fonction)
{
    this._eventMarker.push(new Array(event,fonction));
};
MyMapManager.prototype.addMarkerGrpEvent = function(event,fonction)
{
    this._eventMarkerGrp.push(new Array(event,fonction));
};
MyMapManager.prototype.recalculPoint = function()
{
    var createCluster = true;
    var rescan = true;
    var nbIncTab = 0;
    
    while(createCluster)
    {
        createCluster = rescan = false;
        for ( var i = nbIncTab; i < this._points.length; i++)
        {
            if(this._points[i] == null) continue;
            for (var j = 0; j < this._points.length; j++)
            {
                if(this._points[j] == null || i==j) continue;
                
                var pointA = this._points[i];
                var pointB = this._points[j];
                if(this.pointIsProche(pointA,pointB))
                {
                    if(!pointA.isCluster) //création d'un nouveau point si pointA n'est pas déjà un pointCluster
                    {
                        var ptCluster = new Point(pointA.lat,pointA.lng,'cluster');
                        ptCluster.isCluster = true;
                        createCluster = true;
                        this._points[i] = ptCluster;
                        //on rajoute le pointA a la liste des sousPoints du nouveau cluster
                        this._points[i].sousPoint.push(pointA);
                    }
                    // ajout de pointB en sousPoint ou la lsite des sousPoint de pointB si celui ci est déjà un ClusterPoint
                    if(!pointB.isCluster)
                    {
                        this._points[i].sousPoint.push(pointB);
                    }else
                    {
                        for(var k=0; k < pointB.sousPoint.length; k++)
                        {
                            this._points[i].sousPoint.push(pointB.sousPoint[k]);
                        }
                    }
                    this._points[j] = null;// on supprime le point du tableau
                }
            }
            nbIncTab = 0;// on pourrait diminuer le travail de recherche en commençant la premier boucle apres ceux déjà scanner
        }
    }
};
MyMapManager.prototype.refreshMap=function()
{
    this._map.clearOverlays();//on vide les markers déjà affichés
	this.tabMarker.clear();
	var numMarker = 0;
    // on va créer un marqueur sur la map pour chaque point non null
    for ( var i=0; i < this._points.length; i++)
    {
        if(this._points[i]==null) continue;
		this._points[i].numMarker = numMarker;
		if(this._points[i].isCluster)
		{
			for(var j = 0; j < this._points[i].sousPoint.length; j++)
                this._points[i].sousPoint[j].numMarker = numMarker;
		}
        this.createMarkerFromPoint(this._points[i]);
		numMarker++;
    }
};
MyMapManager.prototype.createMarkerFromPoint=function(point)
{
    var pt = new GLatLng(point.getLat(), point.getLng());//on crée un point GLatLng a partir des coordonnées stockées dans point
    if(point.isCluster)
    {
        // On affiche sur l'icone le nombre de sousPoint que regroupe ce point
        this.iconOptionsGrp.label = point.sousPoint.length.toString();
        // DEFINITION DE LA TAILLE DU LABEL
        if(point.sousPoint.length < 10)
            this.iconOptionsGrp.labelSize = 15;
        else if (point.sousPoint.length < 100)
            this.iconOptionsGrp.labelSize = 12;
        else
            this.iconOptionsGrp.labelSize = 10;
        // CREATION DU MARKER FlatIcon
        var marker = new GMarker(pt, MapIconMaker.createFlatIcon(this.iconOptionsGrp));
        // DEFINITION DES EVENTS SUR LES MARKERS GRP
        for(var i = 0; i < this._eventMarkerGrp.length; i++)
        {
            GEvent.addListener(marker, this._eventMarkerGrp[i][0], this._eventMarkerGrp[i][1]);
        }
    }
    else
    {
        // CREATION DU MARKER
        var marker = new GMarker(pt, this._markerOptionClassique);
        // DEFINITION DES EVENTS SUR LES MARKERS SIMPLE
        for(var i = 0; i < this._eventMarker.length; i++)
        {
            GEvent.addListener(marker, this._eventMarker[i][0], this._eventMarker[i][1]);
        }
    }
	marker.isCluster = point.isCluster;
    marker.point = point;// on défini le point associé au marker pour récupérer les informations
	this.tabMarker.push(marker);
    this._map.addOverlay(marker);// on affiche le marker sur la map
}
MyMapManager.prototype.deplacerMap=function(lat,lng,accuracy)
{
    var zoom = 0;
	// Le zoom va dépendre de la variable accuracy associé, si il s'agit d'une ville, d'une adresse ou d'un pays
	// voir la page http://code.google.com/intl/fr/apis/maps/documentation/reference.html#GGeoAddressAccuracy
	var tabAccuracy = new Array(6,5,7,8,10,10,15,16,17,17);
	zoom = tabAccuracy[accuracy];
    var point = new GLatLng(lat,lng);
    this._map.setCenter(point,zoom);
};
MyMapManager.prototype.pointIsProche = function(pointA,pointB)
{
    if (pointA.getLat() && pointB.getLng())
	{
        // on récupère les coordonnées en pixel des deux points à tester depuis l'origine de la map
        var ptA = this._map.fromLatLngToDivPixel(new GLatLng(pointA.getLat(), pointA.getLng()));
        var ptB = this._map.fromLatLngToDivPixel(new GLatLng(pointB.getLat(), pointB.getLng()));

        // maintenant on récupère les distances horizontale et verticale, en pixel, entre ces deux points
        var distX = Math.max(Math.abs(ptA.x),Math.abs(ptB.x))-Math.min(Math.abs(ptA.x),Math.abs(ptB.x));
        var distY = Math.max(Math.abs(ptA.y),Math.abs(ptB.y))-Math.min(Math.abs(ptA.y),Math.abs(ptB.y));
        
        var isProche = (distX < this._distanceMinX) && (distY < this._distanceMinY);

        return isProche;
    }else
    {
        // si les parametre fourni ne sont pas correct, on va renvoyer false et ainsi laisser tomber ce point pour laisser le reste du script se finir pour le reste
        return false;
    }
}

/* //////////////////////////////////////////////////////////////////////////////////////////////////////////// */
/* --------------------------------------------------------------------------------------------------------------------------------------- */
/*                                                   CLASSE Point                                                                          */
/* --------------------------------------------------------------------------------------------------------------------------------------- */
/*  Détails:
     Description : Permet de gérer les informations sur les markers avant et apres la transformation en marker
     Auteur                           : Brice
     Créée le                         : 22 Juillet 2009
     Dernière modification  le : 27 Juillet 2009
     --------------------------------------------------------         ATTRIBUTS        --------------------------------------------------------
     lat = latitude googlemap du point
     lng = longitude googlemap du point
     isCluster = booléen qui va déterminée si le point est un marqueur regroupant plusieurs markers ou un marker seul
     sousPoint = si le marker regroupe d'autre marker, alors il a un tableau de tous les points regroupés     
     --------------------------------------------------------         METHODE         --------------------------------------------------------
     getLat() : renvoi une latitude, celle du point si celui ci est un simple marker, ou le milieu entre les points les plus éloignés (selon leur latitude) lors d'un regroupement de point
     getLng() : renvoi une longitude, celle du point si celui ci est un simple marker, ou le milieu entre les points les plus éloignés (selon leur longitude) lors d'un regroupement de point
     getMaxLat() : renvoi la latitude maximum de tous les sous-points du regroupement de point ou la latitude si il s'agit d'un point simple
     getMaxLng() : renvoi la longitude maximum de tous les sous-points du regroupement de point ou la longitude si il s'agit d'un point simple
     getMinLat() : renvoi la latitude minimale de tous les sous-points du regroupement de point ou la latitude si il s'agit d'un point simple
     getMinLng() : renvoi la longitude minimale de tous les sous-points du regroupement de point ou la longitude si il s'agit d'un point simple
*/
/* //////////////////////////////////////////////////////////////////////////////////////////////////////////// */
Point = function(lat,lng) 
{
    this._lat = lat;
    this._lng = lng;
    this.isCluster = false;
    this.sousPoint = new Array();
};
Point.prototype.getLat=function()
{
    if(!this.isCluster)
        return this._lat;
    else
    {
        //CALCUL DES VALEURS EXTREMES minimum et maximum des LATITUDES des sousPoints de ce cluster
        var min = 200;
        var max = -200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lat > max) max = this.sousPoint[i]._lat;
            if(this.sousPoint[i]._lat < min) min = this.sousPoint[i]._lat;
        }
        dif = parseFloat(max) + parseFloat(min);
        //ON RETOURNE LA MOYENNE DU MINIMUM et du MAXIMUM
        return dif/2;
    }
};
Point.prototype.getLng=function()
{
    if(!this.isCluster.valueOf())
        return this._lng;
    else
    {
        //CALCUL DES VALEURS EXTREMES minimum et maximum des LONGITUDES des sousPoints de ce cluster
        var min = 200;
        var max = -200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lng > max) max = this.sousPoint[i]._lng;
            if(this.sousPoint[i]._lng < min) min = this.sousPoint[i]._lng;
        }
        dif = parseFloat(max) + parseFloat(min);
        //ON RETOURNE LA MOYENNE DU MINIMUM et du MAXIMUM
        return dif/2;
    }
};
Point.prototype.getMaxLat=function()
{
    if(!this.isCluster)
    {
        return this._lat;
    }else
    {
        //CALCUL DU MAXIMUM DE LA LATITUDE DE TOUS LES SOUS-POINTS
        var max = -200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lat > max) max = this.sousPoint[i]._lat;
        }
        return max;
    }
};
Point.prototype.getMaxLng=function()
{
    if(!this.isCluster)
    {
        return this._lng;
    }else
    {
        //CALCUL DU MAXIMUM DE LA LATITUDE DE TOUS LES SOUS-POINTS
        var max = -200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lng > max) max = this.sousPoint[i]._lng;
        }
        return max;
    }
};
Point.prototype.getMinLat=function()
{
    if(!this.isCluster)
        return this._lat;
    else
    {
        //CALCUL DU MAXIMUM DE LA LATITUDE DE TOUS LES SOUS-POINTS
        var min = 200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lat < min) min = this.sousPoint[i]._lat;
        }
        return min;
    }
};
Point.prototype.getMinLng=function()
{
    if(!this.isCluster)
        return this._lng;
    else
    {
        //CALCUL DU MAXIMUM DE LA LATITUDE DE TOUS LES SOUS-POINTS
        var min = 200;
        for(var i = 0; i < this.sousPoint.length; i++)
        {
            if(this.sousPoint[i]._lng < min) min = this.sousPoint[i]._lng;
        }
        return min;
    }
};
