﻿/// --------------------------------------------------------------------------------------------------------------------------
/// Version
/// --------------------------------------------------------------------------------------------------------------------------
/// 1.00	JQ 		Release initiale uniquement compatible avec une carte par page
/// 1.01	JQ		Ajout mapControl et scaleControl
/// 1.02	JQ		Ajout gestion erreur container, panBy, offset sur panTo, modification de l'order pour le picto du centre
/// 1.03    JQ      Ajout du paramètre drag sur l'événement onChange
/// 1.04    JQ      Ajout de selection.remove, suppression à chaque createMap des instances précédentes
/// 1.05    JQ      Selection désactivée par défaut, ajout de l'icône par défaut sur la création d'une marque si omis
/// 1.06    JQ      Correction problème avec l'icône d'un marqueur si indiqué
/// --------------------------------------------------------------------------------------------------------------------------

var googleMapApi = {

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Loader api googleMap
    /// ----------------------------------------------------------------------------------------------------------------------

    key: null,
    language: null,
    callback: null,

    load: function(key, language, callback) {
        this.key = key;
        this.language = language;
        this.callback = callback;
        this.googleFrameworkLoad();
    },

    googleFrameworkLoad: function() {
        jQuery.getScript("http://www.google.com/jsapi?key=" + this.key + "&callback=googleMapApi.googleFrameworkLoaded");
    },

    googleFrameworkLoaded: function() {
        // On charge l'api google map
        google.load("maps", "2.x", {
            "language": googleMapApi.language,
            "callback": googleMapApi.callback
        });
    },

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Fonctions cartographie
    /// ----------------------------------------------------------------------------------------------------------------------

    map: null,
    obj: null,
    options: null,
    container: null,

    // Créé une map
    createMap: function(container, obj, width, height, options) {
        if (GBrowserIsCompatible()) {
            this.container = container;
            // On atteint le container et on définit dynamiquement sa taille
            var _container = jQuery(container);
            _container.css("width", width);
            _container.css("height", height);
            // On indique la variable qui contient l'instance
            this.obj = obj;
            if (typeof _container.get(0) == 'undefined') {
                throw new Error("Invalid map container", "Invalid map container");
            } else {
                // On supprime une éventuelle précédente sélection / gestionnaire de fenêtre
                if (this.map != null) {
                    this.selection.remove();
                    this.map.removeOverlay(this.window);
                }

                // On créé la map
                var _map = this.map = new google.maps.Map2(_container.get(0));
                // On fixe les options
                this.setOptions(options);
                // On définit les contrôles
                var _control, _pos;
                // On affiche le contrôle de l'échelle
                if (options.viewPort.scaleControl.visible) {
                    _control = new GScaleControl();
                    _pos = new GControlPosition(G_ANCHOR_BOTTOM_LEFT, new GSize(68, 5));
                    _map.addControl(_control, _pos);
                }
                // On affiche le contrôle de déplacement et de sélection d'échelle
                if (options.viewPort.mapControl.visible) {
                    _control = new GLargeMapControl3D();
                    _pos = new GControlPosition(
						G_ANCHOR_TOP_LEFT,
						new GSize(
							this.options.viewPort.mapControl.x,
							this.options.viewPort.mapControl.y
						)
					);
                    _map.addControl(_control, _pos);
                }
                // Liaison tardive
                var _objMap = this;
                // On créé un callback lors du déplacement de la souris
                GEvent.addListener(_map, "dragend", function() { _objMap.onChange(true); });
                // On indique à window son parent
                this.window.parent = this;
                // Initialise les fenêtres personnalisées
                _map.addOverlay(this.window);

                return this;
            }
        }
        return null;
    },

    GWindow: null,

    // Localise selon une adresse
    setAddress: function(address, level, callback) {
        // Liaison tardive
        var _objMap = this;
        // On créé une instance du geocoder
        var _geocoder = new GClientGeocoder();
        // On demande à google des réponses
        _geocoder.getLocations(address, function(response) { _objMap.onGeocoderResponse(response, level, callback); });
    },

    // Localise selon une latitude et une longitude
    setPosition: function(lat, lng, level) {
        // On définit le centre
        this.setCenter(
			{
			    lat: lat,
			    lng: lng,
			    level: level
			}
		);

        // On définit le centre
        if (this.options.selection.enableSelection) {
            // On supprime la précédente sélection (si existe)
            this.selection.remove();
            // On en créé une nouvelle
            this.selection.create(
                new google.maps.LatLng(lat, lng),
                this.map,
                this
            );
        }

        this.onChange();
    },

    // Définit le centre de la carte
    setCenter: function(point) {
        var _center = new google.maps.LatLng(point.lat, point.lng);
        // On définit le centre
        this.map.setCenter(_center, point.level);
    },

    // Obtient le centre de la carte
    getCenter: function() {
        // Obtient le centre de la carte
        var _point = this.map.getCenter();
        // Retourne la position du centre
        return (_point != null) ? { lat: _point.lat(), lng: _point.lng(), radius: 0} : { lat: null, lng: null, radius: 0 };
    },

    // Renvoi la latitude et la longitude du point en cours
    getPosition: function() {
        if (this.options.selection.enableSelection) {
            return this.selection.getPosition();
        } else {
            return this.getCenter();
        }
    },

    mapType: {
        normal: 0,
        satellite: 1,
        hybrid: 2,
        physical: 3
    },

    // Change le type de carte
    setMapType: function(map) {
        var _mapType;
        switch (map) {
            case 0: _mapType = G_NORMAL_MAP; break;
            case 1: _mapType = G_SATELLITE_MAP; break;
            case 2: _mapType = G_HYBRID_MAP; break;
            case 3: _mapType = G_PHYSICAL_MAP; break;
        }
        this.map.setMapType(_mapType);
        this.selection.update();
    },

    setRadius: function(radius) {
        this.selection.setRadius(radius);
    },

    // Définit les options par défaut
    setDefaultOptions: function(options) {
        options = this.isUndef(options, {});
        options.selection = this.isUndef(options.selection, {});
        options.selection.enableSelection = this.isUndef(options.selection.enableSelection, false);
        options.selection.radius = this.isUndef(options.selection.radius, 2000);
        options.selection.enableChangePosition = this.isUndef(options.selection.enableChangePosition, true);
        options.selection.enableChangeRadius = this.isUndef(options.selection.enableChangeRadius, true);
        options.selection.enableTooltip = this.isUndef(options.selection.enableTooltip, true);
        options.selection.tooltipClass = this.isUndef(options.selection.tooltipClass, 'tooltip');
        options.selection.borderColor = this.isUndef(options.selection.borderColor, {});
        options.selection.borderColor.normal = this.isUndef(options.selection.borderColor.normal, '#888888');
        options.selection.borderColor.inverse = this.isUndef(options.selection.borderColor.inverse, '#FFFFFF');
        options.selection.fillColor = this.isUndef(options.selection.fillColor, {});
        options.selection.fillColor.normal = this.isUndef(options.selection.fillColor.normal, '#888888');
        options.selection.fillColor.inverse = this.isUndef(options.selection.fillColor.inverse, '#FFFFFF');
        options.selection.fillColor.alpha = this.isUndef(options.selection.fillColor.alpha, 0.2);
        options.selection.segments = this.isUndef(options.selection.segments, 72); // 1 segment pour 5Â°

        options.viewPort = this.isUndef(options.viewPort, {});
        options.viewPort.mapControl = this.isUndef(options.viewPort.mapControl, {});
        options.viewPort.mapControl.visible = this.isUndef(options.viewPort.mapControl.visible, true);
        options.viewPort.mapControl.x = this.isUndef(options.viewPort.mapControl.x, 7);
        options.viewPort.mapControl.y = this.isUndef(options.viewPort.mapControl.y, 7);
        options.viewPort.scaleControl = this.isUndef(options.viewPort.scaleControl, {});
        options.viewPort.scaleControl.visible = this.isUndef(options.viewPort.scaleControl.visible, true);

        options.anim = this.isUndef(options.anim, {});
        options.anim.interval = this.isUndef(options.anim.interval, 10);
        options.anim.timeoutInterval = this.isUndef(options.anim.timeoutInterval, 30);

        return options;
    },

    // Renvoi les options courantes
    getOptions: function() {
        return this.options;
    },

    // Définit les options courantes
    setOptions: function(options) {
        // Définit les options
        this.options = this.setDefaultOptions(options);
    },

    // Renvoi le contenu de la variable si elle est définit si non la valeur
    isUndef: function(variable, value) {
        return (typeof variable != 'undefined') ? variable : value;
    },

    // Obtient les données depuis le webservice
    getData: function(webservice, params, callback) {
        GDownloadUrl(webservice,
			function(data) {
			    var _res = null;
			    if (typeof (data) != 'undefined' && data != '') {
			        // On récupère la valeur D
			        _res = JSON.parse(data);
			        // Si D contient une valeur
			        if (typeof (_res.d) != 'undefined' && _res.d != '') {
			            // On parse le résultat de la valeur D
			            _res = JSON.parse(_res.d);
			        } else
			            _res = null;
			    }

			    // On envoi des infos au callback
			    callback(_res);
			},
			JSON.stringify(params),
			"application/json; charset=utf-8"
		);
    },

    markers: [],

    // Ajoute un marqueur
    addMarker: function(point, icon, callback, params) {
        // On définit le point
        var _latLng = new GLatLng(point.lat, point.lng);
        // On définit l'image
        if (typeof icon == 'undefined') icon = null;
        if (icon != null) {
            _icon = new GIcon();
            _icon.iconSize = new GSize(icon.size.width, icon.size.height);
            _icon.iconAnchor = new GPoint(icon.anchor.x, icon.anchor.y);
            if (typeof icon.windowAnchor != 'undefined')
                _icon.infoWindowAnchor = new GPoint(icon.windowAnchor.x, icon.windowAnchor.y);
        } else {
            _icon = G_DEFAULT_ICON;
        }
        // On définit la marque
        var _marker = new GMarker(_latLng, _icon);
        // On ajoute au calque		
        this.map.addOverlay(_marker);
        // On définit l'image l'image
        if (icon != null) this.setIcon(_marker, icon, false);
        // On ajoute la marque au tableau
        this.markers.push(_marker);
        // Liaison tardive
        var _objMap = this;
        // On ajoute un callback lorsque l'on va cliquer sur l'icône et pour l'état hover
        if (typeof callback != 'undefined') {
            GEvent.addListener(_marker, "click", function(latlng, animation) { if (!animation) _objMap.pause(); callback(_marker, point, params, 0) });
            GEvent.addListener(_marker, "clickTab", function(tab) { callback(_marker, point, params, tab) });
        }
        if (icon != null) {
            GEvent.addListener(_marker, "mouseover", function() { _objMap.setIcon(_marker, icon, true); });
            GEvent.addListener(_marker, "mouseout", function() { _objMap.setIcon(_marker, icon, false); });
        }
    },

    // On change l'icône
    setIcon: function(marker, icon, state) {
        // Si l'icône over n'est pas définit on ne touche pas à l'image
        icon.fileOver = this.isUndef(icon.fileOver, icon.fileUp);
        // On récupère l'image à utiliser en fonction de l'état
        var _file = this.isSlashed(icon.path) + ((state) ? icon.fileOver : icon.fileUp);

        // On met à jour l'image de la marque
        marker.setImage(_file);
    },

    // Vérifie si il y'a un séparateur après le chemin 
    isSlashed: function(path) {
        return path + (path.substr(path.length - 1) == '/' ? '' : '/');
    },

    // Supprime les marqueurs
    clearMarkers: function() {
        // Pour chaque marque
        for (var _i = 0; _i < this.markers.length; _i++)
        // On la supprime du calc
            this.map.removeOverlay(this.markers[_i]);
        // On remets à 0
        this.markers = [];
    },

    // Retourne l'index d'une marque
    getMarkerIndex: function(marker) {
        // Pour chaque marque
        for (var _i = 0; _i < this.markers.length; _i++)
        // Si la marque correspond on renvoi l'index
            if (this.markers[_i] == marker) return _i;
        // Si on trouve rien on retourne null
        return null;
    },

    // Ouvre une fenêtre
    openWindow: function(marker, tabs, tab, callback, options) {
        this.window.open(marker, tabs, tab, callback, options);
    },

    // Ferme la fenêtre courante
    closeWindow: function() {
        this.window.close();
    },

    // On se déplace au point définit
    panTo: function(point, offsetX, offsetY) {
        this.map.panTo(
        	this.offset(
				new GLatLng(point.lat, point.lng),
				offsetX,
				offsetY
			)
		);
    },

    panBy: function(x, y) {
        this.map.panBy(new GSize(x, y));
    },

    // Décale un point du nombre de pixel donné
    offset: function(point, offsetX, offsetY) {
        offsetX = (offsetX == null) ? 0 : offsetX;
        offsetY = (offsetY == null) ? 0 : offsetY;

        if (offsetX + offsetY != 0) {
            // On récupère le niveau de zoom courant
            var _zoom = this.map.getZoom();
            // On récupère la carte courante
            var _mapType = this.map.getCurrentMapType();
            // On récupère la projection mercator associé à la carte			
            var _projection = _mapType.getProjection();
            // On convertie le point en pixel
            var _point = _projection.fromLatLngToPixel(point, _zoom);
            // On renvoi le nouveau point avec le décalage
            return _projection.fromPixelToLatLng(
				new GPoint(
					_point.x - offsetX,
					_point.y - offsetY
				),
				_zoom
			);
        }
    },

    distanceFromCenter: function(point) {
        var _point = new GLatLng(point.lat, point.lng);
        // Retourne la distance depuis le centre
        return _point.distanceFrom(this.map.getCenter());
    },

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Animation de la carte
    /// ----------------------------------------------------------------------------------------------------------------------

    playHwnd: null,
    playOutHwnd: null,
    currentMarker: 0,
    animate: false,

    // Débute l'animation
    play: function() {
        var _parent = this;

        this.playHwnd = window.setInterval(
			function() {
			    _parent.anim();
			},
			this.options.anim.interval * 1000
		);
        // On lance la première annim
        _parent.anim();
    },

    // Arrête l'animation
    stop: function() {
        // On arrête l'animation
        window.clearInterval(this.playHwnd);
        window.clearTimeout(this.playOutHwnd);
        // On remets à null les variables
        this.playHwnd = null;
        this.playOutHwnd = null;
        // On remets à 0 le compteur
        this.currentMarker = 0;
    },

    // Mets en pause l'animation et reprends au bout du timeout donné
    pause: function() {
        // On arrête l'animation
        this.stop();
        // Liaison tardive
        var _objMap = this;
        // On attends 30 secondes et on reprend
        if (this.animate) {
            this.playOutHwnd = window.setTimeout(
				function() { _objMap.play(); },
				this.options.anim.timeoutInterval * 1000
			);
        }
    },

    anim: function() {
        // On indique que l'animation est possible
        this.animate = true;
        // Si on dépasse le tableau on revient à 0
        if (this.currentMarker > this.markers.length) this.currentMarker = 0;
        // On execute le trigger
        var _mark = this.markers[this.currentMarker];
        // Si la marque existe
        if (typeof _mark != 'undefined') {
            // On récupère le point (nécessaire comme premier paramètre pour le trigger)
            var _point = _mark.getLatLng();
            // On appel le trigger
            GEvent.trigger(_mark, 'click', _point, true);
        }
        // On incrémente le compteur de 1
        this.currentMarker++;
    },

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Evènements
    /// ----------------------------------------------------------------------------------------------------------------------

    onChange: function(drag) {
        if (this.options.onChange != null)
            this.options.onChange(
			    (this.options.selection.enableSelection) ? this.selection.getPosition() : this.getPosition(),
			    drag
		    );
    },

    // Evènement appelé lors du geoencodage d'une adresse
    onGeocoderResponse: function(response, level, callback) {
        var _locations = [];
        // Si au moins une réponse
        if (response && response.Status.code == 200) {
            // On géolocalise la première réponse
            this.setPosition(
				response.Placemark[0].Point.coordinates[1],
				response.Placemark[0].Point.coordinates[0],
				level
			);

            // On renvoi au callback la liste des réponses
            for (var _i = 0; _i < response.Placemark.length; _i++) {
                var _location = response.Placemark[_i];
                // On ajoute les localités au tableau
                _locations.push({
                    address: _location.address,
                    point: {
                        lat: _location.Point.coordinates[1],
                        lng: _location.Point.coordinates[0]
                    },
                    accuracy: _location.AddressDetails.Accuracy,
                    countryNameCode: _location.AddressDetails.Country.CountryNameCode
                });
            }
        }

        // On appelle le callback
        if (typeof callback != 'undefined') callback(_locations);
    },

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Fonctions rayon
    /// ----------------------------------------------------------------------------------------------------------------------

    window: {
        custWindow: null,
        custStem: null,
        centerHandle: null,
        map: null,
        point: null,
        parent: null,
        options: null,

        initialize: function(map) {
            this.map = map;
            // On créé des div pour les éléments de la fenêtre
            this.custWindow = document.createElement("div");
            this.custStem = document.createElement("div");
            // On les mets en absolute
            this.custWindow.style.position = this.custStem.style.position = "absolute";
            // On ajoute aux div par défaut de googlemap nos div personnalisés
            map.getPane(G_MAP_FLOAT_SHADOW_PANE).appendChild(this.custWindow);
            map.getPane(G_MAP_FLOAT_SHADOW_PANE).appendChild(this.custStem);
        },

        remove: function() {
            // On supprime les ojects du parent			
            this.custWindow.parentNode.removeChild(this.custWindow);
            this.custStem.parentNode.removeChild(this.custStem);
        },

        redraw: function(force) {
            if (this.point != null && this.map != null) {
                // On récupère les coordonnées en pixel du point
                var _point = this.map.fromLatLngToDivPixel(this.point);
                // On définit la position des éléments
                this.custStem.style.left = (_point.x + this.centerHandle.x) + "px";
                this.custStem.style.bottom = (-_point.y + this.centerHandle.y - this.getFudge()) + "px";
                this.custWindow.style.left = (_point.x + this.centerHandle.x + this.options.offset.width) + "px";
                this.custWindow.style.bottom = (-_point.y + this.centerHandle.y + this.options.offset.height) + "px";
            }
        },

        // Ouvre une fenêtre
        open: function(text, marker, tabs, tab, options) {
            // On enregistre les options
            this.options = this.setDefaultOptions(options);
            // Récupère les coordonnées x/y du point d'ancrage
            var _x = marker.getIcon().iconAnchor.x - marker.getIcon().infoWindowAnchor.x;
            var _y = marker.getIcon().iconAnchor.y - marker.getIcon().infoWindowAnchor.y;
            // On définit les coordponées du point d'ancrage
            this.centerHandle = new GPoint(_x, _y);
            // Obtient le point du marqueur
            var _point = this.point = marker.getPoint();
            // Obtient un zIndex pour la latitude du marqeur
            var _z = GOverlay.getZIndex(_point.lat());
            // On définit la largeur de la fenêtre
            this.custWindow.style.width = options.width + 'px';

            var _htmlTabs = '';
            // Si il y a plusieurs tabs définis
            if (tabs.length > 1) {
                // Pour chaque tab
                for (var _i = 0; _i < tabs.length; _i++)
                    _htmlTabs += '<div ' +
						'class="' + ((tab == _i) ? tabs[_i].classSelected : tabs[_i].classUnselected) + '" ' +
						'onclick="GEvent.trigger(' + this.parent.obj + '.markers[' + this.parent.getMarkerIndex(marker) + '], \'clickTab\', ' + _i + ');">' + (tabs[_i].title ? tabs[_i].title : '') +
						'</div>';

                _htmlTabs = '<div class="' + options.tabsClass + '">' + _htmlTabs + '</div>';
            }
            // On insert le contenu...
            this.custWindow.innerHTML = _htmlTabs + '<div class="' + options.windowClass + '">' + text + '</div>';
            // On insert l'image de la languette
            if (this.isIE() && options.stem.url.toLowerCase().indexOf(".png") > -1) {
                var _loader = "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + options.stem.url + "', sizingMethod='scale');";
                this.custStem.innerHTML = '<div style="height:' + options.stem.height + 'px; width:' + options.stem.width + 'px; ' + _loader + '" ></div>';
            } else {
                this.custStem.innerHTML = '<img src="' + options.stem.url + '" width="' + options.stem.width + '" height="' + options.stem.height + '">';
            }
            // On indique le zIndex des différents div de la fenêtre
            this.custWindow.style.zIndex = _z;
            this.custStem.style.zIndex = _z + 1;
            // Affiche les élements de la fenêtre personnalisée
            this.custWindow.style.display = this.custStem.style.display = "";
            // Force le dessin
            this.redraw(true);
        },

        setDefaultOptions: function(options) {
            options.width = this.isUndef(options.width, 200);
            options.tabsClass = this.isUndef(options.tabsClass, 'tabs');
            options.windowClass = this.isUndef(options.windowClass, 'window');
            return options;
        },

        // Renvoi le contenu de la variable si elle est définit si non la valeur
        isUndef: function(variable, value) {
            return (typeof variable != 'undefined') ? variable : value;
        },

        // Ferme la fenêtre
        close: function() {
            this.custWindow.style.display = this.custStem.style.display = "none";
        },

        // Obtient le décalage des marges sur des navigateurs 
        getFudge: function() {
            var _agent = navigator.userAgent.toLowerCase();
            if (_agent.indexOf("opera") > -1) return 3;
            if (_agent.indexOf("msie") > -1 && _agent.indexOf("opera") < 1) return 0;
            return 5;
        },

        isIE: function() {
            var _agent = navigator.userAgent.toLowerCase();
            return ((_agent.indexOf("msie") > -1) && (_agent.indexOf("opera") < 1)) ? true : false;
        },

        /// ----------------------------------------------------------------------------------------------------------------------
        /// Evènements
        /// ----------------------------------------------------------------------------------------------------------------------

        onTabClick: function(marker, point, params, tab, callback) {
            // On appelle le callback
            callback(marker, point, params, tab);
        }
    },

    /// ----------------------------------------------------------------------------------------------------------------------
    /// Fonctions rayon
    /// ----------------------------------------------------------------------------------------------------------------------

    selection: {
        centerPosition: null,
        centerHandle: null,
        dragPosition: null,
        dragHandle: null,
        radius: 0,
        map: null,
        polygon: null,
        parent: null,
        tooltip: null,

        remove: function() {
            if (this.map != null) {
                if (this.centerHandle != null) this.map.removeOverlay(this.centerHandle);
                if (this.dragHandle != null) this.map.removeOverlay(this.dragHandle);
                if (this.polygon != null) this.map.removeOverlay(this.polygon);
                this.centerHandle = null;
                this.dragHandle = null;
                this.polygon = null;
            }
        },

        exist: function() {
            return (this.centerHandle == null) ? false : true;
        },

        // Créé un cercle
        create: function(center, map, parent) {
            this.map = map;
            this.parent = parent;
            this.radius = parent.options.selection.radius;

            if (this.centerHandle == null) {
                // Liaison tardive vers l'objet courant			
                var _objselection = this;

                // -----------------------------------------------------------------------------
                //  On créé le label pour le tooltip
                // -----------------------------------------------------------------------------

                // On créé un div pour le tooltip
                this.tooltip = document.createElement("div");
                this.tooltip.style.position = "absolute";

                // On ajoute aux div par défaut de googlemap nos div personnalisés
                map.getPane(G_MAP_FLOAT_SHADOW_PANE).appendChild(this.tooltip);

                // -----------------------------------------------------------------------------
                //  Icône de déplacement
                // -----------------------------------------------------------------------------

                if (this.parent.options.selection.enableChangePosition) {
                    var _iconCenter = this.parent.options.selection.centerIcon;

                    // On créé une instance d'un objet pour un GMarkerOptions
                    var _centerIcon = new Object();
                    _centerIcon.icon = new GIcon();
                    _centerIcon.icon.iconSize = new GSize(_iconCenter.size.width, _iconCenter.size.height);
                    _centerIcon.icon.iconAnchor = new GPoint(_iconCenter.anchor.x, _iconCenter.anchor.y);
                    _centerIcon.icon.shadowSize = new GSize(0, 0);
                    _centerIcon.zIndexProcess = this.highOrder;
                    _centerIcon.draggable = this.parent.options.selection.enableChangePosition ? true : false;
                    _centerIcon.bouncy = true;

                    // Position du centre du cercle
                    this.centerPosition = center;
                    // On créé le marqueur correspondant
                    var _markerCenter = this.centerHandle = new GMarker(this.centerPosition, _centerIcon);
                    // On ajoute le calcul
                    this.map.addOverlay(this.centerHandle);
                    // On définit l'image
                    this.setIcon(_markerCenter, _iconCenter, false);
                    // On créé des évènements pour le drag
                    GEvent.addListener(this.centerHandle, "drag", function() { _objselection.update(); });
                    GEvent.addListener(this.centerHandle, "dragend", function() { _objselection.update(false, true); });
                    GEvent.addListener(this.centerHandle, "mouseover", function() { _objselection.setIcon(_markerCenter, _iconCenter, true); });
                    GEvent.addListener(this.centerHandle, "mouseout", function() { _objselection.setIcon(_markerCenter, _iconCenter, false); });
                }

                // -----------------------------------------------------------------------------
                //  Icône de redimenssionnement
                // -----------------------------------------------------------------------------

                if (this.parent.options.selection.enableChangeRadius) {
                    var _iconDrag = this.parent.options.selection.dragIcon;

                    // On créé une instance d'un objet pour un GMarkerOptions
                    var _dragIcon = new Object();
                    _dragIcon.icon = new GIcon();
                    _dragIcon.icon.iconSize = new GSize(_iconDrag.size.width, _iconDrag.size.height);
                    _dragIcon.icon.iconAnchor = new GPoint(_iconDrag.anchor.x, _iconDrag.anchor.y);
                    _dragIcon.icon.shadowSize = new GSize(0, 0);

                    _dragIcon.draggable = this.parent.options.selection.enableChangeRadius ? true : false;
                    _dragIcon.bouncy = false;

                    // On définit la position de la poignée de redimensionnement
                    this.dragPosition = this.getPoint(center, 90, this.radius / 1000);
                    // On créé le marqueur correspondant
                    var _markerDrag = this.dragHandle = new GMarker(this.dragPosition, _dragIcon);
                    // On ajoute le calcul
                    this.map.addOverlay(this.dragHandle);
                    // On définit l'image
                    this.setIcon(_markerDrag, _iconDrag, false);
                    // On créé des évènements pour le drag
                    GEvent.addListener(this.dragHandle, "drag", function() { _objselection.update(true); });
                    GEvent.addListener(this.dragHandle, "dragend", function() { _objselection.update(true, true); });
                    GEvent.addListener(this.dragHandle, "mouseover", function() { _objselection.setIcon(_markerDrag, _iconDrag, true); });
                    GEvent.addListener(this.dragHandle, "mouseout", function() { _objselection.setIcon(_markerDrag, _iconDrag, false); });
                }
            } else {
                // Position du centre de la sélection
                this.centerPosition = center;
                this.centerHandle.setLatLng(center);
            }
            // On mets à jour le tracé sur la carte
            this.update();
        },

        highOrder: function(marker, b) {
            return GOverlay.getZIndex(marker.getPoint().lat()) + 1000000;
        },

        update: function(dragHandle, dragEnd) {
            if (this.parent.options.selection.enableSelection) {
                // On arrête l'animation (si il y en a une)
                this.parent.stop();
                // On ferme les fenêtre éventuellement ouverte
                this.parent.window.close();

                if (this.parent.options.selection.enableChangeRadius) {
                    // Si on augmente le rayon
                    if (dragHandle) {
                        // On obtient la position du drag
                        this.dragPosition = this.dragHandle.getPoint();
                        // On redéfinit le rayon
                        this.radius = this.getDistance(this.centerPosition, this.dragPosition) * 1000;
                        // On affiche le tooltip
                        this.showTooltip();
                    }
                    // On définit le point central
                    this.centerPosition = this.centerHandle.getPoint();
                    // On obtient les points du cercle			
                    var _points = this.getPoints();
                    // On déplace la poignée pour agrandir le rayon
                    if (!dragHandle)
                        this.dragPosition = this.dragHandle.setPoint(_points[(this.parent.options.selection.segments / 4)]);
                    // Obient le type
                    var _mapType = this.map.getCurrentMapType()
                    // Détermine la couleur à utiliser pour la carte
                    var _borderColor = (_mapType == G_NORMAL_MAP || _mapType == G_PHYSICAL_MAP) ? this.parent.options.selection.borderColor.normal : this.parent.options.selection.borderColor.inverse;
                    var _fillColor = (_mapType == G_NORMAL_MAP || _mapType == G_PHYSICAL_MAP) ? this.parent.options.selection.fillColor.normal : this.parent.options.selection.fillColor.inverse;
                    // On supprime le calc si existant
                    if (this.polygon != null) this.map.removeOverlay(this.polygon);
                    // Affiche le polygon correspondant au cercle
                    this.polygon = new GPolygon(_points, _borderColor, 1, 1, _fillColor, this.parent.options.selection.fillColor.alpha);
                    // On ajoute le calc
                    this.map.addOverlay(this.polygon);
                    // Si on relache la souris
                    if (dragEnd) {
                        // On efface le tooltip
                        this.hideTooltip();
                        // On obtient le rectangle englobant
                        var _bounds = this.getBounds(_points);
                        // On obtient le level à utiliser
                        var _level = this.map.getBoundsZoomLevel(_bounds);
                        // Si le zoom courant est différent du zoom à utiliser
                        // et si le centre du cercle n'est pas au centre de la page
                        // on recentre sur la page
                        if (this.map.getZoom() != _level && this.map.getCenter() != this.centerHandle.getPoint())
                            this.setRadius(this.radius);
                        // On fixe le niveau de zoom
                        this.map.setZoom(_level);
                    }
                }

                // Si on relache la souris
                if (dragEnd)
                // On appelle le callback
                    this.parent.onChange();
            }
        },

        // On change l'icône
        setIcon: function(marker, icon, state) {
            var _path = this.parent.isSlashed(icon.path);
            // On met à jour l'image de la marque
            marker.setImage(
				(!state || (state && typeof icon.fileOver == 'undefined')) ?
					_path + icon.fileUp :
					_path + icon.fileOver
			);
        },

        // Obtient les points du cercle
        getPoints: function() {
            // On mets à zéro le tableau des points
            var _points = [];
            // On calcul une distance en mètre
            var _distance = this.radius / 1000;
            // Détermine la position des 72 points du cercles.
            for (var _i = 0; _i < this.parent.options.selection.segments; _i++)
                _points.push(this.getPoint(this.centerPosition, _i * 360 / this.parent.options.selection.segments, _distance));
            // Point final (pour le tour)
            _points.push(this.getPoint(this.centerPosition, 0, _distance));
            // Retourne le points qui correspondent au cercle
            return _points;
        },

        // Obtient le rectangle englobant
        getBounds: function(points) {
            return new GLatLngBounds(
            // SW
				new GLatLng(
					points[(this.parent.options.selection.segments / 2)].lat(),
					points[(this.parent.options.selection.segments / 4 * 3)].lng()
				),
            /// NE
				new GLatLng(
					points[0].lat(),
					points[(this.parent.options.selection.segments / 4)].lng()
				)
			);
        },

        // Obtient les données données d'un point du cercle
        getPoint: function(orig, hdng, dist) {
            var _R = 6378.137; // Rayon moyen de la terre en km
            var _oX, _oY;
            var _x, _y;
            var _d = dist / _R;

            hdng = this.rad(hdng);
            _oX = this.rad(orig.x);
            _oY = this.rad(orig.y);

            _y = Math.asin(Math.sin(_oY) * Math.cos(_d) + Math.cos(_oY) * Math.sin(_d) * Math.cos(hdng));
            _x = _oX + Math.atan2(Math.sin(hdng) * Math.sin(_d) * Math.cos(_oY), Math.cos(_d) - Math.sin(_oY) * Math.sin(_y));
            _y = this.deg(_y);
            _x = this.deg(_x);

            return new GLatLng(_y, _x);
        },

        // Obtient la distance entre deux points
        getDistance: function(point1, point2) {
            var _R = 6378.137; // Rayon moyen de la terre en km

            var _lng1 = this.rad(point1.lng());
            var _lng2 = this.rad(point2.lng());
            var _lat1 = this.rad(point1.lat());
            var _lat2 = this.rad(point2.lat());

            var _deltaLat = _lat1 - _lat2;
            var _deltaLng = _lng1 - _lng2;

            var _step1 = Math.pow(Math.sin(_deltaLat / 2), 2) + Math.cos(_lat2) * Math.cos(_lat1) * Math.pow(Math.sin(_deltaLng / 2), 2);
            var _step2 = 2 * Math.atan2(Math.sqrt(_step1), Math.sqrt(1 - _step1));

            return _step2 * _R;
        },

        // Obtient le radius avec une distance en m/km
        getRadius: function() {
            var _radius = this.radius.toFixed();
            if (_radius < 1000) return _radius + '&nbsp;m';
            return (this.radius / 1000).toPrecision(2) + '&nbsp;km';
        },

        // Change le rayon
        setRadius: function(radius) {
            // On redéfinit le radius
            this.parent.options.selection.radius = this.radius = radius;
            // On déplace la marque au centre
            this.centerHandle.setPoint(this.map.getCenter());
            // On mets à jour	
            this.update(false, true);
        },

        getPosition: function() {
            // On récupère la position du point
            var _point = this.centerHandle.getPoint();

            return {
                lat: _point.lat(),
                lng: _point.lng(),
                radius: this.radius
            };
        },

        showTooltip: function() {
            if (this.parent.options.selection.enableTooltip) {
                // Obtient le point de la poignée
                var _dragHandle = this.dragHandle.getPoint();
                // Obtient un zIndex pour la latitude de la poignée
                var _z = GOverlay.getZIndex(_dragHandle.lat());
                // On insert le contenu...
                this.tooltip.innerHTML = '<div class="' + this.parent.options.selection.tooltipClass + '">' + this.getRadius() + '</div>';
                // On indique le zIndex du div
                this.tooltip.style.zIndex = _z;
                // Affiche le tooltip
                this.tooltip.style.display = "";
                // On obtient la position en pixel du point
                var _point = this.map.fromLatLngToDivPixel(_dragHandle);
                // On déplace le div
                this.tooltip.style.left = (_point.x + 12) + "px";
                this.tooltip.style.top = (_point.y - 12) + "px";
            }
        },

        hideTooltip: function() {
            // Efface le tooltip
            this.tooltip.style.display = "none";
        },

        // Convertit en radian
        rad: function(deg) {
            return deg * Math.PI / 180;
        },

        // Convertit en degré
        deg: function(rad) {
            return rad * 180 / Math.PI;
        }
    }
}