Source: api/SpwMap.js

Retour à la documentation
define(["dojo/_base/declare", "dijit/_WidgetBase", "dijit/_TemplatedMixin", "dijit/_WidgetsInTemplateMixin", "dojo/text!./templates/SpwMap.html",
        "esri/SpatialReference", "esri/map", "esri/dijit/Popup", "spw/api/Utils", "spw/api/MessageManager",
        "esri/symbols/SimpleFillSymbol", "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol", "dojo/_base/Color",
        "dojo/Evented", "dojo/_base/lang", "spw/api/ConfigLoader", "dojo/_base/array", "spw/api/MapService", "dojo/on",
        "esri/geometry/Extent", "esri/geometry/Point", "esri/graphicsUtils", "esri/toolbars/navigation","esri/geometry/scaleUtils",
        "spw/api/MapServiceFactory", "esri/geometry/screenUtils", "esri/geometry/ScreenPoint", "dojo/dom-construct",
        "spw/api/ProjectionManager", "dojo/dom-attr", "spw/api/SpwBaseTemplatedWidget", "dojo/Deferred", "esri/config",
        "dojo/sniff", "esri/symbols/PictureMarkerSymbol", "esri/graphic", "esri/InfoTemplate", "dojo/aspect", "dojo/dom-class",
        "dojo/promise/all", "dojo/request/xhr", "spw/api/GeometryUtils",

        "dijit/layout/BorderContainer", "dijit/layout/ContentPane", "dojo/domReady!"],
    function(declare, _WidgetBase, _TemplatedMixin, _WidgetsInTemplateMixin, template,
             SpatialReference, Map, EsriPopup, Utils, MessageManager,
             SimpleFillSymbol, SimpleMarkerSymbol, SimpleLineSymbol, Color,
             Evented, lang, ConfigLoader, array, MapService, on,
             Extent, Point, graphicsUtils,navigation,scaleUtils, MapServiceFactory, screenUtils, ScreenPoint,
             domConstruct, ProjectionManager, domAttr, SpwBaseTemplatedWidget, Deferred, esriConfig, has,
             PictureMarkerSymbol, Graphic, InfoTemplate, aspect, domClass, all, xhr, GeometryUtils) {
        var SpwMap = null;
        SpwMap = declare("spw.api.SpwMap", [SpwBaseTemplatedWidget, Evented], /** @lends spw.api.SpwMap.prototype */{

            widgetId: 'SpwMap',
            widgetTitle: 'Carte',
            sizeLimit: null,

            templateString: template,
            widgetsInTemplate: true,

            /**
             * Objet esri représentant la carte
             * @private
             * @type esri.Map
             */
            esriMap: null,

            /**
             * Référence vers l'instance de spw.api.SpwViewer
             */
            spwViewer: null,

            /**
             * Tableau contenant les spw.api.MapService ajouté sur la carte.
             */
            _mapServices: null,

            /**
             * Curseur de la carte
             */
            _cursor: null,

            /**
             * Curseur actuel de la carte
             */
            _currentCursor: null,

            /**
             * Chemin d'accès sur le serveur pour l'image de chargement
             */
            _loadingImagePath: null,

            /**
             * Indique si la carte est chargée
             */
            loaded: null,

            /**
             * Indique si au moins un service fond de plan est attaché à la carte.
             */
            hasBaseMap: false,

            /**
             * Navigation tool bar d'esri
             */
            _navigationToolBar: null,

            /**
             * Paramètres passés à la map esri
             */
            mapParams: null,

            _servicesAdded: false,

            /**
             * @constructs
             * @extends {dijit._WidgetBase}
             * @classdesc Classe représentant la carte. Elle crée la carte (esri.Map) et interagit avec elle.
             */
            constructor: function() {
                this._mapServices = {};
                this.loaded = false;

                var bundle = this.declaredClass.substr(0, this.declaredClass.lastIndexOf("."));
                this._loadingImagePath = require.toUrl(bundle.replace(".", "/")) + "/images/map-loader.gif";

                //Make events accessible in object instance
                lang.mixin(this, {events: SpwMap.events});
            },

            /**
             * Méthode appelée après la création de la classe
             */
            postCreate: function() {
                this.inherited(arguments);

                this.buildEsriMap();

                var mapLoaderPosition = this.spwViewer.get('mapLoaderPosition');
                if(mapLoaderPosition){
                    if(mapLoaderPosition.top)
                        this.loadingStatusDiv.style.top = mapLoaderPosition.top;
                    if(mapLoaderPosition.right)
                        this.loadingStatusDiv.style.right = mapLoaderPosition.right;
                    if(mapLoaderPosition.left)
                        this.loadingStatusDiv.style.left = mapLoaderPosition.left;
                    if(mapLoaderPosition.bottom)
                        this.loadingStatusDiv.style.bottom = mapLoaderPosition.bottom;
                } else {
                    this.loadingStatusDiv.style.top = "5px";
                    this.loadingStatusDiv.style.right = "5px";
                }

                this.createBaseMap();
            },

            createNavigationBar: function() {
                this._navigationToolBar = new navigation(this.esriMap);
            },

            /**
             * Construit l'objet EsriMap sur base des options passée
             * @param opts options pour la création de la EsriMap
             */
            buildEsriMap: function(opts) {
                opts = opts || {};

                var srid = opts.srid;
                var newContext = opts.ctx;
                var lods = opts.lods;

                this._reAddServices = [];
                this._reAddGraphics = null;

                var extent = null;

                if ((this.esriMap || (lods && this.esriMap))) {
                    for (var key in this._mapServices) {
                        if (this._mapServices.hasOwnProperty(key)) {
                            this._mapServices[key].reset(this.esriMap);
                            this.emit(SpwMap.events.MapServiceRemoved, this._mapServices[key]);

                            if (!newContext && !this._mapServices[key].isBaseMap) {
                                this._reAddServices.push(this._mapServices[key]);
                            }

                            delete this._mapServices[key];
                        }
                    }

                    if (!newContext) {
                        if (this.esriMap.graphics && this.esriMap.graphics.graphics) {
                            this._reAddGraphics = [].concat(this.esriMap.graphics.graphics);

                            array.forEach(this._reAddGraphics, lang.hitch(this, function(g) {
                                this.esriMap.graphics.remove(g);
                            }));
                        }
                    }

                    extent = this.esriMap.extent;
                    this.esriMap.destroy();
                    this.esriMap = null;

                    this.emit(SpwMap.events.MapDestroyed, null);

                    if(this.get('loaded')){
                        this._mapDestroyed = true;
                    }
                    this.set('loaded', false);
                }

                if (srid) {
                    this.spwViewer.set('spatialReference', srid);

                    if (extent) {
                        extent = ProjectionManager.getInstance().transform(extent.spatialReference.wkid, srid, extent);
                    }
                }

                var popup = new EsriPopup({}, domConstruct.create('div'));
                var bbox = (Utils.gua("BBOX") ? array.map(Utils.gua("BBOX").split(","), function(i) {return parseFloat(i);}) : null);
                if(bbox){
                    bbox = new Extent(bbox[0], bbox[2], bbox[1], bbox[3], (srid ? new SpatialReference({wkid: srid}) : this.spwViewer.get('initialSpatialReference')));
                }
                var mapParams = lang.mixin({
                    extent: extent || bbox || this.spwViewer.get('initialExtent'),
                    infoWindow: popup,
                    logo: false,
                    nav: false,
                    isPanArrows: false,
                    slider: false,
                    autoResize: true,
                    showAttribution: true,
                    navigationMode: 'classic'
                }, this.mapParams);

                if(this.spwViewer.get('minScale')){
                    mapParams.minScale = this.spwViewer.get('minScale');
                }

                if(this.spwViewer.get('maxScale')){
                    mapParams.maxScale = this.spwViewer.get('maxScale');
                }

                if (lods) {
                    mapParams.lods = lods;
                }
                
                mapParams.showLabels = true;

                this.spwViewer._prepareLayout();
                this.esriMap = new Map(this.mapRegion, mapParams);

                this.emit(SpwMap.events.MapCreated, this.esriMap);

                this.esriMap.on("load", lang.hitch(this, this.mapLoaded));
                this.esriMap.on("extent-change", lang.hitch(this, this.mapExtentChanged));
                this.esriMap.on("zoom", lang.hitch(this, this.mapZoom));
                this.esriMap.on("zoom-start", lang.hitch(this, this.mapZoomStart));
                this.esriMap.on("zoom-end", lang.hitch(this, this.mapZoomEnd));
                this.esriMap.on("pan-end", lang.hitch(this, this.mapPanEnd));
                this.esriMap.on("mouse-move", lang.hitch(this, this.mapMouseMove));
                this.esriMap.on("dbl-click", lang.hitch(this, this.mapMouseDblClicked));
                this.esriMap.on("click", lang.hitch(this, function(e){
                    this.emit(SpwMap.events.MapClicked, e.mapPoint.x, e.mapPoint.y,
                        e.mapPoint.spatialReference ? e.mapPoint.spatialReference.wkid : this.spwViewer.get('spatialReference').wkid,
                        e);
                }));
                this.esriMap.on("layer-reorder", lang.hitch(this, function(e){
                    this.emit(SpwMap.events.LayerReorder, e);
                }));
                this.esriMap.on("layer-add", lang.hitch(this, function(e){
                    this.emit(SpwMap.events.LayerAdd, e);
                }));
                this.esriMap.on("update-start", lang.hitch(this, function() {
                    this._updating = true;
                    this.showMapLoading();
                }));
                this.esriMap.on("update-end", lang.hitch(this, function() {
                    this._updating = false;
                    this.hideMapLoading();
                }));
                this.createNavigationBar();
            },

            /**
             * Copie les services présents dans la carte passée en paramètre
             * @param fromMap la carte dont il faut copier les services
             */
            copyServices: function(fromMap) {
                if (!this.loaded) {
                    on.once(this, SpwMap.events.MapLoaded, lang.hitch(this, this.copyServices, fromMap));
                    return;
                }

                var mss = fromMap.getMapServices({
                    visible: true,
                    isBaseMap: false
                });

                array.forEach(mss, lang.hitch(this, function(ms) {
                    this.addMapService(ms.getServiceConfig(), true);
                }));
            },

            /**
             * Initialise les handlers pour les différents MapEvents
             * @param map référence à SpwMap
             * @return un tableau contenant les handlers
             */
            mapEvents: function(map) {
                var handlers = [];

                handlers.push(this.esriMap.on("load", lang.hitch(map, map.mapLoaded)));
                handlers.push(this.esriMap.on("extent-change", lang.hitch(map, map.mapExtentChanged)));
                handlers.push(this.esriMap.on("zoom", lang.hitch(map, map.mapZoom)));
                handlers.push(this.esriMap.on("zoom-start", lang.hitch(map, map.mapZoomStart)));
                handlers.push(this.esriMap.on("zoom-end", lang.hitch(map, map.mapZoomEnd)));
                handlers.push(this.esriMap.on("pan-end", lang.hitch(map, map.mapPanEnd)));
                handlers.push(this.esriMap.on("mouse-move", lang.hitch(map, map.mapMouseMove)));
                handlers.push(this.esriMap.on("dbl-click", lang.hitch(map, map.mapMouseDblClicked)));
                handlers.push(this.esriMap.on("click", lang.hitch(map, function(e){
                    this.emit(SpwMap.events.MapClicked, e.mapPoint.x, e.mapPoint.y,
                        e.mapPoint.spatialReference ? e.mapPoint.spatialReference.wkid : this.spwViewer.get('spatialReference').wkid,
                        e);
                })));
                handlers.push(this.esriMap.graphics.on("mouse-down", lang.hitch(this, function(e) {
                    this.emit(SpwMap.events.GraphicsMouseDown, e);
                })));
                handlers.push(this.esriMap.graphics.on("mouse-up", lang.hitch(this, function(e) {
                    this.emit(SpwMap.events.GraphicsMouseUp, e);
                })));
                handlers.push(this.esriMap.on("layer-reorder", lang.hitch(map, function(e){
                    this.emit(SpwMap.events.LayerReorder, e);
                })));
                handlers.push(this.esriMap.on("layer-add", lang.hitch(map, function(e){
                    this.emit(SpwMap.events.LayerAdd, e);
                })));
                handlers.push(this.esriMap.on("update-start", lang.hitch(map, function() {
                    this.showMapLoading();
                })));
                handlers.push(this.esriMap.on("update-end", lang.hitch(map, function() {
                    this.hideMapLoading();
                })));

                return handlers;
            },

            /**
             * Appelée lorsque la carte est chargée
             */
            mapLoaded: function() {
                // graphics disponible après le onLoad
                this.esriMap.graphics.on("mouse-down", lang.hitch(this, function(e) {
                    this.emit(SpwMap.events.GraphicsMouseDown, e);
                }));
                this.esriMap.graphics.on("mouse-up", lang.hitch(this, function(e) {
                    this.emit(SpwMap.events.GraphicsMouseUp, e);
                }));
                this.esriMap.graphics.on("click", lang.hitch(this, function(evt) {
                    this.emit(SpwMap.events.GraphicsClicked, evt);
                }));

                this.emit(SpwMap.events.FirstServiceAdded, this);

                if (this._reAddServices && this._reAddServices.length > 0) { // changement de projection
                    this._reAddServices.sort(function(a, b) {
                        if (a.order == null) {
                            return b.order == null ? 1 : -1;
                        }
                        else if (b.order == null) {
                            return 1;
                        }

                        return +(a.order) - +(b.order);
                    });

                    array.forEach(this._reAddServices, lang.hitch(this, function(service) {
                    	var inTocServices = {}, visibleServices = {};
                    	for(var k in service.mapServiceLayers) {
                    		inTocServices[k] = service.mapServiceLayers[k].get('inTOC');
                    		visibleServices[k] = service.mapServiceLayers[k].get('visible');
                    	}
                    	service.addToMap(this.esriMap);
                        this._mapServices[service.serviceId] = service;
                        for(var k in visibleServices) {
                        	service.mapServiceLayers[k].set('defaultVisibility', visibleServices[k]);
                        }
                        if(service.loaded){
                            for(var k in inTocServices) {
                            	service.mapServiceLayers[k].set('inTOC', inTocServices[k]);
                            	service.mapServiceLayers[k].set('visible', visibleServices[k]);
                            	service.mapServiceLayers[k].set('visibility', visibleServices[k]);
                            }
                        } else {
                        	service.on(this.events.MapServiceLoaded, lang.hitch(this, function(){
                                for(var k in inTocServices) {
                                	service.mapServiceLayers[k].set('inTOC', inTocServices[k]);
                                	service.mapServiceLayers[k].set('visible', visibleServices[k]);
                                	service.mapServiceLayers[k].set('visibility', visibleServices[k]);
                                }
                                service.refreshLayersVisibility();
                        	}));
                        }
                        this.emit(SpwMap.events.MapServiceAdded, service);
                    }));
                }
                else if (!this._mapDestroyed || !this._servicesAdded) {
                    this.createServices();
                }

                array.forEach(this._reAddGraphics, lang.hitch(this, function(g) {
                    var srid = lang.getObject('geometry.spatialReference.wkid', false, g);

                    if (srid && srid != this.getSpatialReferenceSRID()) {
                        g.setGeometry(ProjectionManager.getInstance().transform(srid, this.getSpatialReferenceSRID(), g.geometry));
                    }

                    this.esriMap.graphics.add(g);
                }));

                this._mapDestroyed = false;
                this.set('loaded', true);
                this.emit(SpwMap.events.MapLoaded, this);
            },

            /**
             * Affiche le loader sur la carte
             */
            showMapLoading: function() {
                if (this.loadingStatusDiv && this.loadingStatusDiv.style) {
                    this.loadingStatusDiv.style.display = "block";
                }
                this.emit(this.events.MapUpdateStart);
            },

            /**
             * Cache le loader sur la carte
             */
            hideMapLoading: function() {
                if (this.loadingStatusDiv && this.loadingStatusDiv.style) {
                    this.loadingStatusDiv.style.display = "none";
                }
                this.emit(this.events.MapUpdateEnd);
            },

            /**
             * Démarre la carte
             */
            startup: function() {
                this.inherited(arguments);
            },

            /**
             * Appelée lorsque l'extent de la carte change
             * @param extentEvent
             */
            mapExtentChanged: function(extentEvent) {
                var extent = extentEvent.extent;

                if (isNaN(extent.xmin) || isNaN(extent.xmax) || isNaN(extent.ymin) || isNaN(extent.ymax)) {
                    return;
                }

                if (extent.spatialReference.wkid !== this.spwViewer.get('initialSpatialReference').wkid) {
                    extent = ProjectionManager.getInstance().transform(extent.spatialReference.wkid, this.spwViewer.get('initialSpatialReference').wkid, extent);
                }

                Utils.sua("BBOX", extent.xmin + "," + extent.xmax + "," + extent.ymin + "," + extent.ymax);
                this.emit(SpwMap.events.MapExtentChanged, extentEvent);
            },

            /**
             * Emet l'événement mapZoom
             */
            mapZoom: function(zoomEvent){
                this.emit(SpwMap.events.MapZoom, zoomEvent);
            },

            /**
             * Emet l'événement mapZoomStart
             */
            mapZoomStart: function(zoomEvent){
                this.emit(SpwMap.events.MapZoomStart, zoomEvent);
            },

            /**
             * Emet l'événement mapZoomEnd
             */
            mapZoomEnd: function(zoomEvent){
                this.emit(SpwMap.events.MapZoomEnd, zoomEvent);
            },

            /**
             * Emet l'événement mapPanEnd
             */
            mapPanEnd: function(panEvent){
                this.emit(SpwMap.events.MapPanEnd, panEvent);
            },

            /**
             * Emet l'événement mapMouseMove
             */
            mapMouseMove: function(mouseEvent){
                this.emit(SpwMap.events.MapMouseMove, mouseEvent);
            },

            /**
             * Emet l'événement mapMouseDblClicked
             */
            mapMouseDblClicked: function(mouseEvent) {
                this.emit(SpwMap.events.MapDblClicked, mouseEvent);
            },

            /**
             * Récupère l'échelle courante de la carte
             * @returns {Number} scale
             */
            getCurrentScale: function() {
                return this.get('esriMap').getScale();
            },

            /**
             * Récupère l'extent courant de la carte
             * @returns {Array.<Number>}
             */
            getCurrentExtent: function(){
                return this.get('esriMap').extent;
            },

            /**
             * Permet de déterminer si la carte est en train d'être mise à jour ou pas
             */
            isUpdating: function() {
                return this._updating === true;
            },

            /**
             * Retourne la largeur de la esriMap
             */
            getWidth: function() {
                return this.get('esriMap').width;
            },

            /**
             * Retourne la hauteur de la esriMap
             */
            getHeight: function() {
                return this.get('esriMap').height;
            },

            pointToExtent: function(x, y) {
                return screenUtils.toMapGeometry(this.getCurrentExtent(), this.getWidth(), this.getHeight(), new ScreenPoint(x, y));
            },

            /**
             * Retourne le niveau de la esriMap
             */
            getLevel: function(){
                return this.get('esriMap').getLevel();
            },

            /**
             * Met à jour le niveau de la esriMap
             * @param level le nouveau niveau
             */
            setLevel: function(level){
                return this.get('esriMap').setLevel(level);
            },

            /**
             * Met à jour le niveau de zoom de la esriMap
             * @param level le nouveau niveau de zoom
             */
            setZoom: function(zoom) {
                return this.get('esriMap').setZoom(zoom);
            },

            /**
             * Retourne le niveau de zoom de la esriMap
             */
            getZoom: function() {
                return this.get('esriMap').getZoom();
            },

            /**
             * Met à jour l'échelle de la esriMap
             * @param scale la nouvelle échelle
             */
            setScale: function(scale){
                return this.get('esriMap').setScale(scale);
            },

            /**
             * Retourne l'extent de la carte à une échelle donnée
             * @param scale l'échelle à laquelle on veut l'extent
             */
            getExtentForScale: function(scale){
                scale = scale || this.getCurrentScale();
                return scaleUtils.getExtentForScale(this.get('esriMap'), scale);
            },

            /**
             * Ajoute un service sur la carte
             * @param service
             * @returns l'instance du spw.api.MapService
             */
            addMapService: function(service, ignoreDupplicate) {
                if (service) {
                    var mapService = this.getMapServiceById(service.serviceId);
                    if (mapService == null) {

                        lang.mixin(service, {
                            spwMap: this
                        });
                        if (service && !service.deleted) {
                            mapService = MapServiceFactory.createService(service);
                            this._mapServices[mapService.get('serviceId')] = mapService;
                        } else {
                            return null;
                        }
                    } else if (ignoreDupplicate !== true) {
                        MessageManager.getInstance().notifyError("Le service existe déjà dans la liste. Il n'a dès lors pas été ajouté.");
                        console.error("Le serviceId " + service.serviceId + " existe déjà.");
                    }

                    var layer = mapService.get('layer');

                    if (layer != null) {
                        mapService.addToMap(this.esriMap);
                        this.emit(SpwMap.events.MapServiceAdded, mapService);
                    } else {
                        MessageManager.getInstance().notifyError("Impossible d'afficher le service : " + service.id);
                    }
                    return mapService;
                } else {
                    MessageManager.getInstance().notifyError("Le service à ajouter n'est pas défini.");
                }
            },

            /**
             * Supprimer un service de la carte sur base du serviceId
             * @param serviceId l'identifiant du MapService à supprimer de la carte
             */
            removeMapService: function(serviceId, ignoreError) {
                try {
                    var mapService = this.getMapServiceById(serviceId);
                    if (mapService == null) {
                        if (ignoreError == null || !ignoreError) {
                            MessageManager.getInstance().notifyError("Le service n'a pas été supprimé car il n'est pas connu par la carte.");
                        }
                        return false;
                    } else {
                        mapService.removeFromMap(this.esriMap);
                        delete this._mapServices[serviceId];
                        this.emit(SpwMap.events.MapServiceRemoved, mapService);
                        mapService.destroy();
                        return true;
                    }
                }
                catch (e) {
                    console.error(e);

                    if (ignoreError !== true) {
                        throw e;
                    }
                }
                return false;
            },

            /**
             * Réordonne un MapService sur base de son identifiant.
             * @param serviceId l'identifiant du service à déplacer
             * @param index le nouvel index du service
             */
            setMapServiceOrder: function(serviceId, index) {
                var mapService = this.getMapServiceById(serviceId);
                if (mapService == null) {
                    MessageManager.getInstance().notifyError("Le service n'a pas été réordonné car il n'est pas connu par la carte.");
                } else {
                    this.esriMap.reorderLayer(mapService.get('layer'), index);
                }
            },

            /**
             * Réordonne les mapServices de la carte en plaçant systématiquement les fonds de plan à l'arrière plan.
             * @param {Array.<Number>} mapServicesId tableau avec les identifiant des services à réordonner.
             */
            setAllMapServiceNotBaseMapOrder: function(mapServicesId) {

                var index = this.getBaseMapLayerCount();
                var nbrOfMapServices = this.getMapServices({isBaseMap: false});
                if(nbrOfMapServices.length == mapServicesId.length){
                    array.forEach(mapServicesId,lang.hitch(this,function(mapServiceId){
                        var mapService = this.getMapServiceById(mapServiceId);
                        if(mapService){
                            index ++;
                            this.esriMap.reorderLayer(mapService.get('layer'), index);

                            var additionnalLayers = mapService.getAdditionnalLayers();
                            array.forEach(additionnalLayers,lang.hitch(this,function(additionnalLayer){
                                index ++;
                                this.esriMap.reorderLayer(additionnalLayer, index);
                            }));
                        }
                    }));
                }

            },

            /**
             * Récupère un spw.api.MapService sur base du serviceId.
             * @param serviceId l'identifiant du service
             * @returns {spw.api.MapService}
             */
            getMapServiceById: function(serviceId) {
                return this._mapServices[serviceId];
            },

            /**
             * Récupère un ou plusieurs MapService sur base d'un queryObject
             * @param serviceQuery Objet de mapping clé-valeur sur base duquel effectuer la comparaison avec les services.
             * @returns {Array} Les MapServices correspondant au queryObject passé.
             */
            getMapServices: function(serviceQuery) {
                var mapServices = [];

                for (var key in this._mapServices) {
                    var mapService = this._mapServices[key];
                    var serviceQueryValid = true;
                    if (serviceQuery) {
                        for (prop in serviceQuery) {
                            if (mapService.get(prop) != serviceQuery[prop]) {
                                serviceQueryValid = false;
                                break;
                            }
                        }
                    }
                    if (serviceQueryValid) {
                        mapServices.push(mapService);
                    }
                }

                var sorted = [];
                var idsAdded = [];

                array.forEach(mapServices, function(service){
                    if(service.inError){
                        sorted.push(lang.mixin(service, {
                            first: false,
                            last: false
                        }));
                        idsAdded.push(service.serviceId);
                    }
                });

                var first = true;

                array.forEach(this.esriMap.layerIds, lang.hitch(this, function(id, idx){
                    array.forEach(mapServices, lang.hitch(this, function(service){
                        if(service.type === 'TIMER'){
                            array.forEach(service.mapServices, lang.hitch(this, function(st){
                                var stId = st.layer && st.layer.id ? st.layer.id : st.serviceId;
                                if(stId == id && idsAdded.indexOf(service.serviceId) < 0){
                                    sorted.push(lang.mixin(service, {
                                        first: first,
                                        last: false
                                    }));
                                    first = false;
                                    idsAdded.push(service.serviceId);
                                }
                            }));
                        } else {
                            var testId = service.layer && service.layer.id ? service.layer.id : service.serviceId;
                            if(testId == id && idsAdded.indexOf(id) < 0){
                                sorted.push(lang.mixin(service, {
                                    first: first,
                                    last: false
                                }));
                                first = false;
                            }
                        }
                    }));
                }));

                if (sorted[sorted.length-1]) {
                    sorted[sorted.length-1].last = true;
                }

                first = true;

                array.forEach(this.esriMap.graphicsLayerIds, lang.hitch(this, function(id, idx){
                    array.forEach(mapServices, lang.hitch(this, function(service){
                        if(service.serviceId == id && idsAdded.indexOf(id) < 0){
                            sorted.push(lang.mixin(service, {
                                first: first,
                                last: false
                            }));
                            first = false;
                        }
                    }));
                }));

                if (sorted[sorted.length-1]) {
                    sorted[sorted.length-1].last = true;
                }

                return sorted.reverse();
            },

            /**
             * Place un élément UI sur la carte (widget, html, ...)
             * @param obj l'objet à afficher sur la carte
             */
            placeIn: function(obj) {
                var domElement = obj.domNode ? obj.domNode : obj;

                if (typeof obj.mapPosition != 'undefined') {
                    domElement.style.position = "absolute";
                    if (typeof obj.mapPosition.top != 'undefined') {
                        domElement.style.top = obj.mapPosition.top;
                    }
                    if (typeof obj.mapPosition.bottom != 'undefined') {
                        domElement.style.bottom = obj.mapPosition.bottom;
                    }
                    if (typeof obj.mapPosition.left != 'undefined') {
                        domElement.style.left = obj.mapPosition.left;
                    }
                    if (typeof obj.mapPosition.right != 'undefined') {
                        domElement.style.right = obj.mapPosition.right;
                    }
                }

                domElement.style['z-index'] = 1;

                if(obj && typeof(obj.placeAt) === 'function'){
                    obj.placeAt(this.mapRegion);
                } else {
                    this.mapRegion.appendChild(domElement);
                }
            },

            /**
             * Cache l'info window Esri
             */
            hideInfoWindow: function() {
                this.esriMap.infoWindow.hide();
            },

            /**
             * Redimensionne l'infoWindow Esri
             * @param width
             * @param height
             */
            resizeInfoWindow: function(width, height) {
                this.esriMap.infoWindow.resize(width, height);
            },

            /**
             * Affiche l'infoWindow Esri sur la carte
             * @param title Titre de l'infoWindow
             * @param content contenu de l'infoWindow
             * @param point Coordonnée du point sur la carte où l'infoWindow doit pointer.
             */
            showInfoWindowAt: function(title, content, point) {
                this.esriMap.infoWindow.setTitle(title);
                this.esriMap.infoWindow.setContent(content);
                this.esriMap.infoWindow.show(point, this.esriMap.getInfoWindowAnchor(point));
            },

            /**
             * Crée les services fonds de plan de la carte. Cette méthode démarre l'initialisation de la carte.
             */
            createBaseMap: function(newContext) {
                var baseMapConfig = ConfigLoader.getInstance().get("baseMap");

                try {
                    var baseMapsToAdd = [];

                    array.forEach(baseMapConfig/*.reverse()*/, lang.hitch(this, function(baseMap) {
                        if (baseMap.selected) {
                            baseMapsToAdd.splice(0, 0, baseMap);
                        }
                        else {
                            baseMapsToAdd.push(baseMap);
                        }
                    }));

                    this.createBaseMapsFromConfig(baseMapsToAdd, newContext);
                } catch(err){
                    console.error(err);
                    console.log("baseMap error :" + err);
                }
            },

            /**
             * Crée les services fonds de plan sur base de la configuration.
             * @param baseMap configuration des baseMaps.
             */
            watchLoaded: null,

            createBaseMapsFromConfig: function(baseMaps, newContext, currentIdx, def) {
                /*if (!this.get('loaded')) {
                    on.once(this, SpwMap.events.FirstServiceAdded, lang.hitch(this, this.createBaseMapsFromConfig, baseMap));
                    return;
                }*/
                array.forEach(this.getMapServices(), lang.hitch(this, function(s) {
                    s.set('legendLoaded', false);
                }));

                def = def || new Deferred();

                currentIdx = currentIdx || 0;

                if (currentIdx >= baseMaps.length) {
                    MessageManager.getInstance().displayLoadingError("Aucun fond de plan n'a pas pu être chargé. Le viewer ne peut pas s'initialiser.",
                        this.spwViewer.errorMailToAdress,
                        this.spwViewer.errorMailSubject
                    );
                    def.reject();
                    return def;
                }

                var baseMap = baseMaps[currentIdx];

                var curWkid = this.esriMap.spatialReference ? this.esriMap.spatialReference.wkid : baseMap.srid ? baseMap.srid : this.spwViewer.get('spatialReference').wkid;

                if (baseMap.srid && baseMap.srid !== curWkid) {
                    // Il faut détruire et reconstruire la carte...
                    this.buildEsriMap({
                        srid: baseMap.srid,
                        ctx: newContext,
                        lods: baseMap.lods
                    });
                }
                else if (baseMap.srid == null && this.spwViewer.get('spatialReference').wkid !== this.spwViewer.get('initialSpatialReference').wkid) {
                    this.buildEsriMap({
                        srid: this.spwViewer.get('initialSpatialReference').wkid,
                        ctx: newContext,
                        lods: baseMap.lods
                    });
                }
                else {
                    if (baseMap.srid && this.esriMap && this.esriMap.extent && this.esriMap.extent.spatialReference && this.esriMap.extent.spatialReference.wkid !== baseMap.srid) {
                        this.esriMap.extent = ProjectionManager.getInstance().transform(this.esriMap.extent.spatialReference.wkid, baseMap.srid, this.esriMap.extent);
                        this.spwViewer.set('spatialReference', baseMap.srid);
                    }
                    var esriMapParams = {
                        ctx: newContext,
                        srid: baseMap.srid
                    };
                    if (baseMap.lods) {
                        esriMapParams.lods = baseMap.lods;
                        this.buildEsriMap(esriMapParams);
                    }
                }

                var services = [];

                array.forEach(baseMap.services, lang.hitch(this, function(service, idx) {
                    if (baseMap.activateBlackAndWhite === true && baseMap.blackAndWhiteByDefault){
                        var bwService = lang.clone(service);

                        bwService.serviceId = "black-and-white-" + service.serviceId;
                        bwService.url = service.urlBlackAndWhite ? service.urlBlackAndWhite : service.url;
                        services.push(this.createMapServiceBaseMap(bwService, true));

                        if(idx == 0){
                            services[0].on(MapService.events.MapServiceError, lang.hitch(this, function(mapService, error) {
                                if(!mapService || !mapService.layer || !mapService.layer.loaded) {
                                    this.removeBaseMaps();

                                    if (esriConfig.defaults.io.corsDetection) {
                                        esriConfig.defaults.io.corsDetection = false;
                                        esriConfig.defaults.io.alwaysUseProxy = true;
                                        esriConfig.defaults.io._oldCorsEnabledServers = esriConfig.defaults.io.corsEnabledServers;
                                        esriConfig.defaults.io.corsEnabledServers = [];
                                        this.createBaseMapsFromConfig(baseMaps, newContext, currentIdx, def);
                                    }
                                    else {
                                        esriConfig.defaults.io.corsDetection = true;
                                        esriConfig.defaults.io.alwaysUseProxy = false;
                                        esriConfig.defaults.io.corsEnabledServers = esriConfig.defaults.io._oldCorsEnabledServers;
                                        this.createBaseMapsFromConfig(baseMaps, newContext, currentIdx + 1, def);
                                    }

                                    console.error(error);
                                } else {
                                    console.error("Erreur avec le service : ", mapService);
                                    def.resolve(currentIdx);
                                }
                            }));
                        }
                    }
                    else {
                        if (baseMap.activateBlackAndWhite === 'filter' && baseMap.blackAndWhiteByDefault) {
                            service.grayscale = 100;
                        }

                        services.push(this.createMapServiceBaseMap(service, true));

                        if(idx == 0){
                            services[0].on(MapService.events.MapServiceError, lang.hitch(this, function(mapService, error){
                                if(!mapService || !mapService.layer || !mapService.layer.loaded) {
                                    this.removeBaseMaps();

                                    if (esriConfig.defaults.io.corsDetection) {
                                        esriConfig.defaults.io.corsDetection = false;
                                        esriConfig.defaults.io.alwaysUseProxy = true;
                                        esriConfig.defaults.io._oldCorsEnabledServers = esriConfig.defaults.io.corsEnabledServers;
                                        esriConfig.defaults.io.corsEnabledServers = [];
                                        this.createBaseMapsFromConfig(baseMaps, newContext, currentIdx, def);
                                    }
                                    else {
                                        esriConfig.defaults.io.corsDetection = true;
                                        esriConfig.defaults.io.alwaysUseProxy = false;
                                        esriConfig.defaults.io.corsEnabledServers = esriConfig.defaults.io._oldCorsEnabledServers;
                                        this.createBaseMapsFromConfig(baseMaps, newContext, currentIdx + 1, def);
                                    }

                                    console.error(error);
                                } else {
                                    console.error("Erreur avec le service : ", mapService, error);
                                    def.resolve(currentIdx);
                                }
                            }));
                        }
                    }
                }));

                if (baseMap.optionalServices && baseMap.optionalServices.length > 0) {
                    array.forEach(baseMap.optionalServices, lang.hitch(this, function(service) {
                        // l'alpha d'un fond de plan n'est pas forcément à 100%
                        service.alpha = (baseMap.alpha / 100) * (!isNaN(service.alpha) ? service.alpha : 100);

                        if(baseMap.activateBlackAndWhite === true && baseMap.blackAndWhiteByDefault){
                            var bwService = lang.clone(service);
                            bwService.serviceId = "black-and-white-" + service.serviceId;
                            bwService.url = service.urlBlackAndWhite ? service.urlBlackAndWhite : service.url;
                            services.push(this.createMapServiceBaseMap(bwService,  bwService.checked ? true : false));
                        } else {
                            if (baseMap.activateBlackAndWhite === 'filter' && baseMap.blackAndWhiteByDefault) {
                                service.grayscale = 100;
                            }

                            services.push(this.createMapServiceBaseMap(service, service.checked ? true : false));
                        }
                    }));
                }

                if(!this.esriMap.loaded) {
                    var handler = this.on(SpwMap.events.FirstServiceAdded, lang.hitch(this, function(evt){
                        if(handler){ handler.remove(); }

                        if (services[0].get('inError')) {
                            return;
                        }

                        if(!this.get("hasBaseMap")){
                            services.splice(0, 1);
                            //services.reverse();
                            array.forEach(services, lang.hitch(this, function(s, idx){
                                if(this.esriMap) this.esriMap.addLayer(s.get('layer'), idx+1);
                            }));
                            this.set("hasBaseMap", true);
                        }

                        def.resolve(currentIdx);
                    }));
                }
                else {
                    var handler = this.esriMap.on("layer-add-result", lang.hitch(this, function(evt){
                        if(evt.layer == services[0].get('layer') && this.esriMap){
                            if(handler){ handler.remove(); }
                            if(evt.error != null){/*TODO*/}
                            if(!this.get("hasBaseMap")){
                                this.set("hasBaseMap", true);

                                services.splice(0, 1);
                                array.forEach(services, lang.hitch(this, function(s, idx){
                                    if(this.esriMap) this.esriMap.addLayer(s.get('layer'), idx+1);
                                }));
                            }

                            def.resolve(currentIdx);
                        }
                    }));
                }

                //services.reverse();
                array.forEach([services[0]], lang.hitch(this, function(s){s.addToMap(this.esriMap);}));
                //services.reverse();
                return def;
            },

            /**
             * Crée les MapServices de type baseMap correspondant à la configuration
             * @param service
             * @param visible
             * @returns
             */
            createMapServiceBaseMap: function(service, visible){
                lang.mixin(service, {
                    visible: visible,
                    isBaseMap: true,
                    inTOC: false
                });

                var mapService = this.getMapServiceById(service.serviceId);

                if (mapService == null) {
                    lang.mixin(service, {spwMap: this});
                    mapService = MapServiceFactory.createService(service);
                    this._mapServices[mapService.get('serviceId')] = mapService;
                }

                return mapService;
            },

            /**
             * Ajoute un fond de plan sur la carte
             * @param service le service fond de plan à ajouter
             * @param visible indique si le fond de plan à ajouter doit être visible ou non.
             * @returns le spw.api.MapService du type baseMap
             */
            addBaseMap: function(service, visible){
                var baseMapIndex = this.getBaseMapLayerCount();

                lang.mixin(service, {
                    visible: visible,
                    isBaseMap: true,
                    inTOC: false
                });

                var mapService = this.getMapServiceById(service.serviceId);

                if (mapService == null) {
                    lang.mixin(service, {
                        spwMap: this
                    });

                    mapService = MapServiceFactory.createService(service);

                    this._mapServices[mapService.get('serviceId')] = mapService;
                }

                mapService.checked = service.checked;

                var layer = mapService.get('layer');

                if (layer != null && this.esriMap) {
                    this.esriMap.addLayer(layer, baseMapIndex);
                }

                return mapService;
            },

            /**
             * Lit un GeoJSON passé en paramètre et l'ajoute sur la carte.
             * @param resource le GeoJSON à ajouter (une URL sur laquelle aller chercher le fichier, un GeoJSON
             * au format texte ou le GeoJSON déjà parsé
             * @param options un ensemble d'options à appliquer sur la couche: {forceColorCode, mapServiceConfig}
             * @returns une promesse de l'ajout du service sur la carte
             */
            addServiceFromGeoJSONResource: function(resource, options) {
                var serviceAddedDef = new Deferred();
                if (typeof resource == 'string') {
                    if (resource.startsWith('[') || resource.startsWith('{')) {
                        this.parseGeoJSONData(JSON.parse(resource), options, serviceAddedDef);
                    } else {
                        xhr(resource, { handleAs : "json", sync: true }).then(
                            lang.hitch(this, function(json) {
                                this.parseGeoJSONData(json, options, serviceAddedDef);
                            }),
                            lang.hitch(this, function(err) {
                                MessageManager.getInstance().notifyError("Impossible de charger le fichier JSON, vérifiez votre URL");
                            })
                        )
                    }
                } else {
                    this.parseGeoJSONData(resource, options, serviceAddedDef);
                }

                return serviceAddedDef;
            },

            parseGeoJSONData: function(GeoJSON, options, serviceAddedDef) {
                options = options ? options : {};
                var forceColorCode = options.forceColorCode;
                var service = {
                    label: 'New layer',
                    serviceId: Utils.guid(),
                    type: 'DRAW_LAYER',
                    visible: true,
                    hasLegend: false,
                    identifiable: true,
                    showTemplate: false,
                    toLoad: true,
                    style: forceColorCode ? {fillColor: forceColorCode}: {},
                    editable: false
                };
                var service = lang.mixin(service, options.mapServiceConfig);

                if (Array.isArray(GeoJSON)) {
                    var defs = [];
                    array.forEach(GeoJSON, lang.hitch(this, function(layerConfig) {
                        var serviceConfig = lang.mixin({}, service);
                        serviceConfig.geoJsonInfo = layerConfig;
                        serviceConfig.label = layerConfig.layer ? layerConfig.layer : serviceConfig.label;
                        serviceConfig.serviceId += Utils.guid();
                        defs.push(this.addDataToMapFromConfig(serviceConfig));
                    }))
                    all(defs).then(
                        lang.hitch(this, function(r) {
                            if (r && r.length > 0) {
                                var extents = array.map(r, function(s) {
                                    var ex = s.layer.getExtent();
                                    if (ex) {
                                        return new Extent(ex);
                                    }
                                    return null;
                                });
                                var union = extents[0];
                                array.forEach(extents, lang.hitch(this, function(e) {
                                    if (e) {
                                        union = union.union(e);
                                    }
                                }));
                                if (union) {
                                    this.spwViewer.get('spwMap').zoomToExtent(union.getExtent());
                                }
                            }
                            serviceAddedDef.resolve(r);
                        }),
                        lang.hitch(this, function(err) {
                            serviceAddedDef.reject();
                        })
                    )
                } else {
                    service.geoJsonInfo = GeoJSON;
                    this.addDataToMapFromConfig(service).then(
                        lang.hitch(this, function(r) {
                            var extent = r.layer.getExtent ? r.layer.getExtent() : null;
                            if (extent) {
                                this.zoomToExtent(extent);
                            }
                            serviceAddedDef.resolve(r);
                        }),
                        lang.hitch(this, function(err) {
                            serviceAddedDef.reject();
                        })
                    );
                }
            },

            addDataToMapFromConfig: function(service) {
                var serviceAdded = new Deferred();
                var mapService = null;

                try {
                    mapService = this.addMapService(service);
                    if (mapService.loaded) {
                        if (mapService.get('valuesIgnored')) {
                            MessageManager.getInstance().notifyWarning('Certaines entrées ont été ignorées à cause de coordonnées erronées ou absentes.');
                        }
                        serviceAdded.resolve(mapService);
                    }
                    else {
                        var handler = null;

                        this.own(
                            handler = on(mapService, mapService.events.MapServiceLoaded, lang.hitch(this, function(m) {
                                if (mapService.get('valuesIgnored')) {
                                    MessageManager.getInstance().notifyWarning('Certaines entrées ont été ignorées à cause de coordonnées erronées ou absentes.');
                                }

                                if (handler) {
                                    handler.remove();
                                }

                                serviceAdded.resolve(mapService);

                            }))
                        );
                    }
                }
                catch (e) {
                    if (e instanceof RangeError) {
                        MessageManager.getInstance().notifyError('Votre fichier est trop volumineux.');
                    }
                    else {
                        console.error(e);
                        MessageManager.getInstance().notifyError('Une erreur est survenue lors de la lecture du fichier. Veuillez vérifier le format de celui-ci et réessayer ou contactez un administrateur.');
                    }
                    this.removeMapService(service.serviceId, true);
                    serviceAdded.reject(service);
                }
                return serviceAdded;
            },


            /**
             * Supprime tous les fonds de plan de la carte.
             */
            removeBaseMaps: function(){
                var mapServices = this.getMapServices({isBaseMap: true});
                array.forEach(mapServices, lang.hitch(this, function(mapService){
                    var layer = mapService.get('layer');
                    if (layer != null) {
                        this.esriMap.removeLayer(layer);
                    }
                    delete this._mapServices[mapService.get('serviceId')];
                    //this.emit(SpwMap.events.MapServiceRemoved, mapService);
                    mapService.destroy();
                }));
                this.set('hasBaseMap', false);
            },

            editService: function(service) {
                if (service == null) {
                    return;
                }

                this.emit(this.events.EditMapService, service);
            },

            /**
             * Récupère le nombre de baseMaps actuellement attachées à la carte.
             * @returns {Number} Le nombre de fonds de plan
             */
            getBaseMapLayerCount: function() {
                var baseMapServices = this.getMapServices({ isBaseMap: true });

                return baseMapServices.length;
            },

            /**
             * Crée tous les services définis dans la configuration du catalogue.
             */
            createServices: function() {
                var catalogConfig = ConfigLoader.getInstance().get("catalog");
                var toLoad = [];

                array.forEach(catalogConfig, lang.hitch(this, function(serviceGroup) {
                    var services = this.getServicesFromGroup(serviceGroup);
                    array.forEach(services,lang.hitch(this,function(service){
                        if (service.toLoad) {
                            toLoad.push(service);
                        }
                    }));
                }));

                toLoad.sort(function(s1, s2) {
                    if (s1.order == null && s2.order != null) {
                        return -1;
                    }
                    else if (s2.order == null && s1.order != null) {
                        return 1;
                    } 
                    else if(s2.order == null && s1.order == null) {
                    	return 0;
                    }
                    return parseInt(s1.order) - parseInt(s2.order);
                });

//                if (has('ie') || (window.navigator.appName === 'Netscape' && window.navigator.userAgent.indexOf('Trident') > -1)) { // si ie, ordre inverse
//                    toLoad.reverse();
//                }

                array.forEach(toLoad, lang.hitch(this, function(s, idx) {
                    this.addMapService(s);
                }));

                this._servicesAdded = true;
            },

            /**
             * Obtient la liste des services d'un groupe.
             * @param serviceGroup le nom du groupe de service
             * @returns {Array} les services de ce groupe
             */
            getServicesFromGroup: function(serviceGroup) {
                var services = [];

                if(serviceGroup.mapServices){
                    services = services.concat(serviceGroup.mapServices);
                }
                if(serviceGroup.groups){
                    array.forEach(serviceGroup.groups,lang.hitch(this,function(group){
                        services = services.concat(this.getServicesFromGroup(group));
                    }));
                }
                return services;
            },

            /**
             * Affiche un MapService sur la carte
             * @param serviceId l'identifiant du service
             */
            showService: function(serviceId) {
                var service = this.getMapServiceById(serviceId);
                if (service) {
                    service.show();
                } else {
                    MessageManager.getInstance().notifyError("Le service " + serviceId + " à afficher n'existe pas.");
                }
            },

            /**
             * Cache un MapService sur la carte
             * @param serviceId l'identifiant du service
             */
            hideService: function(serviceId) {
                var service = this.getMapServiceById(serviceId);
                if (service) {
                    service.hide();
                } else {
                    MessageManager.getInstance().notifyError("Le service " + serviceId + " à cacher n'existe pas.");
                }
            },

            /**
             * Définit un curseur de carte.
             * @param cursor le curseur de carte
             */
            setMapCursor: function(cursor) {
                this._currentCursor = cursor;
                this.get('esriMap').setMapCursor(cursor);
            },

            /**
             * Obtient le curseur actuel de la carte
             * @returns {string} cursor (valeur css)
             */
            getCurrentToolMapCursor: function() {
                return this._cursor;
            },

            /**
             * Mobient le curseur de la carte
             */
            getMapCursor: function() {
                return this._currentCursor;
            },

            /**
             * Met à jour le curseur actuelle de la carte
             * @param {string} cursor (valeur css)
             * @param {activate} permet de déterminer s'il faut appliquer le changement maintenant
             */
            setCurrentToolMapCursor: function(cursor, activate) {
                this._cursor = cursor;
                if (activate) {
                    this.setMapCursor(cursor);
                }
            },
            /**
             * Réinitialise le curseur de la carte
             */
            resetCursor: function() {
                this.setMapCursor(this._cursor);
            },

            /**
             * Recadre la carte à l'extent initial
             */
            zoomToInitExtent: function() {
                this.zoomToExtent(this.spwViewer.get('initialExtent'));
            },

            /**
             * Recadre la carte à l'extent complet
             */
            zoomToFullExtent: function() {
                this.zoomToExtent(this.spwViewer.get('fullExtent'));
            },

            /**
             * Zoom sur la BBox passée en paramètre
             * @param xMin
             * @param xMax
             * @param yMin
             * @param yMax
             */
            zoomToBbox: function(xMin, xMax, yMin, yMax, srid) {
                var extent = new Extent(xMin, yMin, xMax, yMax, (srid ? new SpatialReference({wkid: srid}) : this.spwViewer.get('initialSpatialReference')));

                if (extent.spatialReference.wkid !== this.spwViewer.get('spatialReference').wkid) {
                    extent = ProjectionManager.getInstance().transform(extent.spatialReference.wkid, this.spwViewer.get('spatialReference').wkid, extent);
                }

                this.zoomToExtent(extent);
            },

            /**
             * Zoom sur l'extent passé en paramètre
             * @param extent
             */
            zoomToExtent: function(extent, fit) {
                if (!extent) {
                    return;
                }

                if (fit == null) {
                    fit = true;
                }

                // var res = this.spwViewer.centerExtentForVisibleZone(extent);
                if (extent.spatialReference && extent.spatialReference.wkid !== this.getSpatialReferenceSRID()) {
                    extent = ProjectionManager.getInstance().transform(extent.spatialReference.wkid, this.getSpatialReferenceSRID(), extent);
                }

                this.get('esriMap').setExtent(this.spwViewer.centerExtentForVisibleZone(extent), fit);
            },

            /**
             * Zoom sur un point en fonction de ses coordonnées et d'un rayon passé.
             * @param x
             * @param y
             * @param radius le rayon autour du point
             */
            zoomToPoint: function(x, y, radius, srid) {
                this.zoomToBbox(x - radius, x + radius, y - radius, y + radius, srid);
            },

            /**
             * Zoom sur un objet feature (esri.Graphic)
             * @param {esri.Graphic} feature
             */
            zoomToFeature: function(feature) {
                if (feature.geometry.type == "point") {
                    var x = feature.geometry.x;
                    var y = feature.geometry.y;
                    this.zoomToPoint(x, y, 250, lang.getObject('spatialReference.wkid', false, feature.geometry));
                }
                else {
                    var extent = feature.geometry.getExtent();
                    this.zoomToExtent(extent);
                }
            },

            /**
             * Zoom sur un ensemble d'objet feature (esri.Graphic)
             * @param {Array.<esri.Graphic>} features
             */
            zoomToFeatures: function(features) {
                var extent = graphicsUtils.graphicsExtent(features);
                if (extent.xmin == extent.xmax && extent.ymin == extent.ymax) {
                    extent = null;
                }
                if (!extent) {
                    this.zoomToFeature(features[0]);
                } else {
                    this.zoomToExtent(extent);
                }
            },

            /**
             * Centre la carte sur les coordonnées passées.
             * @param x
             * @param y
             */
            centerAt: function(x, y, srid) {
                srid = srid || this.getSpatialReferenceSRID();
                this.get('esriMap').centerAt(new Point(x, y, new SpatialReference({wkid: srid})));
            },

            /**
             *
             * Centre et zoom sur un point
             * @param x
             * @param y
             * @param level niveau de zoom
             */
            centerAndZoom: function(x, y, level) {
                this.get('esriMap').centerAndZoom(new Point(x, y, SpwMap.LAMBERT72_SP_REF), level);
            },

            /**
             * Affiche un objet graphique sur la carte du type esri.Graphic
             * @param {esri.Graphic} feature
             */
            showFeature: function(feature) {
                if (!feature.symbol) {
                    this.createDefaultSymbol(feature);
                }
                this.get('esriMap').graphics.add(feature);
            },


            /**
             * Affiche un groupe d'objets graphiques sur la carte du type esri.Graphic
             * @param {esri.Graphic} features
             */
            showFeatures: function(features) {
                array.forEach(features, lang.hitch(this, this.showFeature));
            },

            /**
             * Affiche une géométrie sur la carte et retourne le Graphic esri créé
             * @param {esri.Graphic} feature
             */
            showGeometry: function(geometry) {
                if (!geometry) {
                    return;
                }
                var g = new Graphic(geometry);
                this.createDefaultSymbol(g);
                this.get('esriMap').graphics.add(g);
                return g;
            },
            
            /**
             * Affiche un tableau de géométries sur la carte et retourne les Graphics esri créés
             * @param {esri.Graphic} feature
             */
            showGeometries: function(geometries) {
                if (!geometries) {
                    return;
                }
                var gs = [];
                for(var i = 0; i < geometries.length; i++){;
                    gs.push(this.showGeometry(geometries[i]));
                }
                return gs;
            },

            /**
             * Affiche un marqeur sur la carte pour un point donné
             * @param {esri.Geometry.Point} point
             * @param {object} options
             */
            addMarkerOnPoint: function(point, options, zoom) {
                var configurable = {
                    symbol: PictureMarkerSymbol(this.imagesPath + "/map-marker.png", 36, 36),
                    title: "",
                    text: "",
                    scale: 500,
                    allowDelete: true,
                    show: false,
                    tooltipOpen: false
                };
                if (options) {
                    configurable = lang.mixin(configurable, options);
                }
                if (zoom) {
                    this.zoomToPoint(point.x, point.y, 50, point.spatialReference.wkid);
                }
                if (configurable.show) {
                    this.setScale(configurable.scale);
                    if (point.spatialReference.wkid != this.getSpatialReferenceSRID()) {
                        point = ProjectionManager.getInstance().transform(point.spatialReference.wkid, this.spwViewer.get('spwMap').getSpatialReferenceSRID(), point.x, point.y);
                    }
                    var infoTemplate = new InfoTemplate(configurable.title, configurable.text);
                    var marker  = new Graphic(point, configurable.symbol, {}, infoTemplate);
                    this.get('esriMap').graphics.add(marker);
                    var infoWindow = this.spwViewer.get('spwMap').get('esriMap').infoWindow;
                    var addDeleteButton = lang.hitch(this, function(featureOrEvent) {
                        var curr = infoWindow.features[0];
                        if (infoWindow._deleteButton) {
                            domConstruct.destroy(infoWindow._deleteButton);
                        }
                        if (curr == null && featureSet.features.indexOf(featureOrEvent) < 0) {
                            return;
                        }
                        else if (curr == null) {
                            curr = featureOrEvent;
                        }
                        if (configurable.allowDelete) {
                            var link = infoWindow._deleteButton = domConstruct.create('a', {
                                'class': 'action zoomTo',
                                'style': 'float: right;',
                                innerHTML: 'Supprimer',
                                href: 'javascript:void(0);'
                            }, infoWindow._actionList);
                            on(link, 'click', lang.hitch(this, function() {
                                this.spwViewer.get('spwMap').removeFeature(curr);

                                if (infoWindow.features && infoWindow.features.length > 1) {
                                    infoWindow.selectNext();
                                }
                                else {
                                    infoWindow.hide();
                                }
                            }));
                        }
                        domClass.remove(infoWindow._actionList, 'hidden');

                    });
                    on(infoWindow, 'show', lang.hitch(this, addDeleteButton));
                    if(configurable.tooltipOpen) {
                        infoWindow.features = [];
                        infoWindow.features[0] = marker;
                        this.showInfoWindowAt(configurable.title, configurable.text, point);
                    }
                }
                return marker;
            },

            getEsriPointFromXYWkid: function(x, y, wkid) {
                var point = ProjectionManager.getInstance().transform(wkid, this.getSpatialReferenceSRID(), x, y);
                point = new Point(point.x, point.y, this.getSpatialReference());
                return point;
            },

            /**
             * Crée un symbol par défaut en fonction du type de géométrie passé.
             * @param feature
             */
            createDefaultSymbol: function(feature) {
                switch (feature.geometry.type) {
                    case 'point':
                    case 'Point':
                    case 'multipoint':
                        feature.symbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 10, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0, 1]), 1), new Color([255, 0, 0, 1]));
                        break;
                    case 'polyline':
                    case 'extent':
                        feature.symbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([79, 143, 165, 0.75]), 4);
                        break;
                    case 'polygon':
                        feature.symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([79, 143, 165, 0.5]), 2), new Color([79, 143, 165, 0.3]));
                        break;
                }
            },

            getDefaultSymbol: function(feature) {
                switch (feature.geometry.type) {
                    case 'point':
                    case 'Point':
                    case 'multipoint':
                    case 'MultiPoint':
                        return new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_CIRCLE, 10, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0, 1]), 1), new Color([255, 0, 0, 1]));
                        break;
                    case 'polyline':
                    case 'Polyline':
                    case 'LineString':
                    case 'extent':
                        return new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0, 0.75]), 4);
                        break;
                    case 'polygon':
                    case 'Polygon':
                        return new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([255, 0, 0, 1]), 1), new Color([255, 0, 0, 0.3]));
                        break;
                }
            },

            /**
             * Crée un symbol de mise en évidence par défaut en fonction du type de géométrie passé.
             * @param feature
             */
            createHighlightSymbol: function(feature) {
                switch (feature.geometry.type) {
                    case 'point':
                    case 'multipoint':
                        feature.symbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.STYLE_SQUARE, 10, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0, 255, 255]), 1), new Color([0, 255, 255, 0.5]));
                        break;
                    case 'polyline':
                    case 'extent':
                        feature.symbol = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0, 255, 255]), 4);
                        break;
                    case 'polygon':
                        feature.symbol = new SimpleFillSymbol(SimpleFillSymbol.STYLE_SOLID, new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color([0, 255, 255]), 4), new Color([0, 255, 255, 0.5]));
                        break;
                }
            },

            /**
             * Supprime un objet graphique de la carte
             * @param {esri.Graphic} feature
             */
            removeFeature: function(feature) {
                if(this.get('esriMap'))
                    this.get('esriMap').graphics.remove(feature);
            },

            /**
             * Supprime les objets graphiques spécifiés de la carte
             * @param {Array.<esri.Graphic>} features
             */
            removeFeatures: function(features) {
                array.forEach(features,lang.hitch(this,function(feature){
                    this.removeFeature(feature);
                }));
            },

            /**
             * Met en évidence le feature passé sur la carte
             * @param {esri.Graphic} feature
             */
            highlightFeature: function(feature) {
                lang.mixin(feature, {_originalSymbol: feature.symbol});
                this.removeFeature(feature);
                this.createHighlightSymbol(feature);
                this.showFeature(feature);
            },

            /**
             * Retire la mise en évidence du feature passé sur la carte
             * @param {esri.Graphic} feature
             */
            unhighlightFeature: function(feature) {
                this.removeFeature(feature);
                if (feature._originalSymbol) {
                    this.createDefaultSymbol(feature);
                }
                this.showFeature(feature);
            },

            /**
             * Appelé lorsque la visibilité d'un MapService change.
             * @param mapService
             */
            mapServiceVisibilityChanged: function(mapService) {
                this.emit(this.events.MapServiceVisibilityChanged, mapService);
            },

            resize: function() {
                this.inherited(arguments);
            },

            /**
             * Appelé lorsqu'un MapService est chargé.
             * @param mapService
             */
            mapServiceLoaded: function(mapService) {
                this.emit(this.events.MapServiceLoaded, mapService);
            },

            mapServiceAddedToMap: function(service) {
                this.emit(this.events.MapServiceAddedToMap, service);
            },

            /**
             * Permet de récupérer la légende d'un MapService.
             * @param mapService
             */
            getLegend: function(){
                var legend = new Array();
                for (var key in this._mapServices) {
                    var mapService = this._mapServices[key];
                    var mapServiceLegend = mapService.getMapServiceLegend(this.getCurrentScale());
                    if(mapServiceLegend)
                        legend.push(mapServiceLegend);
                }
                return legend;
            },

            /**
             * Appelé à la destruction de SpwMap
             */
            destroy: function(){
                this.inherited(arguments);
                this.esriMap.destroy();
                this.esriMap = null;
            },

            /**
             * Modifie le mode de navigation de la toolbar
             * @param mode le nouveau mode de navigation à appliquer
             */
            activateNavigationMode:function(mode){
                this._navigationToolBar.activate(mode);
            },

            /**
             * Désactive la navigation de la toolbar
             */
            deactivateNavigation:function(){
                this._navigationToolBar.deactivate();
            },

            /**
             * Retourne le SRID de la référence spatiale actuelle de l'Esri Map
             */
            getSpatialReferenceSRID:function(){
                if (this.esriMap == null || this.esriMap.spatialReference == null) {
                    return this.spwViewer.get('spatialReference').wkid;
                }

                return this.esriMap.spatialReference.wkid;
            },

            /**
             * Retourne la référence spatiale actuelle de la Esri map
             */
            getSpatialReference:function(){
                return this.esriMap.spatialReference;
            },

            /**
             * Projette le point donné dans la référence spatiale actuelle de la carte.
             * @param x la coordonnée X du point
             * @param y la coordonnée Y du point
             * @return le point converti
             */
            toMapPoint: function(x, y) {
                return this.get('esriMap').toMap(new ScreenPoint(x, y));
            },

            /**
             * Convertit une coordonnée en point pixel
             * @param x la coordonnée X du point
             * @param y la coordonnée Y du point
             * @param extent l'extent à prendre en compte lors du calcul
             * @param width la largeur de la carte à prendre en compte lors du calcul
             * @param height la hauteur de la carte à prendre en compte lors du calcul
             * @return le point converti
             */
            toScreenPoint: function(x, y, extent, width, height) {
                extent = extent || this.getCurrentExtent();
                width = width || this.get('esriMap').width;
                height = height || this.get('esriMap').height;

                var pt = new Point(x, y, this.getSpatialReference());

                return screenUtils.toScreenPoint(extent, width, height, pt);
            }
        });

        SpwMap.navigationModes = {
            ZoomTwoPoints: navigation.ZOOM_IN
        };

        SpwMap.events = {
            /**
             * Evènement déclenché lorsque la carte est chargée.
             * @event spw.api.SpwMap#MapLoaded
             */
            MapLoaded: "MapLoaded",

            LayerReorder: "LayerReorder",
            LayerAdd: "LayerAdd",

            /**
             * Evènement déclenché lorsque le premier service est ajouté à la carte.
             * @event spw.api.SpwMap#FirstServiceAdded
             */
            FirstServiceAdded: "FirstServiceAdded",
            /**
             * Evènement déclenché lorsque l'extent de la carte change.
             * @event spw.api.SpwMap#MapExtentChanged
             */
            MapExtentChanged: "MapExtentChanged",
            /**
             * Evènement déclenché lorsque lors du processus de zoom.
             * @event spw.api.SpwMap#MapZoomEnd
             */
            MapZoom: "MapZoom",
            /**
             * Evènement déclenché lorsque le processus de zoom commence.
             * @event spw.api.SpwMap#MapZoomEnd
             */
            MapZoomStart: "MapZoomStart",
            /**
             * Evènement déclenché lorsque le processus de zoom est fini.
             * @event spw.api.SpwMap#MapZoomEnd
             */
            MapZoomEnd: "MapZoomEnd",
            /**
             * Evènement déclenché lorsque le pan est fini.
             * @event spw.api.SpwMap#MapPanEnd
             */
            MapPanEnd: "MapPanEnd",
            /**
             * Evènement déclenché lorsque le curseur est déplacé sur la carte.
             * @event spw.api.SpwMap#MapMouseMove
             */
            MapMouseMove : "MapMouseMove",
            /**
             * Evènement déclenché lorsque l'on double clique sur la carte.
             * @event spw.api.SpwMap#MapDblClicked
             */
            MapDblClicked : "MapDblClicked",
            /**
             * Evènement déclenché lorsque la carte est cliquée.
             * @event spw.api.SpwMap#MapClicked
             */
            MapClicked: "MapClicked",

            /**
             * Evènement déclenché lorsqu'un MapService est ajouté.
             * @event spw.api.SpwMap#MapServiceAdded
             */
            MapServiceAdded: "MapServiceAdded",
            /**
             * Événement déclenché lorsque le layer du MapService est ajouté à la carte esri.
             * @event spw.api.SpwMap#MapServiceAddedToMap
             */
            MapServiceAddedToMap: "MapServiceAddedToMap",
            /**
             * Evènement déclenché lorsqu'un MapService est supprimé.
             * @event spw.api.SpwMap#MapServiceRemoved
             */
            MapServiceRemoved: "MapServiceRemoved",
            /**
             * Evènement déclenché lorsque la visibilité d'un MapService est modifiée.
             * @event spw.api.SpwMap#MapServiceVisibilityChanged
             */
            MapServiceVisibilityChanged: "MapServiceVisibilityChanged",
            /**
             * Evènement déclenché lorsqu'un MapService est chargé.
             * @event spw.api.SpwMap#MapServiceLoaded
             */
            MapServiceLoaded: "MapServiceLoaded",

            MapUpdateStart: "MapUpdateStart",
            MapUpdateEnd: "MapUpdateEnd",

            EditMapService: 'EditMapService',

            MapDestroyed: 'MapDestroyed',
            MapCreated: 'MapCreated',
            GraphicsClicked: 'GraphicsClicked',
            GraphicsMouseDown: "GraphicsMouseDown",
            GraphicsMouseUp: "GraphicsMouseUp"
        };

        SpwMap.LAMBERT72_SP_REF = new SpatialReference({wkid: 31370});
        SpwMap.WGS84_SP_REF = new SpatialReference({wkid: 4326});

        return SpwMap;
    });