UNPKG

geoportal-extensions-leaflet

Version:
1,462 lines (1,263 loc) 50.9 kB
import Gp from "geoportal-access-lib"; import L from "leaflet"; import Logger from "../../Common/Utils/LoggerByDefault"; import ID from "../../Common/Utils/SelectorID"; import MathUtils from "../../Common/Utils/MathUtils"; import MousePositionDOM from "../../Common/Controls/MousePositionDOM"; import PositionFormater from "./Utils/PositionFormater"; import CRS from "../CRS/CRS"; var logger = Logger.getLogger("mouseposition"); /** * @classdesc * * Leaflet Control Class to display Mouse position in various CRS and altitude using the <a href="https://geoservices.ign.fr/documentation/geoservices/alti.html" target="_blank">altimetric web service of the Geoportal Platform</a>. * * Use {@link module:Controls.MousePosition L.geoportalControl.MousePosition()} factory to create instances of that class. * * **Extends** Leaflet <a href="http://leafletjs.com/reference.html#control" target="_blank">L.Control</a> native class. * * @namespace * @alias L.geoportalControl.MousePosition */ var MousePosition = L.Control.extend(/** @lends L.geoportalControl.MousePosition.prototype */ { includes : MousePositionDOM, /** * options by default * * @private */ options : { position : "bottomleft", collapsed : true, units : [], systems : [], displayAltitude : true, displayCoordinates : true, editCoordinates : false, altitude : { triggerDelay : 200, responseDelay : 500, noDataValue : -99999, noDataValueTolerance : 90000, serviceOptions : {} } }, /** * @constructor MousePosition * * @private * @alias MousePosition * @extends {L.Control} * @param {Object} options - options for function call. * @param {String} [options.apiKey] - API key. The "calcul" key is used by default. * @param {Boolean} [options.ssl = true] - use of ssl or not (default true, service requested using https protocol) * @param {String} [options.position] - position of component into the map, 'bottomleft' by default * @param {Boolean} [options.collapsed = true] - collapse mode, false by default * @param {Array} [options.systems] - list of projection systems, GEOGRAPHIC, MERCATOR, LAMB93 and LAMB2E by default * Each array element (=system) is an object with following properties : * @param {String} options.systems.crs - Proj4 crs alias (from proj4 defs). e.g. : "EPSG:4326". Required * @param {String} [options.systems.label] - CRS label to be displayed in control. Default is crs code (e.g. "EPSG:4326") * @param {String} [options.systems.type] - CRS units type for coordinates conversion : "Geographical" or "Metric". Default: "Metric" * @param {Object} [options.systems.geoBBox] - Aera covered by the system (WGS84 coordinates). * @param {Number} options.systems.geoBBox.right - Right bound. * @param {Number} options.systems.geoBBox.left - Left bound. * @param {Number} options.systems.geoBBox.top - Top bound. * @param {Number} options.systems.geoBBox.bottom - Bottom bound. * @param {Array} [options.units] - list of units by system, Geographical and Metric by default * Values may be "DEC" (decimal degrees), "DMS" (sexagecimal), "RAD" (radians) and "GON" (grades) for geographical coordinates, * and "M" or "KM" for metric coordinates * @param {Boolean} [options.displayAltitude= true] - active/desactivate the altitude panel, if desactivate, have just the coordinate panel, true by default * @param {Boolean} [options.displayCoordinates= true] - active/desactivate the coordinate panel, if desactivate, have just the altitude panel, true by default * @param {Boolean} [options.editCoordinates = false] - add edit coordinates options. False by default. * @param {Object} [options.altitude] - elevation configuration * @param {Object} [options.altitude.serviceOptions] - options of elevation service * @param {Number} [options.altitude.responseDelay] - latency for altitude request, 500 ms by default * @param {Number} [options.altitude.triggerDelay] - immobilisation time of movement on the map to trigger the elevation calculation, 200 ms by default * @param {Number} [options.altitude.noDataValue] - value used for altitude service no data (default is -99999). In this case, "---m" will be displayed instead of "-99999m" * @param {Number} [options.altitude.noDataValueTolerance] - tolerance for no data value : * values in [noDataValue - noDataValueTolerance ; noDataValue + noDataValueTolerance] interval will not be displayed, but "---m" will be displayed instead. * Default is 90000 * @example * var MousePosition = L.geoportalControl.MousePosition({ * position : 'bottomleft', * collapsed : false, * displayAltitude : true, * displayCoordinates : true, * editCoordinates : false, * altitude : { * triggerDelay : 100, * responseDelay : 500, * noDataValue : -99999, * noDataValueTolerance : 90000, * serviceOptions : {} * }, * systems : [ * { * crs : L.CRS.EPSG4326, * label : "Lon,Lat", * type : "Geographical" * }, * { * crs : L.geoportalCRS.EPSG2154, * label : "Lambert 93", * type : "Metric" * } * ], * units : ["DEC", "DMS"] * }); */ initialize : function (options) { // on merge les options avec celles par defaut L.Util.extend(this.options, options); // uuid this._uid = ID.generate(); // initialisation des systemes de projections this._projectionSystems = []; this._initProjectionSystems(); // initialisation des systemes des unités this._projectionUnits = {}; this._initProjectionUnits(); // detection du support : desktop ou tactile this._isDesktop = this._detectSupport(); // on met en place un seuil sur le timer if (this.options.altitude.triggerDelay < 100) { this.options.altitude.triggerDelay = 100; } // timer sur le delai d'immobilisation du mouvement this._timer = this.options.altitude.triggerDelay; // Systeme de projection selectionné (cf. _initProjectionSystems) this._currentProjectionSystems = this._projectionSystems[0]; // Container des systemes this._projectionSystemsContainer = null; /** Type d'unité de projection selectionnés : Geographical ou Metric (cf._initProjectionSystems ) */ this._currentProjectionType = this._projectionSystems[0].type; // Unité de projection selectionnés (cf. _initProjectionUnits) this._currentProjectionUnits = this._projectionUnits[this._currentProjectionType][0].code; // Container des unités this._projectionUnitsContainer = null; /** Container de visualisation du panneau du composant */ this._showContainer = null; this._pictoContainer = null; this._panelContainer = null; this._panelHeaderContainer = null; // gestion de l'affichage du panneau de l'altitude / coordonnées if (!this.options.displayAltitude && !this.options.displayCoordinates) { // on reactive cette option ! this.options.displayCoordinates = true; } if (!this.options.displayCoordinates) { // si les coordonnées ne sont pas affichées : pas besoin de les éditer... this.options.editCoordinates = false; } /** Edition des coordonnées en cours ou non */ this._isEditing = false; // on transmet les options au controle L.Util.setOptions(this, this.options); }, /** * this method is called by this.addTo(map) when the control is added on the map * and fills variable 'this._container = this.onAdd(map)', * and create events on map. * @param {Object} map - the map * * @returns {DOMElement} DOM element * @private */ onAdd : function (map) { // initialisation du DOM du composant var container = this._container = this._initLayout(); // on met en place l'evenement sur la carte pour recuperer les coordonnées, // on l'active à l'ouverture du panneau uniquement ! if (!this.options.collapsed) { // this.onShowMousePositionClick(); // evenement valable pour le mode desktop ! if (this._isDesktop) { map.on("mousemove", this.onMouseMove, this); } else { map.on("move", this.onMapMove, this); } } // deactivate of events that may interfere with the map L.DomEvent .disableClickPropagation(container) .disableScrollPropagation(container); // on stoppe la propagation de l'événement mousemove sur le container L.DomEvent .addListener(container, "mousemove", L.DomEvent.stopPropagation) .addListener(container, "mousemove", L.DomEvent.preventDefault); return container; }, /** * this method is called when the control is removed from the map * and removes events on map. * @param {Object} map - the map * * @private */ onRemove : function (map) { map.off("mousemove", this.onMouseMove); }, /** * this method is called by the constructor and initialize the projection * systems. * getting coordinates in the requested projection : * see this.onMousePositionProjectionSystemChange() * * @private */ _initProjectionSystems : function () { // on donne la possibilité à l'utilisateur de modifier // la liste des systèmes à afficher // Ex. this.options.systems // systemes de projection disponible par defaut var projectionSystemsByDefault = [{ label : "G\u00e9ographique", crs : L.CRS.Simple, // L.Projection.LonLat ! type : "Geographical" }, { label : "Web Mercator", crs : L.CRS.EPSG3395, // L.Projection.SphericalMercator ! type : "Metric" }, { label : "Lambert 93", crs : CRS.EPSG2154, type : "Metric", geoBBox : { left : -9.86, bottom : 41.15, right : 10.38, top : 51.56 } }, { label : "Lambert II \u00e9tendu", crs : CRS.EPSG27572, type : "Metric", geoBBox : { left : -4.87, bottom : 42.33, right : 8.23, top : 51.14 } }]; var systems = this.options.systems; for (var i = 0; i < systems.length; i++) { // definition d'un systeme de reference var sys = systems[i]; if (!sys.label) { logger.error("not defined !"); continue; } if (!sys.crs) { logger.error("crs not defined !"); continue; } if (!sys.type) { logger.warn("type srs not defined, use 'Metric' by default !"); sys.type = "Metric"; } this._projectionSystems.push(systems[i]); // it's a just a test ... var found = false; for (var j = 0; j < projectionSystemsByDefault.length; j++) { var obj = projectionSystemsByDefault[j]; if (sys.crs === obj.crs) { found = true; logger.info("crs '{}' already configured by default", obj.code); } } if (!found) { logger.info("crs '{}' not found, it's a new projection", sys.code || sys.label); } } // au cas où... if (this._projectionSystems.length === 0) { this._projectionSystems = projectionSystemsByDefault; } // re-initilisation des codes pour gerer le lien entre _projectionSystems et select du mouse position (lien code/value) for (var k = 0; k < this._projectionSystems.length; ++k) { this._projectionSystems[k].code = k; } }, /** * this method is called by the constructor and initialize the units. * getting coordinates in the requested units : * see this.onMousePositionProjectionUnitsChange() * * @private */ _initProjectionUnits : function () { // on donne la possibilité à l'utilisateur de modifier // la liste des unités à afficher // Ex. // this.options.units : ["DEC", "DMS"] // unités disponible par defaut var projectionUnitsByDefault = { Geographical : [{ code : "DEC", label : "degrés décimaux", format : this._displayDEC }, { code : "DMS", label : "degrés sexagésimaux", format : this._displayDMS }, { code : "RAD", label : "radians", format : this._displayRAD }, { code : "GON", label : "grades", format : this._displayGON }], Metric : [{ code : "M", label : "mètres", format : this._displayMeter }, { code : "KM", label : "kilomètres", format : this._displayKMeter }] }; var units = this.options.units; for (var type in projectionUnitsByDefault) { if (projectionUnitsByDefault.hasOwnProperty(type)) { var found = false; for (var j = 0; j < projectionUnitsByDefault[type].length; j++) { var obj = projectionUnitsByDefault[type][j]; for (var i = 0; i < units.length; i++) { var unit = units[i]; if (obj.code === unit) { found = true; if (!this._projectionUnits[type]) { this._projectionUnits[type] = []; } this._projectionUnits[type].push(obj); } } } if (!found) { this._projectionUnits[type] = projectionUnitsByDefault[type]; } } } // au cas où... if (Object.keys(this._projectionUnits).length === 0) { this._projectionUnits = projectionUnitsByDefault; } }, /** * this method is called by the constructor. * this information is useful to switch to touch mode. * Detection : test for desktop or tactile * * @returns {Boolean} is desktop * @private */ _detectSupport : function () { // TODO // Choix de gérer la détection dans le code du composant au lieu du DOM car : // Utilisation de l'implémentation Leaflet // http://leafletjs.com/reference.html#browser var isDesktop = true; var userAgent = window.navigator.userAgent.toLowerCase(); if (userAgent.indexOf("iphone") !== -1 || userAgent.indexOf("ipod") !== -1 || userAgent.indexOf("ipad") !== -1 || userAgent.indexOf("android") !== -1 || userAgent.indexOf("mobile") !== -1 || userAgent.indexOf("blackberry") !== -1 || userAgent.indexOf("tablet") !== -1 || userAgent.indexOf("phone") !== -1 || userAgent.indexOf("touch") !== -1) { isDesktop = false; } if (userAgent.indexOf("msie") !== -1 || userAgent.indexOf("trident") !== -1) { isDesktop = true; } return isDesktop; }, // ################################################################### // // ######################## methods handle dom ####################### // // ################################################################### // /** * this method is called by this.onAdd(map) * and initialize the container HTMLElement * * @returns {DOMElement} DOM element * @private */ _initLayout : function () { // create main container var container = this._createMainContainerElement(); var inputShow = this._showContainer = this._createShowMousePositionElement(); container.appendChild(inputShow); // mode "collapsed" if (!this.options.collapsed) { inputShow.checked = true; } var picto = this._pictoContainer = this._createShowMousePositionPictoElement(this._isDesktop); container.appendChild(picto); var panel = this._panelContainer = this._createMousePositionPanelElement(); var header = this._panelHeaderContainer = this._createMousePositionPanelHeaderElement(); panel.appendChild(header); var basic = this._createMousePositionPanelBasicElement( this.options.displayAltitude, this.options.displayCoordinates, this.options.editCoordinates ); panel.appendChild(basic); var arraySettings = this._createShowMousePositionSettingsElement(this.options.displayCoordinates); for (var j = 0; j < arraySettings.length; j++) { panel.appendChild(arraySettings[j]); } var settings = this._createMousePositionSettingsElement(); var systems = this._projectionSystemsContainer = this._createMousePositionSettingsSystemsElement(this._projectionSystems); var units = this._projectionUnitsContainer = this._createMousePositionSettingsUnitsElement(this._projectionUnits[this._currentProjectionType]); settings.appendChild(systems); settings.appendChild(units); panel.appendChild(settings); container.appendChild(panel); // ce tag n'est pas à placer dans le container du controle, // mais dans celui de la map ! var center = this._createMapCenter(); var map = this._map; map.getContainer().appendChild(center); return container; }, /** * this method is called by this.() * and it changes the elevation view panel into the dom. * FIXME call by ID ! * * @param {Boolean} active - true:active, false:disable * * @private */ _setElevationPanel : function (active) { var div = null; if (!active) { div = L.DomUtil.get(this._addUID("GPmousePositionAltitude")); div.style.display = "none"; } }, /** * this method is called by this.() * and it changes the coordinate view panel into the dom. * FIXME call by ID ! * * @param {Boolean} active - true:active, false:disable * * @private */ _setCoordinatePanel : function (active) { if (!active) { var div = L.DomUtil.get(this._addUID("GPmousePositionCoordinate")); div.style.display = "none"; } }, /** * this method is called by this.() * and it changes the settings view panel into the dom. * FIXME call by ID ! * * @param {Boolean} active - true:active, false:disable * * @private */ _setSettingsPanel : function (active) { if (!active) { var divPicto = L.DomUtil.get("GPshowMousePositionSettingsPicto"); var divPanel = L.DomUtil.get(this._addUID("GPmousePositionSettings")); divPicto.style.display = "none"; divPanel.style.display = "none"; } }, /** * this method is called by this.onMousePositionProjectionSystemChange() * when changes to a metric or a geographical units. * * @param {String} type - Geographical or Metric * * @private */ _setTypeUnitsPanel : function (type) { var container = this._projectionUnitsContainer; // on supprime les enfants... while (container.firstChild) { container.removeChild(container.firstChild); } var units = this._projectionUnits[type]; for (var j = 0; j < units.length; j++) { var obj = units[j]; var option = document.createElement("option"); option.value = (obj.code) ? obj.code : j; option.text = obj.label || j; // option.label = obj.label; container.appendChild(option); } var projectionUnits = this._projectionUnits[type][0].code; if (this._currentProjectionUnits === "DMS" || projectionUnits === "DMS") { this._resetCoordinateElements(this.options.editCoordinates, type, projectionUnits); this._setEditMode(this._isEditing); } // le nouveau type de system ... this._currentProjectionType = type; // Mise a jour des elements labels et unites this._resetLabelElements(type); this._resetUnitElements(projectionUnits); // et comme on a changé de type de systeme, // il faut changer aussi d'unité ! this._currentProjectionUnits = this._projectionUnits[type][0].code; }, // ################################################################### // // ######################## method units format ###################### // // ################################################################### // /** * degreedecimal * @param {Object} oLatLng - coordinates * * @returns {Object} coordinates in decimal * @private */ _displayDEC : function (oLatLng) { var coordinate = {}; coordinate.lat = PositionFormater.roundToDecimal(oLatLng.lat, 6); coordinate.lng = PositionFormater.roundToDecimal(oLatLng.lng, 6); coordinate.unit = "°"; return coordinate; }, /** * degreedecimal2sexagecimal * @param {Object} oLatLng - coordinates * * @returns {Object} coordinates in DMS * @private */ _displayDMS : function (oLatLng) { var coordinate = {}; coordinate.lat = PositionFormater.decimalLatToDMS(oLatLng.lat, true); coordinate.lng = PositionFormater.decimalLonToDMS(oLatLng.lng, true); return coordinate; }, /** * degreedecimal2radian * @param {Object} oLatLng - coordinates * * @returns {Object} coordinates in radian * @private */ _displayRAD : function (oLatLng) { var coordinate = {}; coordinate.lat = PositionFormater.decimalToRadian(oLatLng.lat); coordinate.lng = PositionFormater.decimalToRadian(oLatLng.lng); coordinate.unit = "rad"; return coordinate; }, /** * degreedecimal2grade * @param {Object} oLatLng - coordinates * * @returns {Object} coordinates in gon * @private */ _displayGON : function (oLatLng) { var coordinate = {}; coordinate.lat = PositionFormater.decimalToGrade(oLatLng.lat); coordinate.lng = PositionFormater.decimalToGrade(oLatLng.lng); coordinate.unit = "gon"; return coordinate; }, /** * meter * @param {Object} oXY - coordinates * * @returns {Object} coordinates in meters * @private */ _displayMeter : function (oXY) { // on recoit toujours des coordonnées metriques var coordinate = {}; coordinate.x = L.Util.formatNum(oXY.x, 2); coordinate.y = L.Util.formatNum(oXY.y, 2); coordinate.unit = "m"; return coordinate; }, /** * kilometer * @param {Object} oXY - coordinates * * @returns {Object} coordinates in km * @private */ _displayKMeter : function (oXY) { var coordinate = {}; coordinate.x = L.Util.formatNum(oXY.x / 1000, 2); coordinate.y = L.Util.formatNum(oXY.y / 1000, 2); coordinate.unit = "km"; return coordinate; }, // ################################################################### // // ####################### method system project ##################### // // ################################################################### // /** * this method projects a coordinate to a specific projection. * FIXME * * @param {Object} oLatLng - geographic coordinate (L.LatLng) * @param {Object} crs - projection system (ex. GEOGRAPHIC, LAMB93, LAMB2E, MERCATOR, ...) * @returns {Object} oXY - coordinate * @private */ _project : function (oLatLng, crs) { // cf. http://leafletjs.com/reference.html#iprojection // notre carte est dans la projection par defaut : // Spherical Mercator projection (EPSG:3857) // - GEOGRAPHIC : conversion native, L.CRS.Simple ou L.Projection.LngLat.project(latlng) // - LAMB93 : L.GeoportalCRS.EPSG2154 ou projection.project(latlng) // - LAMB2E : L.GeoportalCRS.EPSG27572 ou projection.project(latlng) // - MERCATOR ou EPSG:3395 : L.CRS.EPSG3395 ou L.Projection.Mercator.project(latlng) if (typeof crs === "function") { // "crs is an function !"... en mode AMD ! crs = crs(); } if (typeof crs !== "object") { logger.log("crs is not an object !"); return; } // pas de reprojection pour le systeme de projection natif ! if (crs === L.CRS.Simple) { return oLatLng; } if (!crs.projection || typeof crs.projection !== "object") { logger.error("projection is not an object !"); return; } var oPoint = crs.projection.project(oLatLng); // FIXME reprojeter du geographique en geographique cause qq problemes // Ex. LatLng en EPSG4326 ! // FIXME probleme d'inversion d'axe sur les projections geographiques // Ex. EPSG:4326 -> lat/lon // IGNF:RGF93G -> lon/lat if (this._currentProjectionType === "Geographical") { oPoint.lat = oPoint.y; oPoint.lng = oPoint.x; } if (!oPoint || Object.keys(oPoint).length === 0) { logger.error("Failed to project with crs code : " + crs.code); } return oPoint; }, /** * this method unprojects a coordinate to a geographic projection. * * @param {Object} oXY - coordinate * @returns {Object} oLatLng - geographic coordinate (L.LatLng) * @private */ _unproject : function (oXY) { // cf. http://leafletjs.com/reference.html#iprojection // notre carte est dans la projection par defaut : // Spherical Mercator projection (EPSG:3857) // - GEOGRAPHIC : conversion native, L.CRS.Simple ou L.Projection.LngLat.project(latlng) // - LAMB93 : L.GeoportalCRS.EPSG2154 ou projection.project(latlng) // - LAMB2E : L.GeoportalCRS.EPSG27572 ou projection.project(latlng) // - MERCATOR ou EPSG:3395 : L.CRS.EPSG3395 ou L.Projection.Mercator.project(latlng) var oSrs = this._currentProjectionSystems.crs; if (!oSrs) { logger.log("system crs not found"); return; } if (typeof oSrs === "function") { // "crs is an function !"... en mode AMD ! oSrs = oSrs(); } if (typeof oSrs !== "object") { logger.log("crs is not an object !"); return; } // pas de reprojection pour le systeme de projection natif ! if (oSrs === L.CRS.Simple) { return { lat : oXY.y, lng : oXY.x }; } if (this._currentProjectionType === "Geographical") { return { lat : oXY.y, lng : oXY.x }; } if (!oSrs.projection || typeof oSrs.projection !== "object") { logger.error("projection is not an object !"); return; } var oLatLng = oSrs.projection.unproject(oXY); if (!oLatLng || Object.keys(oLatLng).length === 0) { logger.error("Failed to unproject coordinate"); } return oLatLng; }, // ################################################################### // // ##################### handlers events to control ################## // // ################################################################### // /** * this sends the coordinates to the panel. * (cf. this.GPdisplayCoords() into the DOM functions) * * @param {Object} oLatLng - geographic coordinate (L.LatLng) * * @private */ _setCoordinate : function (oLatLng) { // structure // L.LatLng // lat: 4.07249425916745 // lng: 2.4609375 // type de systeme : Geographical ou Metric var type = this._currentProjectionSystems.type; // on recherche la fonction de formatage dans l'unitée demandée var format = null; var units = this._projectionUnits[type]; for (var i = 0; i < units.length; i++) { if (units[i].code === this._currentProjectionUnits) { format = units[i].format; break; } } // structure pour les coordonnées en fonctin du type demandé : // {x:, y:, unit:} ou {lng:, lat:} ou {lon:, lat:} ou {e:, n:, unit:}... var coordinate = {}; // on projete le point dans le systeme demandé var oSrs = this._currentProjectionSystems.crs; if (!oSrs) { logger.error("crs not found !"); return; } coordinate = format(this._project(oLatLng, oSrs)); if (!coordinate || Object.keys(coordinate).length === 0) { return; } this.GPdisplayCoords(coordinate); }, /** * this sends the coordinates to the panel. * (cf. this.GPdisplayElevation() into the DOM functions) * * @param {Object} oLatLng - geographic coordinate (L.LatLng) * * @private */ _setElevation : function (oLatLng) { // gestion du timer de la requete du service d'altitude var delay = this.options.altitude.responseDelay; var noDataValue = this.options.altitude.noDataValue; var noDataValueTolerance = this.options.altitude.noDataValueTolerance; this.GPdisplayElevation(oLatLng, delay, noDataValue, noDataValueTolerance); }, /** * this method is triggered when the mouse or the map is stopped. * (cf. onMouseMove and onMapMove) * * @param {Object} oLatLng - geographic coordinate (L.LatLng) * * @private */ onMoveStopped : function (oLatLng) { this._setElevation(oLatLng); }, /** * this method is an handler event to control. The event is 'mousemove' on * the map. The handler sends the coordinates to the panel. * (cf. this.GPdisplayCoords() into the DOM functions) * * @param {Object} e - HTMLElement * * @private */ onMouseMove : function (e) { var self = this; var oLatLng = e.latlng; this._setCoordinate(oLatLng); clearTimeout(this._timer); this._timer = setTimeout(function () { self.onMoveStopped(oLatLng); }, this.options.altitude.triggerDelay); }, /** * this method is an handler event to control. The event is 'moveend' on * the map. The handler sends the coordinates to the panel. * (cf. this.GPdisplayCoords() into the DOM functions) * * @private */ onMapMove : function () { var self = this; var map = this._map; var oLatLng = map.getCenter(); this._setCoordinate(oLatLng); clearTimeout(this._timer); this._timer = setTimeout(function () { self.onMoveStopped(oLatLng); }, this.options.altitude.triggerDelay); }, // ################################################################### // // ####################### handlers events to dom #################### // // ################################################################### // /** * this method is called by this.GPdisplayCoords() in the dom, and * it executes a request to the elevation service. * * @param {Object} coordinate - {lat:..., lng:...} * @param {Function} callback - callback * * @private */ onRequestAltitude : function (coordinate, callback) { logger.log("onRequestAltitude"); // INFORMATION // on effectue la requête au service d'altitude... // on met en place des callbacks afin de recuperer les resultats ou // les messages d'erreurs du service. // le resultat est affiché dans une balise du dom. // les messages d'erreurs sont affichés sur la console (?) if (!coordinate || Object.keys(coordinate).length === 0) { return; } // si on ne veut pas de calcul d'altitude, on ne continue pas ! if (!this.options.displayAltitude) { return; } logger.log(coordinate); var options = {}; // on recupere les options du service L.Util.extend(options, this.options.altitude.serviceOptions); // ainsi que les coordonnées L.Util.extend(options, { zonly : true, positions : [{ lon : coordinate.lon || coordinate.lng, lat : coordinate.lat }] }); // et les callbacks L.Util.extend(options, { scope : this, // callback onSuccess onSuccess : function (results) { logger.log(results); if (results && Object.keys(results).length) { // var context = this.options.scope; // context._setAltidude(results.elevations[0].z); callback.call(this, results.elevations[0].z); } }, // callback onFailure onFailure : function (error) { logger.error(error.message); } }); // cas où la clef API n'est pas renseignée dans les options du service, // on utilise celle renseignée au niveau du controle ou la clé "calcul" par défaut L.Util.extend(options, { apiKey : options.apiKey || this.options.apiKey }); // si l'utilisateur a spécifié le paramètre ssl au niveau du control, on s'en sert // true par défaut (https) L.Util.extend(options, { ssl : this.options.ssl }); logger.log(options); Gp.Services.getAltitude(options); }, /** * this method is called by event 'click' on 'GPshowMousePositionPicto' tag label * (cf. this._createShowMousePositionPictoElement), * and toggles event 'mousemove' on map. * FIXME * * @param {Object} e - HTMLElement * * @private */ onShowMousePositionClick : function (e) { logger.log(e); // checked : true - panel close // checked : false - panel open var map = this._map; // evenement declenché à l'ouverture/fermeture du panneau, // et en fonction du mode : desktop ou tactile ! if (this._showContainer.checked) { (this._isDesktop) ? map.off("mousemove", this.onMouseMove, this) : map.off("move", this.onMapMove, this); } else { (this._isDesktop) ? map.on("mousemove", this.onMouseMove, this) : map.on("move", this.onMapMove, this); } // on gère l'affichage des panneaux ici..., // même si ce n'est pas l'endroit adequate... this._setElevationPanel(this.options.displayAltitude); this._setCoordinatePanel(this.options.displayCoordinates); if (!this.options.displayCoordinates) { this._setSettingsPanel(false); } }, /** * this method is called by event 'click' on input coordinate * * @param {Boolean} editing - editing mode * @private */ onMousePositionEditModeClick : function (editing) { if (!this.options.editCoordinates) { return; } if (this._isEditing === editing) { return; } this._isEditing = editing; // Affichage des outils, input en ecriture this._setEditMode(this._isEditing); var map = this._map; if (this._isDesktop) { (this._isEditing) ? map.off("mousemove", this.onMouseMove, this) : map.on("mousemove", this.onMouseMove, this); } else { (this._isEditing) ? map.off("move", this.onMapMove, this) : map.on("move", this.onMapMove, this); } }, /** * Convert Coordinate value : km to meters, radians, grades to decimal degrees * @param {Number} value - value to convert * @param {String} unit - unit * * @returns {Number} converted value * @private */ _convertCoordinate : function (value, unit) { var result; if (unit === "DEC" || unit === "DMS") { // DMS est converti en DEC ! result = value; } else if (unit === "M") { result = value; } else if (unit === "KM") { result = value * 1000; } else if (unit === "RAD") { var rd = (180 / Math.PI).toFixed(20); result = (value * rd).toFixed(20); } else if (unit === "GON") { var d = (9 / 10).toFixed(20); result = (value * d).toFixed(20); } return result; }, /** * Validate Extend coordinate * * @param {String} coordType - Lat or Lon * @param {String} value - coordinate * @param {Event} e - event * @returns {Boolean} value is within extent */ validateExtentCoordinate : function (coordType, value, e) { // FIXME pas de validation... if (e !== undefined) { return true; } if (["Lon", "Lat"].indexOf(coordType) === -1) { return false; } var geoBBox = this._currentProjectionSystems.geoBBox; if (geoBBox === undefined) { return true; } if (geoBBox) { // check if coordinates are in the extent var extent = [geoBBox.left, geoBBox.bottom, geoBBox.right, geoBBox.top]; var unit = this._currentProjectionUnits; // on convertit un point..., mais on n'a pas de fonction // de conversion comme pour openlayers... var oLatLon = this._unproject({ x : (coordType === "Lon") ? this._convertCoordinate(value, unit) : 0, y : (coordType === "Lat") ? this._convertCoordinate(value, unit) : 0 }); if (coordType === "Lon" && (oLatLon.lng < extent[0] || oLatLon.lng > extent[2])) { logger.warn("coordinates (lon) out of extent !?"); return false; } if (coordType === "Lat" && (oLatLon.lat < extent[1] || oLatLon.lat > extent[3])) { logger.warn("coordinates (lat) out of extent !?"); return false; } } return true; }, /** * Get coordinate from inputs and select in decimal degrees * * @param {String} coordType - "Lon" or "Lat" * @returns {String} coordinate * @private */ _getCoordinate : function (coordType) { var inputDegrees = L.DomUtil.get(this._addUID("GPmousePosition" + coordType + "Degrees")); var degrees = inputDegrees.value; if (!degrees) { return null; } degrees = degrees.replace(",", "."); if (!MathUtils.isInteger(degrees)) { return null; } var result = MathUtils.toInteger(degrees); if (result < Number(inputDegrees.dataset.min) || result > Number(inputDegrees.dataset.max)) { return null; } var direction = L.DomUtil.get(this._addUID("GPmousePosition" + coordType + "Direction")).value; var inputMinutes = L.DomUtil.get(this._addUID("GPmousePosition" + coordType + "Minutes")); var minutes = inputMinutes.value; if (minutes) { minutes = minutes.replace(",", "."); if (MathUtils.isInteger(minutes)) { var mins = MathUtils.toInteger(minutes); if (mins >= Number(inputMinutes.dataset.min) && mins <= Number(inputMinutes.dataset.max)) { result += (mins / 60); } } } var inputSeconds = L.DomUtil.get(this._addUID("GPmousePosition" + coordType + "Seconds")); var seconds = inputSeconds.value; if (seconds) { seconds = seconds.replace(",", "."); var secs = MathUtils.toFloat(seconds); if (secs && secs >= Number(inputSeconds.dataset.min) && secs <= Number(inputSeconds.dataset.max)) { result += (secs / 3600); } } if (direction === "O" || direction === "S") { result = -result; } return result; }, /** * locate DMS coordinates on map * * @private */ _locateDMSCoordinates : function () { // on est toujours en coordonnées geographiques... var oLatLon = { lat : this._getCoordinate("Lat"), lng : this._getCoordinate("Lon") }; if (!this.validateExtentCoordinate("Lon", oLatLon.lng)) { return; } if (!this.validateExtentCoordinate("Lat", oLatLon.lat)) { return; } // FIXME https://github.com/Leaflet/Leaflet/issues/922 var map = this._map; map.panTo(oLatLon); }, /** * locate coordinates on map (not DMS) * * @private */ _locateCoordinates : function () { // soit longitude ou soit y var lonYDom = L.DomUtil.get(this._addUID("GPmousePositionLon")).value; lonYDom = lonYDom.replace(",", "."); lonYDom = parseFloat(lonYDom); if (isNaN(lonYDom)) { return; } // soit lattitude ou soit x var latXDom = L.DomUtil.get(this._addUID("GPmousePositionLat")).value; latXDom = latXDom.replace(",", "."); latXDom = parseFloat(latXDom); if (isNaN(latXDom)) { return; } var lon = null; var lat = null; var x = null; var y = null; if (this._currentProjectionType === "Geographical") { lon = lonYDom; lat = latXDom; } else { x = latXDom; y = lonYDom; } if (!this.validateExtentCoordinate("Lon", lon || x)) { return; } if (!this.validateExtentCoordinate("Lat", lat || y)) { return; } var unit = this._currentProjectionUnits; var oLatLon = this._unproject({ x : this._convertCoordinate(lon !== null ? lon : x, unit), y : this._convertCoordinate(lat !== null ? lat : y, unit) }); // FIXME https://github.com/Leaflet/Leaflet/issues/922 var map = this._map; map.panTo(oLatLon); }, /** * locate coordinates on map * * @method locate * @private */ onMousePositionEditModeLocateClick : function () { if (!this.options.editCoordinates) { return; } if (!this._isEditing) { this.onMousePositionEditModeClick(true); return; } (this._currentProjectionUnits === "DMS") ? this._locateDMSCoordinates() : this._locateCoordinates(); }, /** * this method is called by event 'change' on 'GPmousePositionProjectionSystem' * tag select (cf. this._createMousePositionSettingsElement), * and selects the system projection. * * @param {Object} e - HTMLElement * * @private */ onMousePositionProjectionSystemChange : function (e) { logger.log("onMousePositionProjectionSystemChange", e); var idx = e.target.selectedIndex; // index var value = e.target.options[idx].value; // crs, ex. MERCATOR (optionnel) var label = e.target.options[idx].label; // etiquette, ex Géographiques logger.log(idx, value, label); this._setCurrentSystem(value); }, /** * this method selects the current system projection. * * @param {Object} systemCode - inner code (rank in array _projectionSystems) * * @private */ _setCurrentSystem : function (systemCode) { // si on change de type de systeme, on doit aussi changer le type d'unités ! var type = null; for (var i = 0; i < this._projectionSystems.length; ++i) { if (this._projectionSystems[i].code === Number(systemCode)) { type = this._projectionSystems[i].type; break; } } if (!type) { logger.log("system not found in projection systems container"); return; } if (type !== this._currentProjectionType) { this._setTypeUnitsPanel(type); } // on enregistre le systeme courrant this._currentProjectionSystems = this._projectionSystems[Number(systemCode)]; // on simule un deplacement en mode tactile pour mettre à jour les // resultats if (!this._isDesktop) { this.onMapMove(); } }, /** * this method is called by event 'mouseover' on 'GPmousePositionProjectionSystem' * tag select (cf. this._createMousePositionSettingsElement), * and selects the system projection. * * @param {Object} e - HTMLElement * * @private */ onMousePositionProjectionSystemMouseOver : function (e) { logger.log("onMousePositionProjectionSystemMouseOver", e); var map = this._map; if (!map) { return; } // clear select var systemList = L.DomUtil.get(this._addUID("GPmousePositionProjectionSystem")); systemList.innerHTML = ""; // add systems whose extent intersects the map extent for (var j = 0; j < this._projectionSystems.length; j++) { var proj = this._projectionSystems[j]; var option = null; if (proj.geoBBox) { // bboxes intersection test if (map.getBounds()._southWest.lng > proj.geoBBox.right || map.getBounds()._southWest.lat > proj.geoBBox.top || map.getBounds()._northEast.lng < proj.geoBBox.left || map.getBounds()._northEast.lat < proj.geoBBox.bottom ) { if (proj === this._currentProjectionSystems) { option = document.createElement("option"); option.value = proj.code; option.text = proj.label || j; option.setAttribute("selected", "selected"); option.setAttribute("disabled", "disabled"); systemList.appendChild(option); } continue; // do not intersect } } option = document.createElement("option"); option.value = proj.code; option.text = proj.label || j; if (proj === this._currentProjectionSystems) { option.setAttribute("selected", "selected"); } systemList.appendChild(option); } }, /** * this method is called by event 'change' on 'GPmousePositionProjectionUnits' * tag select (cf. this._createMousePositionSettingsElement), * and selects the units projection. * * @param {Object} e - HTMLElement * * @private */ onMousePositionProjectionUnitsChange : function (e) { logger.log("onMousePositionProjectionUnitsChange", e); var idx = e.target.selectedIndex; var value = e.target.options[idx].value; var label = e.target.options[idx].label; logger.log(idx, value, label); var oldProjectionUnits = this._currentProjectionUnits; var newProjectionUnits = this._currentProjectionUnits = value; var newProjectionType = this._currentProjectionType; // Mise a jour des elements lebels et unites this._resetLabelElements(newProjectionType); this._resetUnitElements(newProjectionUnits); // mise a jour des inputs pour les coordonnees if (oldProjectionUnits === "DMS" || newProjectionUnits === "DMS") { this._resetCoordinateElements(this.options.editCoordinates, newProjectionType, newProjectionUnits); this._setEditMode(this._isEditing); } // on simule un deplacement en mode tactile pour mettre à jour les // resultats if (!this._isDesktop) { this.onMapMove(); } }, // #####