geopf-extensions-openlayers
Version:
French Geoportal Extensions for OpenLayers libraries
1,331 lines (1,185 loc) • 64.2 kB
JavaScript
// import CSS
import "../../CSS/Controls/MousePosition/GPFmousePosition.css";
// import "../../CSS/Controls/MousePosition/GPFmousePositionStyle.css";
// import OpenLayers
// import Control from "ol/control/Control";
import Widget from "../Widget";
import Control from "../Control";
import Map from "ol/Map";
import Overlay from "ol/Overlay";
import { unByKey as olObservableUnByKey } from "ol/Observable";
import {
transform as olTransformProj,
get as olGetProj,
transformExtent as olTransformExtentProj
} from "ol/proj";
// import geoportal library access
import Gp from "geoportal-access-lib";
// import local
import Logger from "../../Utils/LoggerByDefault";
import Utils from "../../Utils/Helper";
import Interactions from "../Utils/Interactions";
import Markers from "../Utils/Markers";
import SelectorID from "../../Utils/SelectorID";
import MathUtils from "../../Utils/MathUtils";
import Draggable from "../../Utils/Draggable";
// import defs proj4 manually (cf. line 125)
// import Proj4 from "proj4";
// import { register } from "ol/proj/proj4";
// import Register from "../../Utils/Register";
// import local with ol dependencies
import CRS from "../../CRS/CRS";
// import "../CRS/AutoLoadCRS";
// DOM
import MousePositionDOM from "./MousePositionDOM";
var logger = Logger.getLogger("GeoportalMousePosition");
/**
* @classdesc
* MousePosition Control.
*
* @alias ol.control.GeoportalMousePosition
* @module GeoportalMousePosition
*/
class MousePosition extends Control {
/**
* @constructor
* @param {Object} options - options for function call.
* @param {Number} [options.id] - Ability to add an identifier on the widget (advanced option)
* @param {String} [options.apiKey] - API key. The key "calcul" is used by default.
* @param {Boolean} [options.ssl = true] - use of ssl or not (default true, service requested using https protocol)
* @param {Boolean} [options.draggable = false] - Specify if widget is draggable
* @param {Boolean} [options.collapsed = true] - Specify if MousePosition control should be collapsed at startup. Default is true.
* @param {Array} [options.units] - list of coordinates units, to be displayed in control units list.
* 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] - activate (true) or deactivate (false) the altitude panel. True by default
* @param {Boolean} [options.displayCoordinates = true] - activate (true) or deactivate (false) the coordinates panel. True by default
* @param {Boolean} [options.editCoordinates = false] - If true, coordinates from the MousePosition control can be edited by users to re-center the view. False by default.
* @param {Function} [options.mapCenterCallback] - callback...
* @param {Array} [options.systems] - list of projection systems, default are Geographical ("EPSG:4326"), Web Mercator ("EPSG:3857"), Lambert 93 ("EPSG:2154") and extended Lambert 2 ("EPSG:27572").
* 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 {Object} [options.positionMarker] - options for position marker
* @param {String} options.positionMarker.url - Marker url (define in src/Openlayers/Controls/Utils/Markers.js)
* @param {Array} options.positionMarker.offset - Offsets in pixels used when positioning the marker towards targeted point.
* The first element in the array is the horizontal offset. A positive value shifts the marker right.
* The second element in the array is the vertical offset. A positive value shifts the marker down. [0,0] value positions the top-left corner of the marker image to the targeted point.
* Default is offset associated to default marker image.
* @param {Boolean} options.positionMarker.hide - if true, marker is not displayed, otherwise displayed (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 (no data values = [-9999 ; -189999])
* @example
* var MousePosition = new ol.control.GeoportalMousePosition({
* "collapsed" : false,
* "graggable" : true,
* "displayCoordinates" : true,
* "displayAltitude" : true,
* "altitude" : {
* "triggerDelay" : 100,
* "responseDelay" : 500,
* "noDataValue" : -99999,
* "noDataValueTolerance" : 99000,
* "serviceOptions" : {}
* },
* "systems" : [
* {
* "crs" : "EPSG:3857",
* "label" : "Web Mercator",
* "type" : "Metric"
* },
* {
* "crs" : "EPSG:4326",
* "label" : "Géographiques",
* "type" : "Geographical"
* },
* {
* "label" : "Lambert 93",
* "crs" : "EPSG:2154",
* "type" : "Metric",
* "geoBBox" : {
* "left" : -9.86,
* "bottom" : 41.15,
* "right" : 10.38,
* "top" : 51.56
* }
* }
* ],
* "units" : ["DEC", "DMS"]
* });
*/
constructor (options) {
options = options || {};
// call ol.control.Control constructor
super(options);
if (!(this instanceof MousePosition)) {
throw new TypeError("ERROR CLASS_CONSTRUCTOR");
}
/**
* Nom de la classe (heritage)
* @private
*/
this.CLASSNAME = "MousePosition";
// init Proj4 defs manually
// Register.load(Proj4);
// try {
// register(Proj4);
// } catch (e) {}
this._initialize(options);
// init control DOM container
this.container = this._initContainer(this.options);
// ajout du container
(this.element) ? this.element.appendChild(this.container) : this.element = this.container;
return this;
};
/**
* Overload ol.control.Control setMap method, called when
*
* @param {Map} map - the map
*
*/
setMap (map) {
var context = this;
if (map) { // dans le cas de l'ajout du contrôle à la map
var center = this._createMapCenter();
map.getViewport().appendChild(center);
if (!this.collapsed && !this._isDesktop) {
center.className = "GPmapCenterVisible";
}
// mode "draggable"
if (this.draggable) {
Draggable.dragElement(
this._panelMousePositionContainer,
this._panelHeaderContainer,
map.getTargetElement()
);
}
// on met en place l'evenement sur la carte pour recuperer les coordonnées,
// on l'active à l'ouverture du panneau uniquement !
if (!this.collapsed) {
// evenement valable pour le mode desktop !
if (this._isDesktop) {
this.listenerKey = map.on(
"pointermove",
(e) => { this.onMouseMove(e); }
);
} else {
this.listenerKey = map.on(
"moveend",
(e) => this.onMapMove(e)
);
}
this._showMousePositionButton.setAttribute("aria-pressed", true);
}
// add overlay only if option editCoordinates is true
if (this.options.editCoordinates) {
// création de l'élément DOM
var markerDiv = document.createElement("img");
markerDiv.id = this._addUID("GPmousePositionMarker");
markerDiv.src = this._markerUrl;
markerDiv.title = "Cliquer pour supprimer";
markerDiv.addEventListener("click", function () {
context._markerOverlay.setPosition(undefined);
});
this._markerOverlay = new Overlay({
offset : this._markerOffset,
element : markerDiv,
stopEvent : false
});
map.addOverlay(this._markerOverlay);
}
} else {
olObservableUnByKey(this.listenerKey);
}
// call original setMap method
super.setMap(map);
// HACK: on arrête l'execution de la fonction...
if (map === null) {
return;
}
// position
if (this.options.position) {
this.setPosition(this.options.position);
}
// reunion du bouton avec le précédent
if (this.options.gutter === false) {
this.getContainer().classList.add("gpf-button-no-gutter");
}
// mode "collapsed"
if (!this.collapsed) {
var inputShow = document.getElementById("GPshowMousePosition-" + this._uid);
inputShow.checked = "checked";
this._setElevationPanel(this.options.displayAltitude);
this._setCoordinatesPanel(this.options.displayCoordinates);
if (!this.options.displayCoordinates) {
this._setSettingsPanel(false);
}
}
}
// ################################################################### //
// #################### user interface methods ####################### //
// ################################################################### //
/**
* Set additional projection system
*
* @param {Object} system - projection system
* @param {String} system.crs - Proj4 crs alias (from proj4 defs) e.g. "EPSG:4326"
* @param {String} [system.label] - CRS label to be displayed in control. Default is system.crs alias
* @param {String} [system.type] - CRS units type for coordinates conversion (one of control options.units). Default is "Metric"
*/
addSystem (system) {
if (typeof system !== "object") {
logger.log("[ERROR] MousePosition:addSystem - system parameter should be an object");
return;
}
if (!system.crs) {
logger.error("crs not defined !");
return;
}
if (!system.label) {
logger.warn("crs label not defined, use crs code by default.");
system.label = system.crs;
}
if (!system.type) {
logger.warn("type srs not defined, use 'Metric' by default.");
system.type = "Metric";
}
// chargement de la definition de la projection
// même si déjà chargé...
CRS.loadByName(system.crs);
if (!olGetProj(system.crs)) {
logger.error("crs '{}' not available into proj4 definitions !", system.crs);
return;
}
// 1. add system to control systems
for (var j = 0; j < this._projectionSystems.length; j++) {
var obj = this._projectionSystems[j];
if (system.crs === obj.crs) {
// warn user
logger.info("crs '{}' already configured", obj.crs);
}
}
system.code = this._projectionSystems.length;
this._projectionSystems.push(system);
// 2. add system settings option to container (if it was already build)
var selectSystem = document.getElementById("GPmousePositionProjectionSystem-" + this._uid);
if (selectSystem) {
var option = document.createElement("option");
option.value = system.code;
option.text = system.label;
selectSystem.appendChild(option);
}
}
/**
* Set additional projection systems
*
* @param {Array} systems - Array of system object, with following properties :
* @param {String} systems.crs - Proj4 CRS alias (from proj4 defs) e.g. "EPSG:4326"
* @param {String} systems.label - CRS label (for coordinates conversion)
* @param {String} systems.type - CRS units type to be displayed in control (one of control options.units). Default is "Metric"
*/
addSystems (systems) {
if (!systems) {
return;
}
if (!Array.isArray(systems)) {
logger.log("[ERROR] MousePosition:addSystems - systems parameter should be an array");
return;
}
for (var i = 0; i < systems.length; i++) {
this.addSystem(systems[i]);
}
}
/**
* Remove projection system (in case there are several system with same code, only the first one will be removed)
*
* @param {String} systemCrs - CRS alias (from proj4 defs)
*/
removeSystem (systemCrs) {
if (!systemCrs || typeof systemCrs !== "string") {
logger.log("[ERROR] MousePosition:removeSystem - systemCode parameter should be a string");
return;
}
var systemList = document.getElementById("GPmousePositionProjectionSystem-" + this._uid);
var systemCode = null;
// find system in control projection systems list
for (var i = 0; i < this._projectionSystems.length; i++) {
var proj = this._projectionSystems[i];
if (systemCrs === proj.crs) {
systemCode = proj.code;
// remove system from control projection systems list
this._projectionSystems.splice(i, 1);
break;
}
}
if (systemCode == null) {
logger.log("[WARN] MousePosition:removeSystem - system not found");
return;
}
/* re-initialization of codes */
var oldNewCodeMap = [];
for (var j = 0; j < this._projectionSystems.length; j++) {
oldNewCodeMap[Number(this._projectionSystems[j].code)] = j;
this._projectionSystems[j].code = j;
}
/* find system in control container systems list */
var indexChildToRemove = null;
for (var k = 0; k < systemList.childNodes.length; k++) {
if (systemCode === systemList.childNodes[j].value) {
indexChildToRemove = k;
continue;
}
systemList.childNodes[j].value = oldNewCodeMap[Number(systemList.childNodes[j].value)];
}
/* remove system from control container systems list */
if (indexChildToRemove != null) {
systemList.removeChild(systemList.childNodes[indexChildToRemove]);
}
// choose arbitrarily a new current system if needed
if (this._currentProjectionSystems.code === Number(systemCode)) {
systemList.childNodes[0].setAttribute("selected", "selected");
this._setCurrentSystem(systemList.childNodes[0].value);
}
}
/**
* Set control units (to be displayed)
*
* @param {Array} units - list of all coordinates units, to be displayed in control units list.
* Values may be "DEC" (decimal degrees), "DMS" (sexagecimal), "RAD" (radians) and "GON" (grades) for geographical coordinates,
* and "M" or "KM" for metric coordinates
*/
setUnits (units) {
if (!units || !Array.isArray(units)) {
return;
}
this.options.units = units;
this._projectionUnits = [];
this._initProjectionUnits();
if (this._currentProjectionType) {
this._setTypeUnitsPanel(this._currentProjectionType);
}
}
/**
* Set control altitude options (useless if displayAltitude == false)
*
* @param {Object} options - altitude options
* @param {Object} [options.serviceOptions] - options of elevation service
* @param {Number} [options.responseDelay] - latency for elevation request, 500 ms by default
* @param {Number} [options.triggerDelay] - immobilisation time of movement on the map to trigger the elevation calculation, 200 ms by default
*/
setAltitudeOptions (options) {
if (!options || typeof options !== "object") {
return;
}
this.options.altitude.triggerDelay = options.triggerDelay;
this.options.altitude.responseDelay = options.responseDelay;
if (options.serviceOptions) {
for (var opt in options.serviceOptions) {
if (options.serviceOptions.hasOwnProperty(opt)) {
this.options.altitude.serviceOptions[opt] = options.serviceOptions[opt];
}
}
}
}
/**
* Display or hide elevation panel
*
* @param {Boolean} displayAltitude - true to display elevation panel, false to hide it
*/
displayAltitude (displayAltitude) {
if (displayAltitude === undefined) {
return;
}
this.options.displayAltitude = displayAltitude;
this._setElevationPanel(displayAltitude);
}
/**
* Display or hide coordinates panel
*
* @param {Boolean} displayCoordinates - true to display coordinates panel, false to hide it
*/
displayCoordinates (displayCoordinates) {
if (displayCoordinates === undefined) {
return;
}
this.options.displayCoordinates = displayCoordinates;
this._setCoordinatesPanel(displayCoordinates);
this._setSettingsPanel(displayCoordinates);
}
/**
* Collapse or display control main container
*
* @param {Boolean} collapsed - True to collapse control, False to display it
*/
setCollapsed (collapsed) {
if (collapsed === undefined) {
logger.log("[ERROR] MousePosition:setCollapsed - missing collapsed parameter");
return;
}
if ((collapsed && this.collapsed) || (!collapsed && !this.collapsed)) {
return;
}
if (!this._isDesktop) {
document.getElementById("GPmapCenter").className = collapsed ? "" : "GPmapCenterVisible";
}
// on simule l'ouverture du panneau après un click
this.onShowMousePositionClick();
this._showMousePositionContainer.checked = !collapsed;
}
/**
* Get container
*
* @returns {HTMLElement} container
*/
getContainer () {
return this.container;
}
// ################################################################### //
// ######################## initialize control ####################### //
// ################################################################### //
/**
* Initialize control (called by MousePosition constructor)
*
* @param {Object} options - control options (set by user)
* @private
*/
_initialize (options) {
// Set default options
options = options || {};
// {Object} control options - set by user or by default
this.options = options;
this.options.collapsed = (options.collapsed !== undefined) ? options.collapsed : true;
/** {Boolean} specify if MousePosition control is collapsed (true) or not (false) */
this.collapsed = this.options.collapsed;
this.options.draggable = (options.draggable !== undefined) ? options.draggable : false;
/** {Boolean} specify if MousePosition control is draggable (true) or not (false) */
this.draggable = this.options.draggable;
// position marker
/** @private */
this._markerOverlay = null;
/** @private */
this._markerUrl = null;
/** @private */
this._markerOffset = [0, 0];
/** @private */
this._hideMarker = false;
this._initMarker(options.positionMarker);
this.options.units = options.units || [];
this.options.displayAltitude = (options.displayAltitude !== undefined) ? options.displayAltitude : true;
this.options.displayCoordinates = (options.displayCoordinates !== undefined) ? options.displayCoordinates : true;
if (this.options.displayCoordinates) {
this.options.editCoordinates = (options.editCoordinates !== undefined) ? options.editCoordinates : false;
} else {
// si les coordonnées ne sont pas affichées : pas besoin de les éditer...
this.options.editCoordinates = false;
}
this.editing = false;
this.options.systems = options.systems || [];
if (options.altitude) {
var altitude = options.altitude;
this.options.altitude = {
triggerDelay : (altitude.triggerDelay !== undefined) ? altitude.triggerDelay : 200,
responseDelay : (altitude.responseDelay !== undefined) ? altitude.responseDelay : 500,
serviceOptions : altitude.serviceOptions || {},
noDataValue : (altitude.noDataValue !== undefined) ? altitude.noDataValue : -99999,
noDataValueTolerance : (altitude.noDataValueTolerance !== undefined) ? altitude.noDataValueTolerance : 90000
};
} else {
this.options.altitude = {
triggerDelay : 200,
responseDelay : 500,
serviceOptions : {}
};
}
// identifiant du contrôle :
// utile pour suffixer les identifiants CSS
// (pour gérer le cas où il y en a plusieurs dans la même page)
/** @private */
this._uid = this.options.id || SelectorID.generate();
// initialisation des systemes de projections
/** @private */
this._projectionSystems = [];
this._initProjectionSystems();
// initialisation des systemes des unités
/** @private */
this._projectionUnits = {};
this._initProjectionUnits();
// detection du support : desktop ou tactile
/** @private */
this._isDesktop = Utils.detectSupport();
// on met en place un seuil sur le timer
if (this.options.altitude.triggerDelay < 100) {
this.options.altitude.triggerDelay = 100;
}
// {Number} timer on movestopped delay (altitude calculation)
/** @private */
this._timer = this.options.altitude.triggerDelay;
// {Object} Selected projection system
/** @private */
this._currentProjectionSystems = this._projectionSystems[0];
// {String} Selected projection units typs : Geographical or metric
/** @private */
this._currentProjectionType = this._projectionSystems[0].type;
// {String} Selected projection unit
/** @private */
this._currentProjectionUnits = this._projectionUnits[this._currentProjectionType][0].code;
// {Object} Projection units container (DOM Element)
/** @private */
this._projectionUnitsContainer = null;
// {Object} control panel container (DOM Element)
/** @private */
this._showMousePositionContainer = null;
/** @private */
this._panelMousePositionContainer = null;
/** @private */
this._panelHeaderMousePositionContainer = null;
// gestion de l'affichage du panneau de l'altitude
if (!this.options.displayAltitude && !this.options.displayCoordinates) {
// on reactive l'affichage des coordonnées, pour ne pas afficher un panneau vide !
this.options.displayCoordinates = true;
}
// listener key for event on pointermove or moveend map
this.listenerKey = null;
}
/**
*
* @param {Object} option - positionMarker option
* @private
*/
_initMarker (option) {
if (!this.options.editCoordinates) {
return;
}
if (!option) {
this._markerUrl = Markers["lightOrange"];
this._markerOffset = Markers.defaultOffset;
return;
}
// hide
this._hideMarker = (option.hide !== undefined) ? option.hide : false;
// offset
if (option.offset) {
if (Array.isArray(option.offset) && option.offset.length === 2) {
this._markerOffset = option.offset;
} else {
logger.log("positionMarker.offset should be an array. e.g. : [0,0]");
this._markerOffset = Markers.defaultOffset;
}
} else {
this._markerOffset = Markers.defaultOffset;
}
var url = option.url;
if (!url) {
this._markerUrl = Markers["lightOrange"];
} else if (url.match(/^[a-zA-Z]+$/)) { // un seul mot
this._markerUrl = (Markers[url] !== undefined) ? Markers[url] : Markers["lightOrange"];
} else {
this._markerUrl = url;
}
}
/**
* this method is called by the constructor and initialize the projection
* systems.
* getting coordinates in the requested projection :
* see this.onMousePositionProjectionSystemChange()
*
* @private
*/
_initProjectionSystems () {
// on donne la possibilité à l'utilisateur de modifier
// la liste des systèmes à afficher
// Ex. this.options.systems
// FIXME doit on charger des projections par defaut dans ce composant ?
// chargement des projections par defaut
// CRS.loadByDefault();
// CRS.overload();
// systemes de projection disponible par defaut
var projectionSystemsByDefault = [{
label : "G\u00e9ographique",
crs : olGetProj("EPSG:4326").getCode(),
type : "Geographical"
}, {
label : "Web Mercator",
crs : olGetProj("EPSG:3857").getCode(),
type : "Metric"
}, {
label : "Lambert 93",
crs : olGetProj("EPSG:2154").getCode(),
type : "Metric",
geoBBox : {
left : -9.86,
bottom : 41.15,
right : 10.38,
top : 51.56
}
}, {
label : "Lambert II \u00e9tendu",
crs : olGetProj("EPSG:27572").getCode(),
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];
this.addSystem(sys);
}
if (this._projectionSystems.length === 0) {
// on ajoute les systèmes de projections par défaut
for (var j = 0; j < projectionSystemsByDefault.length; j++) {
this.addSystem(projectionSystemsByDefault[j]);
}
}
}
/**
* this method is called by the constructor and initialize the units.
* getting coordinates in the requested units :
* see this.onMousePositionProjectionUnitsChange()
*
* @private
*/
_initProjectionUnits () {
// 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 : MathUtils.coordinateToDecimal
}, {
code : "DMS",
label : "Degrés sexagésimaux",
format : MathUtils.coordinateToDMS
}, {
code : "RAD",
label : "Radians",
format : MathUtils.coordinateToRad
}, {
code : "GON",
label : "Grades",
format : MathUtils.coordinateToGon
}],
Metric : [{
code : "M",
label : "Mètres",
format : MathUtils.coordinateToMeter
}, {
code : "KM",
label : "Kilomètres",
format : MathUtils.coordinateToKMeter
}]
};
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 (typeof this._projectionUnits === "object" && Object.keys(this._projectionUnits).length === 0) {
this._projectionUnits = projectionUnitsByDefault;
}
}
/**
* this method get label from the current projection units
*
* @returns {String} projection information
*
* @private
*/
_getCurrentProjectionInformation () {
var systemInfo = [
this._currentProjectionSystems.label,
"en"
];
var units = this._projectionUnits[this._currentProjectionType];
for (var u = 0; u < units.length; ++u) {
if (units[u].code === this._currentProjectionUnits) {
systemInfo.push(units[u].label);
break;
}
}
return systemInfo.join(" ");
}
// ################################################################### //
// ######################## methods handle dom ####################### //
// ################################################################### //
/**
* Create control main container (called by MousePosition constructor)
*
* @returns {HTMLElement} DOM element
*
* @private
*/
_initContainer () {
// creation du container principal
var container = this._createMainContainerElement();
// create ReverseGeocode picto
var picto = this._showMousePositionButton = this._createShowMousePositionPictoElement();
container.appendChild(picto);
// panel
var mousePositionPanel = this._panelMousePositionContainer = this._createMousePositionPanelElement();
var mousePositionPanelDiv = this._createMousePositionPanelDivElement();
mousePositionPanel.appendChild(mousePositionPanelDiv);
// header
var panelHeader = this._panelHeaderContainer = this._createMousePositionPanelHeaderElement();
// panel title
var panelTitle = this._panelTitleContainer = this._createMousePositionPanelTitleElement();
panelHeader.appendChild(panelTitle);
// close picto
var closeDiv = this._panelCloseButton = this._createMousePositionPanelCloseElement();
panelHeader.appendChild(closeDiv);
mousePositionPanelDiv.appendChild(panelHeader);
var basic = this._createMousePositionPanelBasicElement(
this.options.displayAltitude,
this.options.displayCoordinates,
this.options.editCoordinates,
this._currentProjectionUnits
);
mousePositionPanelDiv.appendChild(basic);
var buttonSettings = this._createShowMousePositionSettingsElement(this.options.displayCoordinates);
mousePositionPanelDiv.appendChild(buttonSettings);
var settings = this._createMousePositionSettingsElement();
var systems = this._projectionSystemsContainer = this._createMousePositionSettingsSystemsElement(this._projectionSystems);
var units = this._projectionUnitsContainer = this._createMousePositionSettingsUnitsElement(this._projectionUnits[this._currentProjectionType]);
var settingsAccordion = this._createMousePositionSettingsAccordion([systems, units]);
settings.appendChild(settingsAccordion);
mousePositionPanelDiv.appendChild(settings);
container.appendChild(mousePositionPanel);
return container;
}
/**
* this method is called by this.()
* and it changes the elevation view panel into the dom.
*
* @param {Boolean} active - true:active, false:disable
* @private
*/
_setElevationPanel (active) {
var div = null;
if (!active) {
div = document.getElementById("GPmousePositionAltitude-" + this._uid);
div.style.display = "none";
} else {
div = document.getElementById("GPmousePositionAltitude-" + this._uid);
div.style.display = "";
}
}
/**
* this method is called by this.()
* and it changes the coordinate view panel into the dom.
*
* @param {Boolean} active - true:active, false:disable
* @private
*/
_setCoordinatesPanel (active) {
var div = document.getElementById("GPmousePositionCoordinate-" + this._uid);
if (!active) {
div.style.display = "none";
} else {
div.style.display = "";
}
}
/**
* this method is called by this.()
* and it changes the settings view panel into the dom.
*
* @param {Boolean} active - true:active, false:disable
* @private
*/
_setSettingsPanel (active) {
var divPicto = document.getElementById("GPshowMousePositionSettingsPicto-" + this._uid);
var divPanel = document.getElementById("GPmousePositionSettings-" + this._uid);
if (!active) {
divPicto.style.display = "none";
divPanel.style.display = "none";
} else {
divPicto.style.display = "";
divPanel.style.display = "";
}
}
/**
* this method is called by this.onMousePositionProjectionSystemChange()
* when changes to a metric or a geographical units.
*
* @param {String} type - Geographical or Metric
* @private
*/
_setTypeUnitsPanel (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.editing);
}
// 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 = projectionUnits;
}
// ################################################################### //
// ##################### handlers events to control ################## //
// ################################################################### //
/**
* this sends the coordinates to the panel.
* (cf. this.GPdisplayCoords() into the DOM functions)
*
* @param {Array} olCoordinate - ol.Coordinate object [lon, lat]
* @param {Object} crs - coordinate CRS (ol.proj.Projection)
* @private
*/
_setCoordinate (olCoordinate, crs) {
// structure
// ol.Coordinate
// [
// 4 // lon
// 48 // lat
// ]
// 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.log("ERROR : system crs not found");
return;
}
// on reprojette les coordonnées depuis leur CRS d'origine (CRS) vers le CRS demandé (oSrs)
olCoordinate = olTransformProj(olCoordinate, crs, oSrs);
// type de systeme : Geographical ou Metric
var type = this._currentProjectionSystems.type;
// on recherche la fonction de formatage dans l'unité 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;
}
}
if (!format || typeof format !== "function") {
logger.log("WARNING : coordinates format function not found");
return;
} else {
coordinate = format(olCoordinate);
}
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 {Array} olCoordinate - ol.Coordinate object [lon, lat]
* @private
*/
_setElevation (olCoordinate) {
// 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(olCoordinate, delay, noDataValue, noDataValueTolerance);
}
/**
* this method is triggered when the mouse or the map is stopped.
* (cf. onMouseMove and onMapMove)
*
* @param {Array} olCoordinate - ol.Coordinate object [lon, lat]
* @param {Object} crs - coordinate CRS (ol.proj.Projection)
* @private
*/
onMoveStopped (olCoordinate, crs) {
// reprojection en CRS:84 (EPSG:4326) pour le calcul alti
var oLatLng = olTransformProj(olCoordinate, crs, "EPSG:4326");
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 (e) {
var self = this;
const event = e.originalEvent || e;
if (this._panelMousePositionContainer.contains(event.target)) {
// Le curseur est sur le panneau, on ne fait rien
return;
}
// info: coordinate = [x, y]
var coordinate = e.coordinate;
if (!e.map || !e.map.getView()) {
return;
}
var crs = e.map.getView().getProjection();
this._setCoordinate(coordinate, crs);
// calcul de l'altitude après un certain délai après l'arrêt du mouvement de la souris
clearTimeout(this._timer);
this._timer = setTimeout(function () {
self.onMoveStopped(coordinate, crs);
}, 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 () {
var self = this;
var map = this.getMap();
if (!map || !map.getView()) {
return;
}
var view = map.getView();
var coordinate = view.getCenter();
var crs = view.getProjection();
this._setCoordinate(coordinate, crs);
// calcul de l'altitude après un certain délai après l'arrêt du mouvement de la souris
clearTimeout(this._timer);
this._timer = setTimeout(function () {
self.onMoveStopped(coordinate, crs);
}, this.options.altitude.triggerDelay);
}
// ################################################################### //
// ####################### handlers events to dom #################### //
// ################################################################### //
/**
* this method is called by this.GPdisplayElevation() in the dom, and
* it executes a request to the elevation service.
*
* @param {Object} coordinate - {lat:..., lng:...}
* @param {Function} callback - callback
* @private
*/
onRequestAltitude (coordinate, callback) {
// 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.
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;
}
// on recupere les options du service
var options = this.options.altitude.serviceOptions || {};
// gestion du protocole et du timeout
// le timeout est indispensable sur le protocole JSONP.
var _protocol = options.protocol || "XHR";
var _timeout = options.timeOut || 0;
if (_protocol === "JSONP" && _timeout === 0) {
_timeout = 15000;
}
// format de sortie si spécifié
var _outputFormat = options.outputFormat || "json";
// ainsi que les coordonnées : si l'utilisateur explicite zonly false
var _zonly = true;
// cela permet d'activer l'option measures côté service d'alti (surchargée si zonly = true)
var _zonly;
if (options.zonly === false) {
_zonly = options.zonly;
} else {
_zonly = true;
}
// récupération d'une réponse complète avec source et précision
var _measures = options.measures || false;
var _positions = [{
lon : coordinate[0],
lat : coordinate[1]
}];
// utilisation d'une ressource spécifique
var _resource = options.resource;
// et les callbacks
var _scope = this;
var _rawResponse = options.rawResponse || false;
var _customOnSuccess = options.onSuccess || null;
var _onSuccess = null;
var _onFailure = null;
if (!_rawResponse) {
// dans le cas général
// callback onSuccess
_onSuccess = function (results) {
if (results && Object.keys(results).length) {
if (_customOnSuccess) {
_customOnSuccess.call(this, results);
}
callback.call(this, results.elevations[0].z);
}
};
} else {
// callback onSuccess
_onSuccess = function (results) {
if (_customOnSuccess) {
_customOnSuccess.call(this, results);
}
logger.log("alti service raw response : ", results);
};
}
// callback onFailure
_onFailure = function (error) {
logger.log("[getAltitude] 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.
var _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)
if (typeof options.ssl !== "boolean") {
if (typeof this.options.ssl === "boolean") {
options.ssl = this.options.ssl;
} else {
options.ssl = true;
}
}
var _ssl = options.ssl;
Gp.Services.getAltitude({
apiKey : _apiKey,
protocol : _protocol,
ssl : _ssl,
timeOut : _timeout,
scope : _scope,
outputFormat : _outputFormat,
rawResponse : _rawResponse,
onSuccess : _onSuccess,
onFailure : _onFailure,
zonly : _zonly,
measures : _measures,
resource : _resource,
positions : _positions
});
}
/**
* this method is called by event 'click' on 'GPshowMousePositionPicto' tag label
* (cf. this._createShowMousePositionPictoElement),
* and toggles event 'mousemove' on map.
*
* @param { event } e évènement associé au clic
* @private
*/
onShowMousePositionClick (e) {
if (e.target.ariaPressed === "true") {
this.onPanelOpen();
}
// checked : true - panel close
// checked : false - panel open
var map = this.getMap();
// on supprime toutes les interactions
Interactions.unset(map);
var opened = this._showMousePositionButton.ariaPressed;
this.collapsed = !(opened === "true");
// on génère nous même l'evenement OpenLayers de changement de propriété
// (utiliser mousePosition.on("change:collapsed", function(e) ) pour s'abonner à cet évènement)
this.dispatchEvent("change:collapsed");
// on recalcule la position
if (this.options.position && !this.collapsed) {
this.updatePosition(this.options.position);
}
// evenement declenché à l'ouverture/fermeture du panneau,
// et en fonction du mode : desktop ou tactile !
if (opened === "false") {
olObservableUnByKey(this.listenerKey);
} else if (!this.editing) {
if (this._isDesktop) {
this.listenerKey = map.on("pointermove", (e) => { this.onMouseMove(e); });
} else {
this.listenerKey = map.on("moveend", (e) => this.onMapMove(e));
// on simule un deplacement en mode tactile
this.onMapMove();
}
}
// FIXME
// on gère l'affichage des panneaux ici..., même si ce n'est pas l'endroit
// adequate...
this._setElevationPanel(this.options.displayAltitude);
this._setCoordinatesPanel(this.options.displayCoordinates);
if (!this.options.displayCoordinates) {
this._setSettingsPanel(false);
}
}
/**
* this method is called by event 'click' on 'GPshowMousePositionPicto' tag label
* (cf. this._createShowMousePositionPictoElement),
* and toggles event 'mousemove' on map.
*
* @param {Object} e - HTMLElement
* @private
*/
onShowMousePositionSettingsClick (e) {
if (!this.draggable) {
var opened = e.target.ariaPressed;
if (opened === "true") {
// somme stuff...
}
}
}
/**
* this method is called by event 'click' on input coordinate
*
* @param {Boolean} editing - editing mode
* @private
*/
onMousePositionEditModeClick (editing) {
if (!this.options.editCoordinates) {
return;