Source: widgets/SpwMeasure.js

Retour à la documentation
/**
 * @class spw.widgets.SpwMeasure
 */
define(["dojo/_base/declare","spw/api/SpwBaseTemplatedWidget", "dojo/text!./templates/SpwMeasure.html",
        "dojo/dom-class", "spw/api/SpwDrawToolbar", "dojo/_base/lang",
        "esri/graphic", "dojo/dom-construct","spw/api/SpwBaseWidget", "esri/tasks/Geoprocessor",
        "esri/tasks/LinearUnit", "esri/units", "esri/geometry/geodesicUtils", "esri/tasks/FeatureSet",
        "dojo/on", "dojo/Deferred", "esri/request", "dojo/_base/array", "dojo/_base/Color",
        "esri/symbols/SimpleMarkerSymbol", "esri/symbols/SimpleLineSymbol",
        "dojo/i18n!./nls/SpwMeasure", "spw/api/MessageManager", "dojo/dom-style", "dojo/aspect",
        "dojo/window", "dojo/topic", "dojo/touch", "spw/api/GeometryUtils", "spw/api/ProjectionManager"

        /* when esri 3.14 -> "esri/dijit/ElevationProfile"*/],
    function(declare, SpwBaseTemplatedWidget, tmpl, domClass, Draw,
             lang, graphic, domConstruct, SpwBaseWidget,
             Geoprocessor, LinearUnit, Units, geodesicUtils, FeatureSet,
             on, Deferred, esriRequest, array, Color, SimpleMarkerSymbol,
             SimpleLineSymbol, labels, MessageManager, domStyle, aspect, win,
             topic, touch, GeometryUtils, ProjectionManager

             /* when esri 3.14 -> ElevationProfile */){

        var SpwMeasure = declare("spw.widgets.SpwMeasure", [SpwBaseTemplatedWidget], /** @lends spw.widgets.SpwMeasure.prototype */{

            templateString: tmpl,
            labels: labels,

            allowSelectGraph: true, 

            /**
             *  Nombre d'échantillons
             *  @type Number
             */
            sampling: 50,

            /**
             * Informations sur la tâche du service
             * @type Object
             */
            taskInfo: null,

            /**
             * Objet permettant d'attaquer le service et de récupérer les informations
             * @type Geoprocessor
             */
            profileService: null,

            /**
             * Couleur de base du graphique
             * @type String
             */
            graphColor: "#4f8fa5",

            _tooltip: null,
            _tooltipHandlers: 4,

            popupWidth: null,
            popupHeight: null,

            "widgetTitle": "Mesurer",
            "position": "panel-light",
            "iconClass": "mesureIcon",
            "height": "auto",
            "right": "15px",
            "top": "85px",
            "profileServiceUrl": "//geoservices.wallonie.be/arcgis/rest/services/GEOTRAITEMENT/Profil/GPServer/Profil",//"http://elevation.arcgis.com/arcgis/rest/services/Tools/ElevationSync/GPServer/Profil",

            /**
             * @constructs
             * @param config
             */
            constructor: function (config) {
            },

            postCreate: function() {
                this.inherited(arguments);

                this.spwViewer.get('spwMap').on(this.spwViewer.get('spwMap').events.MapDestroyed, lang.hitch(this, function() {
                    this.drawTb = null;
                    this.deactivateAll();

                    if (this.AltimetriePopup && this.AltimetriePopup) {
                        this.AltimetriePopup.onDeactivate();
                    }
                }));

                if (this.allowSelectGraph !== true) {
                    domStyle.set(this._selectGraphText, 'display', 'none');
                }

                this.own(
                    on(this.selectGraphButton, 'change', lang.hitch(this, this.selectGraphOnMap)),
                    on(this.DistanceButton, touch.press, lang.hitch(this, this.activateDistanceClick)),
                    on(this.SurfaceButton, touch.press, lang.hitch(this, this.activateSurfaceClick)),
                    on(this.enlargeImgNode, touch.press, lang.hitch(this, this.enlargeGraphClick)),
                    on(this.AltimetrieButton, touch.press, lang.hitch(this, this.activateAltimetrieClick))
                );
            },

            selectingOnMap: false,
            oldTooltip: null,
            selectHandlers: null,

            selectGraphOnMap: function() {
                if (this.drawTb == null) {
                    return;
                }

                this.selectingOnMap = this.selectGraphButton.get('checked');

                array.forEach(this.selectHandlers, function(s) {
                    s.remove();
                });

                this.selectHandlers = [];

                if (this.selectingOnMap) {
                    this.drawTb.deactivate();

                    if (this._tooltip) {
                        this.oldTooltip = this._tooltip.innerHTML;
                        this._tooltip.innerHTML = 'Cliquez sur un itinéraire';
                    }

                    var mss = this.spwViewer.get('spwMap').getMapServices({
                        isBaseMap: false
                    });

                    this.selectHandlers.push(on(this.spwViewer.get('spwMap'), this.spwViewer.get('spwMap').events.MapServiceAdded, lang.hitch(this, function(ms) {
                        if (ms.layer && ms.layer.graphics) {
                            this.selectHandlers.push(ms.layer.on('click', lang.hitch(this, this.onGraphicsClicked)));
                        }
                    })));

                    array.forEach(mss, lang.hitch(this, function(ms) {
                        if (ms.layer && ms.layer.graphics) {
                            this.selectHandlers.push(ms.layer.on('click', lang.hitch(this, this.onGraphicsClicked)));
                        }
                    }));
                }
                else {
                    this.drawTb.activate(Draw.POLYLINE);

                    if (this._tooltip && this.oldTooltip) {
                        this._tooltip.innerHTML = this.oldTooltip;
                    }
                }
            },

            onGraphicsClicked: function(evt) {
                if (!this.selectingOnMap) {
                    return;
                }

                var graph = evt.graphic;

                if (graph && graph.geometry && graph.geometry.type === 'polyline') {
                    this.showAltimetrie({geometry: graph.geometry});
                }
            },

            activateDistanceClick: function(){
                this.deactivateAll();
                this.activateTool('Distance');
                this.activateDrawLine(lang.hitch(this, function(evt){
                    if (evt.geometry == null) {
                        return;
                    }

                    this.showGeometry(evt.geometry);
                    var m = GeometryUtils.geometryLength(evt.geometry, 'meters');

                    var tmp = (Math.round(m * 10) / 10);
                    this.DistanceMeters.innerHTML = tmp.toString().replace('.', ',');
                    this.DistanceKilometers.innerHTML = (Math.round(tmp * 10) / 10000).toString().replace('.', ',');
                }), 'Cliquez pour commencer la mesure');
            },

            activateSurfaceClick: function(){
                this.deactivateAll();
                this.activateTool('Surface');
                this.activateDrawPolygon(lang.hitch(this, function(evt, type){
                    if (evt.geometry == null) {
                        return;
                    }

                    if (type !== 'move' && evt.geometry.isSelfIntersecting()) {
                        MessageManager.getInstance().notifyError('Impossible de calculer la surface car le polygone s\'intersecte lui-même.');
                        this.removeGeometry();
                        return;
                    }

                    this.showGeometry(evt.geometry);

                    var m = GeometryUtils.area(evt.geometry, 'square-meters');//geometryEngine.planarArea(evt.geometry, 'square-meters');

                    this.SurfaceMeters.innerHTML = (Math.round(m * 10) / 10).toString().replace('.', ',');
                    this.SurfaceKilometers.innerHTML = (Math.round(m * 10) / 10000000).toString().replace('.', ',');
                    this.SurfaceHectares.innerHTML = (Math.round(m * 10) / 100000).toString().replace('.', ',');
                    this.SurfaceAres.innerHTML = (Math.round(m * 10) / 1000).toString().replace('.', ',');

                    var p = GeometryUtils.geometryLength(evt.geometry, 'meters');

                    this.PerimetreMeters.innerHTML = (Math.round(p * 10) / 10).toString().replace('.', ',');
                    this.PerimetreKilometers.innerHTML = (Math.round(p * 10) / 10000).toString().replace('.', ',');
                }), 'Cliquez pour commencer la mesure');
            },

            activateAltimetrieClick: function(){
                this.deactivateAll();
                this.activateTool('Altimetrie');

                if (this.showAltimetrie == null) {
                    this.showAltimetrie = lang.hitch(this, function(evt){
                        domStyle.set(this.graphResult, 'display', '');

                        this.showGeometry(evt.geometry);

                        this.createAltimetrieGraph();

                        this.currGraphAltimetrie.showLoading();

                        if (this.AltimetriePopup) {
                            this.AltimetriePopup.showLoading();
                        }

                        /* when esri 1.14 ->
                         this.emit('update-chart', {geometry: evt.geometry});
                         */

                        // on affiche le profil
                        this._getProfile(evt.geometry).then(lang.hitch(this, function (elevationInfo) {
                            this.elevationInfo = elevationInfo;
                            this.emit("update-chart", elevationInfo);
                        }), lang.hitch(this, function (error) {
                            MessageManager.getInstance().notifyError(lang.replace("{message}\n\n{details.0}", error));
                            console.error(error);
                        }));
                    });
                }


                this.activateDrawLine(this.showAltimetrie, 'Cliquez pour commencer la mesure', ['click']);
            },

            /**
             * initialisation du service
             */
            _initProfileService: function () {
                // promesse retournée
                var deferred = new Deferred();

                if (this.profileService) {
                    deferred.resolve(this.profileService);
                    return deferred;
                }

                if (this.profileServiceUrl) {
                    // s'il on souhaite tester que le service est up et obtenir ses informations //
                    /*esriRequest({
                        url: this.profileServiceUrl,
                        content: {
                            f: "json"
                        },
                        callbackParamName: "callback"
                    }).then(lang.hitch(this, function (taskInfo) {
                        // détails de la tâche
                        this.taskInfo = taskInfo;

                        // création de l'objet permettant d'attaquer le service
                        this.profileService = new Geoprocessor(this.profileServiceUrl);
                        this.profileService.setOutSpatialReference(this.spwViewer.get('spwMap').get('esriMap').spatialReference);

                        deferred.resolve();
                    }), lang.hitch(this, function (error) {
                        deferred.reject(error);
                    }));*/

                    // création de l'objet permettant d'attaquer le service
                    this.profileService = new Geoprocessor(this.profileServiceUrl);
                    deferred.resolve(this.profileService);
                } else {
                    deferred.reject(new Error('invalid url'));
                }

                return deferred.promise;
            },

            _getProfile: function (polyline) {
                var deferred = new Deferred();

                // on récupère la longueur en mètres
                var lengthMeters = GeometryUtils.geometryLength(polyline, 'meters');
                // var roundedLength = (Math.round(m * 10) / 10);
                // on détermine la distance d'échantillonnage
                var samplingDistance = (lengthMeters / this.sampling);

                //var samplingDistance = this.samplingDistance;

                // crée une feature qui sera transmise au service

                var wkidFromMap = this.spwViewer.get('spwMap').esriMap.spatialReference.wkid;

                var inputProfileGraphic = new graphic(ProjectionManager.getInstance().transform(wkidFromMap || 31370, 3857, polyline), null, {
                    OID: 1
                });

                var inputLineFeatures = new FeatureSet();
                inputLineFeatures.features = [inputProfileGraphic];

                // on a besoin de l'OID
                inputLineFeatures.fields = [{
                    "name": "OID",
                    "type": "esriFieldTypeObjectID",
                    "alias": "OID"
                }];

                // on s'assure que le service a été initialisée
                this._initProfileService().then(lang.hitch(this, function(profileService) {

                    profileService.setProcessSpatialReference(polyline.spatialReference ? polyline.spatialReference : this.spwViewer.get('spwMap').esriMap.spatialReference);
                    profileService.setOutSpatialReference(polyline.spatialReference ? polyline.spatialReference : this.spwViewer.get('spwMap').esriMap.spatialReference);

                    // on exécute la requête
                    profileService.execute({
                        "InputLineFeatures": inputLineFeatures,
                        "ProfileIDField": "OID",
                        "DEMResolution": "1m",
                        "MaximumSampleDistance": 1,
                        "MaximumSampleDistanceUnits": "Meters",
                        "returnZ": true,
                        "returnM": true
                    }).then(lang.hitch(this, function (results) {
                        if (results.length > 0) {
                            var profileOutput = results[0].value;

                            if (profileOutput.features.length > 0) {
                                // on récupère le profile
                                var profileFeature = profileOutput.features[0];

                                // récupérer la distance totale
                                this.altiDistance = Math.round(profileFeature.attributes.ProfileLength);
                                var altis = [], points = [];
                                //this.reorderPaths(profileFeature);
                                array.forEach(profileFeature.geometry.paths, lang.hitch(this, function(path){
                                    points = points.concat(array.map(path, function(i){
                                    	return {
                                    		x:i[0], y:i[1], a:i[2]
                                    	};
                                	}));
                                }));
                                //var pts = simplify(points, 0.5, true);
                                altis = simplify(points, window.tolerance || 0.5, true);
                                altis = array.map(altis, function(i){return Math.round(i.a * 100) / 100;});
                                this.altiMax = Math.max.apply(Math, altis);
                                this.altiMax = Math.round(this.altiMax);
                                this.altiMin = Math.min.apply(Math, altis);
                                this.altiMin = Math.round(this.altiMin);

                                var cur=null, next=null;
                                this.cneg = 0;
                                this.cpos = 0;
                                var tab = {};
                                array.forEach(altis, lang.hitch(this, function(a, idx){

                                    cur = a;
                                    if(altis.length > idx + 1) {
                                        next = altis[idx + 1];
                                        if(cur > next){
                                            this.cneg += (cur - next);
                                        } else if(cur < next){
                                            this.cpos += (next - cur);
                                        }
                                    }
                                }));
                                this.cneg = Math.round(this.cneg * 100) / 100;
                                this.cpos = Math.round(this.cpos * 100) / 100;
                                this.cabs = (Math.round((this.cpos - this.cneg) * 100) / 100);

                                // this.altiDistNode.innerHTML = this.altiDistance;
                                // this.altiMaxNode.innerHTML = this.altiMax;
                                // this.altiMinNode.innerHTML = this.altiMin;
                                // this.cabsNode.innerHTML = this.cabs;
                                // this.cposNode.innerHTML = this.cpos;
                                // this.cnegNode.innerHTML = this.cneg;

                                // puis la géométrie
                                var profileGeometry = profileFeature.geometry;
                                var allElevations = [];
                                var allDistances = [];

                                if (profileGeometry.paths.length > 0) {
                                    // chemins de la polyline
                                    array.forEach(profileGeometry.paths, lang.hitch(this, function (profilePoints, pathIndex) {
                                        // informations sur l'altitude
                                        array.forEach(profilePoints, lang.hitch(this, function (coords, pointIndex) {
                                            var elevationInfo = {
                                                x: ((coords.length > 3) ? coords[3] : (pointIndex * samplingDistance)),
                                                y: ((coords.length > 2) ? coords[2] : 0.0),
                                                pathIdx: pathIndex,
                                                pointIdx: pointIndex
                                            };

                                            allElevations.push(elevationInfo);
                                            allDistances.push(elevationInfo.x);
                                        }));
                                    }));

                                    // on résoud la promesse
                                    deferred.resolve({
                                        geometry: profileGeometry,
                                        elevations: allElevations,
                                        distances: allDistances,
                                        samplingDistance: samplingDistance
                                    });
                                } else {
                                    deferred.reject(new Error(this.labels.UnableToProcessResults));
                                }
                            } else {
                                deferred.reject(new Error(this.labels.UnableToProcessResults));
                            }
                        } else {
                            deferred.reject(new Error(this.labels.UnableToProcessResults));
                        }
                    }), deferred.reject);

                }));

                return deferred.promise;
            },
            
//            reorderPaths: function(profileFeature) {
//            	var arr=[profileFeature.geometry.paths[0]];
//            	profileFeature.geometry.paths.forEach((p,idx) => {
//            		//if(idx === 0) return;
//            		var first = {x: Math.round(p[0][0]), y: Math.round(p[0][1])}; var last = {x: Math.round(p[p.length-1][0]), y: Math.round(p[p.length-1][1])};
//            		profileFeature.geometry.paths.forEach((p2, idx2) => {
//            			var first2 = {x: Math.round(p2[0][0]), y: Math.round(p2[0][1])}; var last2 = {x: Math.round(p2[p2.length-1][0]), y: Math.round(p2[p2.length-1][1])};
//            			if(first.x === last2.x && first.y === last2.y){
//            				//console.log("path " + idx + " is after path " +idx2);
//            				var eleIdx = arr.indexOf(profileFeature.geometry.paths[idx]);
//            				if(eleIdx > -1){
//            					arr.splice(eleIdx+1, 0, profileFeature.geometry.paths[idx2]);
//            	            }
//            			} else if(first2.x === last.x && first2.y === last.y){
//            				//console.log("path " + idx + " is before path " +idx2); 
//            				var eleIdx = arr.indexOf(profileFeature.geometry.paths[idx]);
//            				if(eleIdx > -1){
//            					arr.splice(eleIdx, 0, profileFeature.geometry.paths[idx2]);
//            	            }
//            	        }
//            		});
//            	});
//            	profileFeature.geometry.paths = arr;
//            },

            deactivateAll: function(){
                if (this._tooltip) {
                    domConstruct.destroy(this._tooltip);
                    this._tooltip = null;

                    for (var i = 1; i <= this._tooltipHandlers; ++i) {
                        if (this['_tooltipHandler' + i]) {
                            this['_tooltipHandler' + i].remove();
                        }
                    }
                }

                if (this.selectGraphButton) {
                    this.selectGraphButton.set('checked', false);
                    this.selectGraphOnMap();
                }

                this.resetAll();

                this.deactivateTool('Distance');
                this.deactivateTool('Surface');
                this.deactivateTool('Altimetrie');
            },

            activateTool: function(tool){
                domClass.add(this[tool + 'Button'], "activated");
                domClass.add(this['measure' + tool + 'Content'], "activated");
            },

            deactivateTool: function(tool){
                if (this[tool + 'Button'] == null) {
                    return;
                }

                domClass.remove(this[tool + 'Button'], "activated");
                domClass.remove(this['measure' + tool + 'Content'], "activated");

                this.selectGraphButton.set('checked', false);
                this.selectGraphOnMap();

                if(this.drawTb){
                    this.drawTb.deactivate();
                }
                if(this.drawEndHdl){
                    this.drawEndHdl.remove();
                    this.drawEndHdl = null;
                }

                if (this.drawMoveHdl) {
                    this.drawMoveHdl.remove();
                    this.drawMoveHdl = null;
                }
            },

            resetAll: function(){
                this.elevationInfo = null;

                if (this.DistanceMeters) { // peut être undefined si MapDestroyed
                    this.DistanceMeters.innerHTML = "";
                    this.DistanceKilometers.innerHTML = "";

                    this.SurfaceMeters.innerHTML = "";
                    this.SurfaceKilometers.innerHTML = "";
                    this.SurfaceHectares.innerHTML = "";
                    this.SurfaceAres.innerHTML = "";

                    this.PerimetreMeters.innerHTML = "";
                    this.PerimetreKilometers.innerHTML = "";
                }

                if (this.currGraphAltimetrie && (this.currGraphAltimetrie.chartLocationGraphic.nbGraph === 1 || !this.activated)) {
                    this.currGraphAltimetrie.onDeactivate();
                    this.currGraphAltimetrie.destroy();
                    this.currGraphAltimetrie = null;

                    domStyle.set(this.graphResult, 'display', 'none');
                }

                if (this.currGeom && (this.AltimetriePopup == null || !this.AltimetriePopup.activated)) {
                    this.spwViewer.get('spwMap').removeFeature(this.currGeom);
                    this.currGeom = null;
                }
            },

            resetAfter: function() {
                if (this.AltimetriePopup){
                    this.AltimetriePopup.destroy();
                    this.AltimetriePopup = null;
                }

                if (this.currGeom) {
                    this.spwViewer.get('spwMap').removeFeature(this.currGeom);
                    this.currGeom = null;
                }
            },

            onActivate: function() {
                this.inherited(arguments);

                this.own(
                    this._graphicsClickedHandler = on(this.spwViewer.get('spwMap'), this.spwViewer.get('spwMap').events.GraphicsClicked, lang.hitch(this, this.onGraphicsClicked))
                );
            },

            onDeactivate: function(){
                this.inherited(arguments);

                this.deactivateAll();

                if (this._graphicsClickedHandler) {
                    this._graphicsClickedHandler.remove();
                    this._graphicsClickedHandler = null;
                }
            },

            showTooltip: function(msg, draw) {
                if (this._tooltip == null) {
                    this._tooltip = domConstruct.create('div', {
                        'class': 'tooltipMeasureV3',
                        innerHTML: msg
                    }, this.spwViewer.get('spwMap').esriMap.container);

                    var drawing = false;
                    this.own(
                        this._tooltipHandler1 = on(this.spwViewer.get('spwMap'), this.spwViewer.get('spwMap').events.MapMouseMove, lang.hitch(this, function(evt) {
                            domStyle.set(this._tooltip, {
                                'top': (evt.screenPoint.y + 15) + 'px',
                                'left': (evt.screenPoint.x + 15) + 'px'
                            });
                        })),
                        this._tooltipHandler2 = on(this.spwViewer.get('spwMap'), this.spwViewer.get('spwMap').events.MapClicked, lang.hitch(this, function() {
                            if (this.selectingOnMap) {
                                return;
                            }

                            if (!drawing) {
                                drawing = true;

                                if (this._tooltip) {
                                    this._tooltip.innerHTML = 'Cliquez pour continuer ou double-cliquez pour terminer';
                                }
                            }
                        })),
                        this._tooltipHandler3 = on(draw, 'draw-end', lang.hitch(this, function() {
                            drawing = false;
                            if (this._tooltip) {
                                this._tooltip.innerHTML = msg;
                            }
                        })),
                        this._tooltipHandler4 = aspect.after(draw, '_clear', lang.hitch(this, function(def) {
                            drawing = false;
                            if (this._tooltip) {
                                this._tooltip.innerHTML = msg;
                            }
                        }))
                    );
                }
                else {
                    domStyle.set(this._tooltip, 'display', '');
                    if (this._tooltip) {
                        this._tooltip.innerHTML = msg;
                    }
                }
            },

            activateDrawLine: function(fct, msg, opts) {
                opts = opts || ['click', 'move'];

                if(!this.drawTb){
                    this.drawTb = new Draw(this.spwViewer.get('spwMap').get('esriMap'), {
                        showTooltips: false
                    });
                }

                this.showTooltip(msg, this.drawTb);

                if(this.drawEndHdl){
                    this.drawEndHdl.remove();
                    this.drawEndHdl = null;
                }

                if (this.drawMoveHdl) {
                    this.drawMoveHdl.remove();
                    this.drawMoveHdl = null;
                }

                if (opts.indexOf('click') > -1) {
                    this.drawEndHdl = this.drawTb.on('draw-end', lang.hitch(this, function(evt){
                        if(fct) {
                            fct(evt);
                        }
                    }));
                }

                if (opts.indexOf('move') > -1) {
                    this.drawMoveHdl = this.drawTb.on('mouse-move', lang.hitch(this, function(evt) {
                        fct && fct(evt, 'move');
                    }));
                }

                this.drawTb.activate(Draw.POLYLINE);
            },

            activateDrawPolygon: function(fct, msg, opts) {
                opts = opts || ['click', 'move'];

                if(!this.drawTb){
                    this.drawTb = new Draw(this.spwViewer.get('spwMap').get('esriMap'), {
                        showTooltips: false
                    });
                }

                this.showTooltip(msg, this.drawTb);

                if(this.drawEndHdl){
                    this.drawEndHdl.remove();
                    this.drawEndHdl = null;
                }

                if (this.drawMoveHdl) {
                    this.drawMoveHdl.remove();
                    this.drawMoveHdl = null;
                }

                if (opts.indexOf('click') > -1) {
                    this.drawEndHdl = this.drawTb.on('draw-end', lang.hitch(this, function(evt){
                        if(fct) {
                            fct(evt);
                        }
                    }));
                }

                if (opts.indexOf('move') > -1) {
                    this.drawMoveHdl = this.drawTb.on('mouse-move', lang.hitch(this, function(evt) {
                        fct && fct(evt, 'move');
                    }));
                }

                this.drawTb.activate(Draw.POLYGON);
            },

            createAltimetrieGraph: function() {
                if (!this.currGraphAltimetrie) {
                    this.currGraphAltimetrie = new graphAltimetrie({
                        spwViewer: this.spwViewer,
                        measureWidget: this,
                        graphColor: this.graphColor
                    }, domConstruct.create("div", { style: "height: 250px; position: relative;" }, this.AltimetrieGraphDiv));

                    this.currGraphAltimetrie.onActivate(true);
                }
            },

            enlargeGraphClick: function(){
                if(this.AltimetriePopup) {
                    this.AltimetriePopup.onDeactivate();
                    this.AltimetriePopup.destroy();
                }

                this.AltimetriePopup = new graphAltimetrie({
                    position: (this.spwViewer.mobile ? "bottom" : "panel-light"),
                    width: this._vwToPixels(this.popupWidth || '70vw'), // car 70vw ne permet pas au graphique de se redimensionner correctement
                    height: this.popupHeight || "260px",
                    bottom: "20px",
                    right: "130px",
                    widgetTitle: "Profil altimétrique",
                    activated: !this.spwViewer.mobile,
                    spwViewer: this.spwViewer,
                    measureWidget: this,
                    resizable: true,
                    inPopup: true,
                    graphColor: this.graphColor,
                    showMeasureInfo: true
                });

                if (this.spwViewer.mobile) {
                    this.AltimetriePopup.onActivate(); // si on ne le fait pas à cet endroit, ça ne s'affiche pas
                }
            },

            _vwToPixels: function(vw) {
                if (vw.indexOf('vw') < 0) {
                    return vw;
                }

                return (win.getBox().w * (parseInt(vw) / 100)) + 'px';
            },

            removeGeometry: function() {
                if(this.currGeom){
                    this.spwViewer.get('spwMap').removeFeature(this.currGeom);
                    this.currGeom = null;
                }
            },

            showGeometry: function(geometry) {
                this.removeGeometry();
                this.currGeom = new graphic(geometry);
                this.spwViewer.get('spwMap').showFeature(this.currGeom);
            }
        });

        var graphAltimetrie = declare("spw.widgets.SpwMeasureGraph", [SpwBaseWidget], {

            measureWidget: null,
            spwViewer: null,
            inPopup: false,
            profileChart: null,

            profilePolyline: null,
            elevationData: null,
            distances: null,
            /**
             * graphic croix rouge -> partagé entre les différents graphiques
             * @type {Object}
             */
            chartLocationGraphic: {
                graphic: null,
                nbGraph: 0
            },

            plotName: 'default',
            seriesName: 'elevation-data',

            currentChartDistance: null,
            showMeasureInfo: false,

            postCreate: function(){
                this.inherited(arguments);

                this._createGraph();

                if(this.showMeasureInfo){
                    var distance = domConstruct.create("span",{ innerHTML: "Distance: ", style: "text-shadow: -1px 0px"});
                    var distanceVal = domConstruct.create("span",{ innerHTML: this.measureWidget.altiDistance + " m", style: "padding-right: 30px"});
                    var altMin = domConstruct.create("span",{ innerHTML: "Altitude min: ", style: "text-shadow: -1px 0px"});
                    var altMinVal = domConstruct.create("span",{ innerHTML: this.measureWidget.altiMin + " m", style: "padding-right: 30px"});
                    var altMax = domConstruct.create("span",{ innerHTML: "Altitude max: ", style: "text-shadow: -1px 0px"});
                    var altMaxVal = domConstruct.create("span",{ innerHTML: this.measureWidget.altiMax + " m", style: "padding-right: 30px"});
                    var cumPos = domConstruct.create("span",{ innerHTML: "Cumulée +: " , style: "text-shadow: -1px 0px"});
                    var cumPosVal = domConstruct.create("span",{ innerHTML: this.measureWidget.cpos+ " m", style: "padding-right: 30px"});
                    var cumNeg = domConstruct.create("span",{ innerHTML: "Cumulée -: ", style: "text-shadow: -1px 0px"});
                    var cumNegVal = domConstruct.create("span",{ innerHTML: + this.measureWidget.cneg+ " m", style: "padding-right: 30px"});

                    var container = domConstruct.create("div", {style: "width: 100%; text-align: center"});

                    container.appendChild(distance);
                    container.appendChild(distanceVal);
                    container.appendChild(altMin);
                    container.appendChild(altMinVal);
                    container.appendChild(altMax);
                    container.appendChild(altMaxVal);
                    container.appendChild(cumPos);
                    container.appendChild(cumPosVal);
                    container.appendChild(cumNeg);
                    container.appendChild(cumNegVal);

                    this.domNode.appendChild(container);
                }
            },

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

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

                if (!this.inPopup) {
                    return;
                }

                if (this.profileChart) {
                    var w = null;
                    var h = null;

                    if (arguments[0] == null) {
                        w = parseFloat(this.width) * 1;
                        h = parseFloat(this.height) * 0.8;
                    }
                    else {
                        if (arguments[0].w) {
                            w = arguments[0].w * 1;
                        }

                        if (arguments[0].h) {
                            h = arguments[0].h * 0.95; // pour éviter la scrollbar tout le temps
                        }
                    }

                    this.profileChart.resize({w: w, h: h});
                }
            },

            onActivate: function(ignoreSuperClass) {
                if (ignoreSuperClass !== true) {
                    this.inherited(arguments);
                }

                this.chartLocationGraphic.nbGraph += 1;
            },

            onDeactivate: function() {
                this.inherited(arguments);

                this.chartLocationGraphic.nbGraph -= 1;

                this._removeChartLocation();

                if (this.inPopup && !this.measureWidget.activated) {
                    this.measureWidget.resetAfter();
                }
            },

            _removeChartLocation: function() {
                if (this.chartLocationGraphic.graphic && this.spwViewer && this.chartLocationGraphic.nbGraph === 0) {
                    this.spwViewer.get('spwMap').removeFeature(this.chartLocationGraphic.graphic);
                    this.chartLocationGraphic.graphic = null;
                }
            },

            _displayChartLocation: function (chartObjectX) {
                var indexOfCurrentDistance = this.distances.indexOf(chartObjectX);
                this.currentChartDistance = 0;
                if(indexOfCurrentDistance > -1){
                    this.currentChartDistance = this.distances[indexOfCurrentDistance];
                }
                if (this.spwViewer && this.elevationData && this.profilePolyline) {

                    if (this.chartLocationGraphic.graphic == null) {
                        // créatin du graphic (croix rouge)
                        var clr = Color.fromHex(this.graphColor).toRgb();

                        clr.push(128); // alpha à 255 et pas 0-1...

                        var chartLocationSymbol = new SimpleMarkerSymbol({
                            "color": clr,
                            "size": 8,
                            "type": "esriSMS",
                            "style": "esriSMSCircle",
                            "outline": {
                                "color": [0, 0, 0, 255],
                                "width": 1,
                                "type": "esriSLS",
                                "style": "esriSLSSolid"
                            }
                        });

                        /*var couleur = Color.fromHex(this.graphColor);
                        var outline = new SimpleLineSymbol(SimpleLineSymbol.STYLE_SOLID, new Color(Color.named.black), 3);
                        var chartLocationSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.CIRCLE, 13, outline, couleur);*/

                        this.chartLocationGraphic.graphic = new graphic(null, chartLocationSymbol);

                        this.spwViewer.get('spwMap').showFeature(this.chartLocationGraphic.graphic);
                    }

                    // mise à jour de la position de la croix
                    var distanceIndex = (this.distances) ? array.indexOf(this.distances, chartObjectX) : -1;

                    if (distanceIndex > -1) {
                        var elevData = this.elevationData[distanceIndex];
                        this.chartLocationGraphic.graphic.setGeometry(this.profilePolyline.getPoint(elevData.pathIdx, elevData.pointIdx));
                    } else {
                        this.chartLocationGraphic.graphic.setGeometry(null);
                    }

                    topic.publish('measure/refreshCross', {
                        'sender': this,
                        'position': chartObjectX
                    });
                }
            },

            _updateChart: function(elevationInfo) {
                /* when esri 3.14 ->
                epWidget.set("profileGeometry", elevationInfo.geometry);
                */

                this._removeChartLocation();

                var elevationDataSeriesName = this.seriesName;

                this.profilePolyline = elevationInfo.geometry;
                this.elevationData = this._convertElevationsInfoArray(elevationInfo.elevations);
                this.distances = this._convertDistancesArray(elevationInfo.distances);
                //this.samplingDistance.distance = this._convertDistancesArray([elevationInfo.samplingDistance.distance])[0];

                // calculs des y min et max
                var yMinSource = this._getArrayMin(this.elevationData);
                var yMaxSource = this._getArrayMax(this.elevationData);
                var yRange = (yMaxSource - yMinSource);
                yMin = yMinSource - (yRange * 0.05);
                yMax = yMaxSource + (yRange * 0.05);

                // mise à jour du graphique
                this.profileChart.getAxis("y").opt.min = yMin;
                this.profileChart.getAxis("y").opt.max = yMax;
                // on indique que le chart doit être regénéré
                this.profileChart.dirty = true;
                this.profileChart.updateSeries(elevationDataSeriesName, this.elevationData);

                // qu'on regénère
                this.profileChart.render();

                this.hideLoading();
            },

            /** récupère le max d'un tableau */
            _getArrayMax: function (dataArray) {
                var values = array.map(dataArray, function (item) {
                    return item.y;
                });
                return Math.max.apply(Math, values);
            },

            /** récupère le min d'un tableau */
            _getArrayMin: function (dataArray) {
                var values = array.map(dataArray, function (item) {
                    return item.y;
                });
                return Math.min.apply(Math, values);
            },

            /** récupère le x et le y des informations retournées par le service */
            _convertElevationsInfoArray: function (elevationArray) {
                return array.map(elevationArray, lang.hitch(this, function (item) {
                    return lang.mixin(item, {
                        x: item.x,
                        y: item.y
                    });
                }));
            },

            /** récupère les distances */
            _convertDistancesArray: function (distancesArray) {
                return array.map(distancesArray, lang.hitch(this, function (distance) {
                    return distance;
                }));
            },

            _createGraph: function() {
                /*          when esri 3.14 ->
                            var profileParams = {
                                chartOptions: {
                                    title: 'Profil altimétrique',
                                    chartTitleFontSize: 14,
                                    axisTitleFontSize: 11,
                                    axisLabelFontSize: 9,
                                    indicatorFontColor: '#eee',
                                    indicatorFillColor: '#666',
                                    titleFontColor: '#eee',
                                    axisFontColor: '#ccc',
                                    axisMajorTickColor: '#333',
                                    skyTopColor: "#B0E0E6",
                                    skyBottomColor: "#4682B4",
                                    waterLineColor: "#eee",
                                    waterTopColor: "#ADD8E6",
                                    waterBottomColor: "#0000FF",
                                    elevationLineColor: "#D2B48C",
                                    elevationTopColor: "#8B4513",
                                    elevationBottomColor: "#CD853F"
                                },
                                map: this.spwViewer.get('spwMap').get('esriMap'),
                                profileTaskUrl: 'https://elevation.arcgis.com/arcgis/rest/services/Tools/ElevationSync/GPServer',
                                scalebarUnits: Units.METERS
                            };

                            this.profileChart = new ElevationProfile(profileParams, this.domNode);
                            this.profileChart.startup();
                */
                require(["dojox/charting/Chart", "dojox/charting/axis2d/Default", "dojox/charting/plot2d/Areas",
                        "dojox/charting/themes/PlotKit/blue", "dojox/charting/action2d/MouseZoomAndPan",
                        "dojox/charting/action2d/MouseIndicator", "dojox/charting/Theme"],

                    lang.hitch(this, function(Chart, Default, Areas, PlotKitBlue, MouseZoomAndPan, MouseIndicator, Theme) {

                        PlotKitBlue.next = function(elementType, mixin, doPost){
                            var theme = Theme.prototype.next.apply(this, arguments);

                            if(elementType == "line"){
                                theme.marker.outline = {width: 2, color: "#fff"};
                                theme.series.stroke.width = 3.5;
                                theme.marker.stroke.width = 2;
                            }else if(elementType == "candlestick"){
                                theme.series.stroke.width = 1;
                            }/*else if(theme.series.stroke.color && (theme.series.stroke.color.toString() ==
                                new Color(this.colors[(this._current-1) % this.colors.length]).toString())){
                            // if the user did not override the stroke, let's force blank
                            theme.series.stroke.color = "#fff";
                        }*/
                            return theme;
                        };

                        if(this.showMeasureInfo){
                            this.profileChart =  new Chart(domConstruct.create('div', {}, domConstruct.create('div', { style: 'position: absolute; top: 55px; bottom: 1px; left: 0; right: 0;'}, this.domNode)));
                        } else {
                            this.profileChart =  new Chart(this.domNode);
                        }

                        this.profileChart.addPlot(this.plotName, {
                            type: Areas,
                            markers: false,
                            tension: "X",
                            dirty: true
                        });

                        this.profileChart.addAxis("x", {
                            title: 'Distance (m)',
                            titleOrientation: 'away',
                            titleFontColor: 'rgb(153, 153, 153)',
                            titleFont: "Lato"
                        });

                        this.profileChart.addAxis("y", {
                            title: 'Altitude (m)',
                            vertical: true,
                            titleFontColor: 'rgb(153, 153, 153)',
                            titleFont: "Lato"
                        });

                        this.profileChart.addSeries(this.seriesName, []);

                        new MouseZoomAndPan(this.profileChart, this.plotName, { axis: "x" });

                        PlotKitBlue.chart.fill = "#fff";
                        PlotKitBlue.plotarea.fill = "#fff";

                        var baseColor = Color.fromHex(this.graphColor).toRgba();
                        baseColor[3] = 0.6;

                        PlotKitBlue.series.fill = Color.fromArray(baseColor).toString();

                        PlotKitBlue.colors = Theme.defineColors({
                            base: this.graphColor,
                            generator: "monochromatic"
                        });

                        this.profileChart.setTheme(PlotKitBlue);

                        this.mouseIndicator = new MouseIndicator(this.profileChart, this.plotName, {
                            series: this.seriesName,
                            font: "normal normal bold 8pt Tahoma",
                            mouseOver: true,
                            fillFunc: function(v){
                                return "#4f8fa5";//v.y >= 0 ? "green" : "red";
                            },
                            /*markerStroke: {
                                color: "black",
                                width: 3
                            },*/


                            //markerSymbol: "m -6 -6, l 12 12, m 0 -12, l -12 12",
                            labelFunc: lang.hitch(this, function(v) {
                                this._displayChartLocation(v.x);
                                var factor = Math.pow(10, 1);
                                var truncValue = Math.round(this.currentChartDistance * factor) / factor;
                                return ('Alt: '+Math.round(v.y * 100)/100) + ' m; Dist: ' + truncValue + " m";
                            })
                        });

                        this.profileChart.render();

                        this.own(
                            on(this.measureWidget, 'update-chart', lang.hitch(this, this._updateChart))
                        );

                        if (this.measureWidget.elevationInfo) {
                            this._updateChart(this.measureWidget.elevationInfo);
                        }

                        this.resize();

                        topic.subscribe('measure/refreshCross', lang.hitch(this, function(obj) {
                            if (obj.sender === this) {
                                return;
                            }

                            // mettre à jour la valeur sur le graphique...comment ?
                        }));
                    })
                );
            }
        });

        return SpwMeasure;
    });

/*
(c) 2017, Vladimir Agafonkin
Simplify.js, a high-performance JS polyline simplification library
mourner.github.io/simplify-js
*/

(function () { 'use strict';

//to suit your point format, run search/replace for '.x' and '.y';
//for 3D version, see 3d branch (configurability would draw significant performance overhead)

//square distance between 2 points
function getSqDist(p1, p2) {

   var dx = p1.x - p2.x,
       dy = p1.y - p2.y;

   return dx * dx + dy * dy;
}

//square distance from a point to a segment
function getSqSegDist(p, p1, p2) {

   var x = p1.x,
       y = p1.y,
       dx = p2.x - x,
       dy = p2.y - y;

   if (dx !== 0 || dy !== 0) {

       var t = ((p.x - x) * dx + (p.y - y) * dy) / (dx * dx + dy * dy);

       if (t > 1) {
           x = p2.x;
           y = p2.y;

       } else if (t > 0) {
           x += dx * t;
           y += dy * t;
       }
   }

   dx = p.x - x;
   dy = p.y - y;

   return dx * dx + dy * dy;
}
//rest of the code doesn't care about point format

//basic distance-based simplification
function simplifyRadialDist(points, sqTolerance) {

   var prevPoint = points[0],
       newPoints = [prevPoint],
       point;

   for (var i = 1, len = points.length; i < len; i++) {
       point = points[i];

       if (getSqDist(point, prevPoint) > sqTolerance) {
           newPoints.push(point);
           prevPoint = point;
       }
   }

   if (prevPoint !== point) newPoints.push(point);

   return newPoints;
}

function simplifyDPStep(points, first, last, sqTolerance, simplified) {
   var maxSqDist = sqTolerance,
       index;

   for (var i = first + 1; i < last; i++) {
       var sqDist = getSqSegDist(points[i], points[first], points[last]);

       if (sqDist > maxSqDist) {
           index = i;
           maxSqDist = sqDist;
       }
   }

   if (maxSqDist > sqTolerance) {
       if (index - first > 1) simplifyDPStep(points, first, index, sqTolerance, simplified);
       simplified.push(points[index]);
       if (last - index > 1) simplifyDPStep(points, index, last, sqTolerance, simplified);
   }
}

//simplification using Ramer-Douglas-Peucker algorithm
function simplifyDouglasPeucker(points, sqTolerance) {
   var last = points.length - 1;

   var simplified = [points[0]];
   simplifyDPStep(points, 0, last, sqTolerance, simplified);
   simplified.push(points[last]);

   return simplified;
}

//both algorithms combined for awesome performance
function simplify(points, tolerance, highestQuality) {

   if (points.length <= 2) return points;

   var sqTolerance = tolerance !== undefined ? tolerance * tolerance : 1;

   points = highestQuality ? points : simplifyRadialDist(points, sqTolerance);
   points = simplifyDouglasPeucker(points, sqTolerance);

   return points;
}

//export as AMD module / Node module / browser or worker variable
window.simplify = simplify;

})();