geoportal-extensions-openlayers
Version:

1,228 lines (1,140 loc) • 87.8 kB
JavaScript
// import CSS
import "../CSS/Controls/Drawing/GPdrawingOpenLayers.css";
// import OpenLayers
import Control from "ol/control/Control";
import { unByKey as olObservableUnByKey } from "ol/Observable";
import Collection from "ol/Collection";
import Overlay from "ol/Overlay";
import { transform as olTransformProj } from "ol/proj";
import VectorSource from "ol/source/Vector";
import VectorLayer from "ol/layer/Vector";
import {
Fill,
Icon,
Stroke,
Style,
Text,
Circle
} from "ol/style";
import {
LineString,
Point,
Polygon,
MultiLineString,
MultiPoint,
MultiPolygon
} from "ol/geom";
// FIXME not include into ol/geom !?
import LinearRing from "ol/geom/LinearRing";
import {
Select as SelectInteraction,
Modify as ModifyInteraction,
Draw as DrawInteraction
} from "ol/interaction";
import {
singleClick as eventSingleClick,
pointerMove as eventPointerMove
} from "ol/events/condition";
import {
getArea as olGetAreaSphere,
getDistance as olGetDistanceSphere
} from "ol/sphere";
// import local
import Logger from "../../Common/Utils/LoggerByDefault";
import Interactions from "./Utils/Interactions";
import MarkersOther from "./Utils/MarkersOther";
import Draggable from "../../Common/Utils/Draggable";
import SelectorID from "../../Common/Utils/SelectorID";
import Color from "../../Common/Utils/ColorUtils";
// DOM
import DrawingDOM from "../../Common/Controls/DrawingDOM";
// import local with ol dependencies
import KMLExtended from "../Formats/KML";
import GeoJSONExtended from "../Formats/GeoJSON";
import GPXExtended from "../Formats/GPX";
import LayerSwitcher from "./LayerSwitcher";
var logger = Logger.getLogger("Drawing");
/**
* @classdesc
*
* Drawing Control.
*
* @constructor
* @alias ol.control.Drawing
* @type {ol.control.Drawing}
* @extends {ol.control.Control}
* @param {Object} options - options for function call.
* @param {Boolean} [options.collapsed = true] - Specify if Drawing control should be collapsed at startup. Default is true.
* @param {Boolean} [options.draggable = false] - Specify if widget is draggable
* @param {Object} [options.layer = {}] - Openlayers layer that will hosts created features. If none, an empty vector layer will be created.
* @param {Object} [options.popup = {}] - Popup informations
* @param {Boolean} [options.popup.display = true] - Specify if popup is displayed when create a drawing
* @param {Function} [options.popup.function] - Function to display popup informations if you want to cutomise it. You may also provide your own function with params : {geomType / feature / saveFunc(message) / closeFunc()}. This function must return the DOM object of the popup content.
* @param {Object} [options.layerDescription = {}] - Layer informations to be displayed in LayerSwitcher widget (only if a LayerSwitcher is also added to the map)
* @param {String} [options.layerDescription.title = "Croquis"] - Layer title to be displayed in LayerSwitcher
* @param {String} [options.layerDescription.description = "Mon croquis"] - Layer description to be displayed in LayerSwitcher
* @param {Object} options.tools - Tools to display in the drawing toolbox. All by default.
* @param {Boolean} [options.tools.points = true] - Display points drawing tool
* @param {Boolean} [options.tools.lines = true] - Display lines drawing tool
* @param {Boolean} [options.tools.polygons = true] - Display polygons drawing tool
* @param {Boolean} [options.tools.holes = false] - Display polygons with holes drawing tool
* @param {Boolean} [options.tools.text = true] - Display text drawing tool
* @param {Boolean} [options.tools.remove = true] - Display feature removing tool
* @param {Boolean} [options.tools.display = true] - Display style editing tool
* @param {Boolean} [options.tools.tooltip = true] - Display text editing tool
* @param {Boolean} [options.tools.edit = true] - Display editing tool
* @param {Boolean} [options.tools.export = true] - Display exporting tool
* @param {Boolean} [options.tools.measure = false] - Display measure drawing into popup info
* @param {String} [options.labels] - Labels for Control
* @param {String} [options.labels.control] - Label for Control
* @param {String} [options.labels.points] - Label for points drawing tool
* @param {String} [options.labels.lines] - Label for lines drawing tool
* @param {String} [options.labels.polygons] - Label for polygons drawing tool
* @param {String} [options.labels.holes] - Label for polygons with holes drawing tool
* @param {String} [options.labels.text] - Label for text drawing tool
* @param {String} [options.labels.edit] - Label for editing tool
* @param {String} [options.labels.display] - Label for style editing tool
* @param {String} [options.labels.tooltip] - Label for text editing tool
* @param {String} [options.labels.remove] - Label for feature removing tool
* @param {String} [options.labels.export] - Label for exporting tool.
* @param {String} [options.labels.exportTitle] - Title for exporting tool.
* @param {String} [options.labels.applyToObject] - Label for apply to object button.
* @param {String} [options.labels.saveDescription] - Label for save description button.
* @param {String} [options.labels.setAsDefault] - Label for set as default style button.
* @param {String} [options.labels.strokeColor] - Label for stroke color.
* @param {String} [options.labels.strokeWidth] - Label for stroke width.
* @param {String} [options.labels.fillColor] - Label for fill color.
* @param {String} [options.labels.fillOpacity] - Label for fillOpacity.
* @param {String} [options.labels.markerSize] - Label for markerSize.
* @param {Array.<Object>} [options.markersList = [{"src" : "data:image/png;base64,xxxx", "anchor" : [0.5,1]}]] - List of markers src to be used for points with their anchor offsets See {@link http://openlayers.org/en/latest/apidoc/ol.style.Icon.html OpenLayers params} for anchor offset options.
* @param {Object} options.defaultStyles - Default styles applying to geometries (labels, lines and polygons).
* @param {String} [options.defaultStyles.textFillColor = "#000000"] - Text fill color for labels (RGB hex value).
* @param {String} [options.defaultStyles.textStrokeColor = "#FFFFFF"] - Text surrounding color for labels (RGB hex value).
* @param {String} [options.defaultStyles.strokeColor = "#ffcc33"] - Stroke color (RGB hex value).
* @param {Number} [options.defaultStyles.strokeWidth = 2] - Stroke width in pixels.
* @param {String} [options.defaultStyles.polyStrokeColor = "#ffcc33"] - Stroke color (RGB hex value) for polygons.
* @param {Number} [options.defaultStyles.polyStrokeWidth = 2] - Stroke width in pixels for polygons.
* @param {String} [options.defaultStyles.polyFillColor = "#ffffff"] - Polygons fill color (RGB hex value).
* @param {Number} [options.defaultStyles.polyFillOpacity = 0.2] - Polygon fill opacity (alpha value between 0:transparent and 1:opaque).
* @param {Object} options.cursorStyle - cursor (circle) style when drawing or editing.
* @param {String} [options.cursorStyle.fillColor = "rgba(0, 153, 255, 1)"] - Cursor fill color.
* @param {String} [options.cursorStyle.strokeColor = "#FFF"] - Cursor stroke color.
* @param {String} [options.cursorStyle.strokeWidth = 1] - Cursor surrounding stroke width.
* @param {String} [options.cursorStyle.radius = 6] - Cursor radius.
* @example
* var drawing = new ol.control.Drawing({
* collapsed : false,
* draggable : true,
* layerswitcher : {
* title : "Dessins",
* description : "Mes dessins..."
* },
* markersList : [{
* src : "http://api.ign.fr/api/images/api/markers/marker_01.png",
* anchor : [0.5, 1]
* }],
* defaultStyles : {},
* cursorStyle : {},
* tools : {
* points : true,
* lines : true,
* polygons :true,
* holes : true,
* text : false,
* remove : true,
* display : true,
* tooltip : true,
* export : true,
* measure : true
* },
* popup : {
* display : true,
* function : function (params) {
* var container = document.createElement("div");
* // - params.geomType;
* // - params.feature;
* // Les 2 fonctions ferment la popup avec ou sans sauvegarde des informations
* // dans les properties de la feature (key : description)
* // - params.saveFunc(message);
* // - params.closeFunc();
* return container;
* }
* });
*/
var Drawing = (function (Control) {
/**
* See {@link ol.control.Drawing}
* @module Drawing
* @alias module:~Controls/Drawing
* @param {*} options - options
* @example
* import Drawing from "src/OpenLayers/Controls/Drawing"
*/
function Drawing (options) {
options = options || {};
if (!(this instanceof Drawing)) {
throw new TypeError("ERROR CLASS_CONSTRUCTOR");
}
this._initialize(options);
// init control DOM container
this._container = this._initContainer();
// call ol.control.Control constructor
Control.call(this, {
element : this._container,
target : options.target,
render : options.render
});
};
// Inherits from ol.control.Control
// olInherits(Drawing, Control);
if (Control) Drawing.__proto__ = Control;
/**
* Default tools to display for widget
*
* @private
*/
Drawing.DefaultTools = {
points : true,
lines : true,
polygons : true,
holes : false,
text : true,
remove : true,
display : true,
tooltip : true,
edit : true,
export : true,
measure : false
};
/**
* Default labels for widget
*
* @private
*/
Drawing.DefaultLabels = {
control : "Annoter la carte",
creatingTools : "Outils de création",
points : "Placer des points",
lines : "Dessiner des lignes",
polygons : "Dessiner des polygones",
holes : "Créer des trous sur un polygone",
text : "Ecrire sur la carte",
editingTools : "Outils d'édition",
edit : "Editer les tracés",
display : "Modifier l'apparence des objets",
tooltip : "Modifier les textes / infos-bulles",
remove : "Supprimer des objets",
export : "Exporter",
exportTitle : "Exporter en KML",
applyToObject : "Appliquer à l'objet",
saveDescription : "Enregistrer",
setAsDefault : "Définir par défaut",
strokeColor : "Couleur du trait : ",
strokeWidth : "Epaisseur du trait : ",
fillColor : "Couleur de remplissage : ",
fillOpacity : "Opacité du remplissage : ",
markerSize : "Taille du pictogramme : ",
markerColor : "Couleur du pictogramme : ",
labelDisplay : "Afficher le label : "
};
/**
* Default styles applyied to drawn features.
*
* @private
*/
Drawing.DefaultStyles = {
textFillColor : "#000000",
textStrokeColor : "#FFFFFF",
textStrokeWidth : 6,
// INFO : cette option n'est pas surchargeable via les options du constructeur !
textIcon1x1 : {
src : "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR4nGNiYAAAAAkAAxkR2eQAAAAASUVORK5CYII=",
anchor : [0, 0]
},
polyFillColor : "#ffffff",
polyFillOpacity : 0.4,
polyStrokeColor : "#ffcc33",
polyStrokeWidth : 4,
strokeColor : "#ffcc33",
strokeWidth : 4,
markerSize : 1,
markerColor : "#ffcc33",
// INFO : cette option n'est pas surchargeable via les options du constructeur !
labelDisplay : true
};
/**
* Default styles when drawing
*
* @private
*/
Drawing.DefaultCursorStyle = {
radius : 6,
strokeColor : "#FFF",
strokeWidth : 1,
fillColor : "rgba(0, 153, 255, 1)"
};
/**
* @lends module:Drawing
*/
Drawing.prototype = Object.create(Control.prototype, {});
/**
* Copies all source object members to Drawing prototype
*
* @param {Object} source - source object whose properties will be copied.
* @private
*/
Drawing.prototype.assign = function (source) {
for (var prop in source) {
if (source.hasOwnProperty(prop)) {
this[prop] = source[prop];
}
}
};
Drawing.prototype.assign(DrawingDOM);
/**
* Constructor (alias)
*
* @private
*/
Drawing.prototype.constructor = Drawing;
/**
* Overload of {@link http://openlayers.org/en/latest/apidoc/ol.control.Control.html#setMap ol.control.Control.setMap()} method, called when control is added to or removed from map.
*
* @param {Object} map - {@link http://openlayers.org/en/latest/apidoc/ol.Map.html ol.Map} object.
*/
Drawing.prototype.setMap = function (map) {
// call original setMap method
Control.prototype.setMap.call(this, map);
if (this.getMap() && this.eventKey) {
olObservableUnByKey(this.eventKey);
}
// nothing else to do if map == null
if (map == null) {
return;
}
// mode "draggable"
if (this.draggable) {
Draggable.dragElement(
this._drawingPanel,
this._drawingPanelHeader,
map.getTargetElement()
);
}
// mode "collapsed"
if (!this.collapsed) {
var inputShow = document.getElementById(this._addUID("GPshowDrawing"));
inputShow.checked = "checked";
}
if (this.layer) {
// ajout du layer de dessin à la carte s'il n'y est pas déjà
this.setLayer(this.layer);
}
// gestion des suppressions "externes" de la couche de dessin.
this.eventKey = this.getMap().getLayers().on("remove", (evtRm) => {
if (evtRm.element === this.layer) { // FIXME object comparison
// found layer removed.
this.layer = null;
// on supprime l'interaction en cours si besoin
if (this.interactionCurrent) {
this.getMap().removeInteraction(this.interactionCurrent);
this.interactionCurrent = null;
}
}
});
};
/**
* Export features of current drawing layer (KML by default).
*
* @returns {String} a representation of drawn features (KML, GPX or GeoJSON) or null if not possible.
*/
Drawing.prototype.exportFeatures = function () {
var result = null;
if (Control.prototype.getMap.call(this) == null) {
logger.log("Impossible to export : control isn't attached to any map.");
return result;
}
if (!this.layer) {
logger.log("Impossible to export : no layer is hosting features.");
return result;
}
if (!this.layer.getSource() ||
!this.layer.getSource().getFeatures() ||
!this.layer.getSource().getFeatures().length) {
logger.log("Impossible to export : no features found.");
return result;
}
// on invalide les features...
if (this.featuresCollectionSelected) {
this.featuresCollectionSelected.clear();
}
var ClassName = null;
switch (this.getExportFormat()) {
case "KML":
ClassName = new KMLExtended({
writeStyles : true
});
break;
case "GPX":
ClassName = new GPXExtended({
// readExtensions : function (ext) {/* only extensions nodes from wpt, rte and trk can be processed */ }
});
break;
case "GEOJSON":
ClassName = new GeoJSONExtended({});
break;
default:
break;
}
if (!ClassName) {
logger.log("Impossible to export : format unknown !?");
return result;
}
var featProj = this.layer.getSource().getProjection();
featProj = featProj || this.getMap().getView().getProjection();
result = ClassName.writeFeatures(this.layer.getSource().getFeatures(), {
dataProjection : "EPSG:4326",
featureProjection : featProj
});
return result;
};
// ################################################################### //
// #################### user interface methods ####################### //
// ################################################################### //
/**
* Collapse or display control main container
*
* @param {Boolean} collapsed - True to collapse control, False to display it
*/
Drawing.prototype.setCollapsed = function (collapsed) {
if (collapsed === undefined) {
logger.error("[ERROR] Drawing:setCollapsed - missing collapsed parameter");
return;
}
if ((collapsed && this.collapsed) || (!collapsed && !this.collapsed)) {
return;
}
// on simule l'ouverture du panneau après un click
this.onShowDrawingClick();
this._showDrawingContainer.checked = !collapsed;
};
/**
* Setter for Export Name.
*
* @param {String} name - Export Name. By default, "Croquis".
*/
Drawing.prototype.setExportName = function (name) {
this._exportName = name;
};
/**
* getter for Export Name.
*
* @returns {String} export name
*/
Drawing.prototype.getExportName = function () {
return this._exportName;
};
/**
* Setter for Export format (KML, GPX or GeoJSON).
*
* @param {String} format - Export format. By default, "KML".
*/
Drawing.prototype.setExportFormat = function (format) {
this._exportFormat = (format) ? format.toUpperCase() : "KML";
switch (format.toUpperCase()) {
case "KML":
this._exportExt = ".kml";
this._exportMimeType = "application/vnd.google-earth.kml+xml";
break;
case "GPX":
this._exportExt = ".gpx";
this._exportMimeType = "application/gpx+xml";
break;
case "GEOJSON":
this._exportExt = ".geojson";
this._exportMimeType = "application/geo+json";
break;
default:
// redefine format by default !
this._exportFormat = "KML";
break;
}
};
/**
* getter for Export format.
*
* @returns {String} export format
*/
Drawing.prototype.getExportFormat = function () {
return this._exportFormat;
};
/**
* Sets vector layer to hosts feature.
*
* @param {ol.layer.Vector} vlayer - vector layer
*/
Drawing.prototype.setLayer = function (vlayer) {
if (!vlayer) {
this.layer = null;
return;
}
if (!(vlayer instanceof VectorLayer)) {
logger.log("no valid layer given for hosting drawn features.");
return;
}
// ajout du layer de dessin à la carte s'il n'y est pas déjà
var layers = this.getMap().getLayers();
if (layers) {
var found = false;
layers.forEach((mapLayer) => {
if (mapLayer === vlayer) {
logger.trace("layer already in map. Not adding.");
found = true;
}
});
// si le layer n'est pas sur la carte, on l'ajoute.
if (!found) {
this.getMap().addLayer(vlayer);
}
// style par defaut !
// application des styles ainsi que ceux par defaut de ol sur le layer
vlayer.getSource().getFeatures().forEach((feature) => {
var style = feature.getStyle();
if (typeof style !== "function") {
return;
}
var featureStyleFunction = feature.getStyleFunction();
if (featureStyleFunction) {
var styles = featureStyleFunction.call(this, feature, 0);
if (styles && styles.length !== 0) {
feature.setStyle((Array.isArray(styles)) ? styles[0] : styles);
}
}
});
this.layer = vlayer;
// Si un layer switcher est présent dans la carte, on lui affecte des informations pour cette couche
this.getMap().getControls().forEach(
(control) => {
if (control instanceof LayerSwitcher) {
// un layer switcher est présent dans la carte
var layerId = this.layer.gpLayerId;
// on n'ajoute des informations que s'il n'y en a pas déjà (si le titre est le numéro par défaut)
if (control._layers[layerId].title === layerId) {
control.addLayer(
this.layer, {
title : this.options.layerDescription.title,
description : this.options.layerDescription.description
}
);
}
}
}
);
}
};
/**
* Get vector layer
*
* @returns {Object} layer - isocurve layer
*/
Drawing.prototype.getLayer = function () {
return this.layer;
};
/**
* Get container
*
* @returns {DOMElement} container
*/
Drawing.prototype.getContainer = function () {
return this._container;
};
// ################################################################### //
// ######################## initialize control ####################### //
// ################################################################### //
/**
* Gets marker options in options.markersList given its src.
*
* @param {String} src - marker image URI,
* @returns {Object} markers options
* @private
*/
Drawing.prototype._getsMarkersOptionsFromSrc = function (src) {
var markerOptions = null;
for (var i = 0; i < this.options.markersList.length; i++) {
if (src && this.options.markersList[i].src === src) {
markerOptions = this.options.markersList[i];
return markerOptions;
}
}
return markerOptions;
};
/**
* Converts markerElement options into Openlayers IconStyles options.
*
* @param {Object} markerElement - marker element
* @returns {Object} ol.Style.Icon constructor options.
* @private
*/
Drawing.prototype._getIconStyleOptions = function (markerElement) {
var iconOptions = {};
Object.keys(markerElement).forEach((key) => {
switch (key) {
case "src":
case "size":
case "scale":
case "color":
case "anchor":
case "anchorOrigin":
case "anchorXUnits":
case "anchorYUnits":
iconOptions[key] = markerElement[key];
break;
}
});
return iconOptions;
};
/**
* Initialize control (called by Drawing constructor)
*
* @method _initialize
* @param {Object} options - control options (set by user)
* @private
*/
Drawing.prototype._initialize = function (options) {
// determination d'un uid
this._uid = SelectorID.generate();
// export name / format / ...
this._exportName = "Croquis";
this._exportFormat = "KML";
this._exportMimeType = "application/vnd.google-earth.kml+xml";
this._exportExt = ".kml";
options = options || {};
// Set default options
this.options = options;
if (!this.options.layerDescription) {
this.options.layerDescription = {
title : "Croquis",
description : "Mon croquis"
};
}
// applying default tools
if (!this.options.tools) {
this.options.tools = {};
}
Object.keys(Drawing.DefaultTools).forEach((key) => {
if (!this.options.tools.hasOwnProperty(key)) {
this.options.tools[key] = Drawing.DefaultTools[key];
}
});
// styles par defaut lors du dessin
if (!this.options.cursorStyle) {
this.options.cursorStyle = {};
}
Object.keys(Drawing.DefaultCursorStyle).forEach((key) => {
if (!this.options.cursorStyle.hasOwnProperty(key)) {
this.options.cursorStyle[key] = Drawing.DefaultCursorStyle[key];
}
});
this.options.collapsed = (options.collapsed !== undefined) ? options.collapsed : true;
/** {Boolean} specify if Drawing control is collapsed (true) or not (false) */
this.collapsed = this.options.collapsed;
this.options.draggable = (options.draggable !== undefined) ? options.draggable : false;
/** {Boolean} specify if Drawing control is draggable (true) or not (false) */
this.draggable = this.options.draggable;
this.options.markersList = options.markersList || MarkersOther["drawing_api"];
// applying default labels
if (!this.options.labels) {
this.options.labels = {};
}
Object.keys(Drawing.DefaultLabels).forEach((key) => {
if (!this.options.labels.hasOwnProperty(key)) {
this.options.labels[key] = Drawing.DefaultLabels[key];
}
});
// applying default styles
if (!this.options.defaultStyles) {
this.options.defaultStyles = {};
}
Object.keys(Drawing.DefaultStyles).forEach((key) => {
if (!options.defaultStyles.hasOwnProperty(key)) {
this.options.defaultStyles[key] = Drawing.DefaultStyles[key];
return;
}
if (key === "polyFillOpacity" &&
(options.defaultStyles[key] < 0 ||
options.defaultStyles[key] > 1)) {
logger.log("Wrong value (" + options.defaultStyles[key] + ") for defaultStyles.polyFillOpactity. Must be between 0 and 1");
this.options.defaultStyles[key] = Drawing.DefaultStyles[key];
return;
}
if (key === "strokeWidth" || key === "polyStrokeWidth") {
var intValue = parseInt(options.defaultStyles[key], 10);
if (isNaN(intValue) || intValue < 0) {
logger.log("Wrong value (" + options.defaultStyles[key] + ") for defaultStyles.strokeWidth. Must be a positive interger value.");
this.options.defaultStyles[key] = Drawing.DefaultStyles[key];
return;
}
this.options.defaultStyles[key] = intValue;
}
if (key === "markerSize") {
var floatValue = parseFloat(options.defaultStyles[key]);
if (isNaN(floatValue) || floatValue < 0) {
logger.log("Wrong value (" + options.defaultStyles[key] + ") for defaultStyles.markerSize. Must be a positive value.");
this.options.defaultStyles[key] = Drawing.DefaultStyles[key];
return;
}
this.options.defaultStyles[key] = floatValue;
}
});
this.interactionCurrent = null;
this.interactionSelectEdit = null;
this.featuresCollectionSelected = null;
this.stylingOvl = null;
this.popupOvl = null;
this.layer = null;
if (this.options.layer && this.options.layer instanceof VectorLayer) {
this.layer = this.options.layer;
}
// detection du support : desktop ou tactile
// FIXME : utile ?
this._isDesktop = this._detectSupport();
// applying default popup
if (!this.options.popup) {
this.options.popup = {
display : true,
apply : null
};
}
};
/**
* Creates empty layer to host features
*
* @private
*/
Drawing.prototype._createEmptyLayer = function () {
var features = new Collection();
var layer = new VectorLayer({
source : new VectorSource({
features : features
})
});
// on rajoute le champ gpResultLayerId permettant d'identifier une couche crée par le composant.
layer.gpResultLayerId = "drawing";
// on le rajoute au controle (et à la carte)
this.setLayer(layer);
};
/**
* this method is called by the constructor.
* this information is useful to switch to touch mode.
* Detection : test for desktop or tactile
*
* @method _detectSupport
*
* @returns {Boolean} is desktop
* @private
*/
Drawing.prototype._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 ####################### //
// ################################################################### //
/**
* Create control main container (called by Drawing constructor)
*
* @method _initContainer
*
* @returns {DOMElement} DOM element
* @private
*/
Drawing.prototype._initContainer = function () {
// creation du container principal
var container = this._createMainContainerElement();
var inputShow = this._showDrawingContainer = this._createShowDrawingElement();
container.appendChild(inputShow);
var picto = this._createShowDrawingPictoElement();
container.appendChild(picto);
var panel = this._drawingPanel = this._createDrawingPanelElement();
var header = this._drawingPanelHeader = this._createDrawingPanelHeaderElement();
panel.appendChild(header);
var tools = this._createDrawingToolsSections();
for (var i = 0; i < tools.length; i++) {
panel.appendChild(tools[i]);
}
container.appendChild(panel);
return container;
};
// ################################################################### //
// ##################### handlers events to control ################## //
// ################################################################### //
/**
* Callback de fin de dessin de geometrie
* @param {Object} feature - ol feature
* @param {String} geomType - geometry type
* @param {Boolean} clean - clean last feature
*
* @private
*/
Drawing.prototype._drawEndFeature = function (feature, geomType) {
// application des styles par defaut.
var style = null;
switch (geomType) {
case "Point":
style = new Style({
image : new Icon(this._getIconStyleOptions(this.options.markersList[0]))
});
break;
case "LineString":
style = new Style({
stroke : new Stroke({
color : this.options.defaultStyles.strokeColor,
width : this.options.defaultStyles.strokeWidth
})
});
break;
case "Polygon":
style = new Style({
fill : new Fill({
color : Color.hexToRgba(
this.options.defaultStyles.polyFillColor,
this.options.defaultStyles.polyFillOpacity
)
}),
stroke : new Stroke({
color : this.options.defaultStyles.polyStrokeColor,
width : this.options.defaultStyles.polyStrokeWidth
})
});
break;
}
feature.setStyle(style);
// gestion des mesures
this._updateMeasure(feature, geomType);
if (this.options.popup.display) {
// creation overlay pour saisie du label
// contexte
var context = this;
/**
* Enregistrement de la valeur saisie dans l'input.
*
* @param {String} key - clef de l'attribut.
* @param {String} value - valeur de l'attribut.
* @param {Boolean} save - true si on garde le label.
*/
var setAttValue = function (key, value, save) {
context.getMap().removeOverlay(context.popupOvl);
context.popupOvl = null;
if (save && value && value.trim().length > 0) {
var obj = {};
obj[key] = value.replace(/\n/g, "<br>");
feature.setProperties(obj);
}
};
var popup = null;
var popupByDefault = true;
var displayFunction = this.options.popup.function;
if (displayFunction && typeof displayFunction === "function") {
// la sauvegarde et la fermeture sont des actions à implementer par l'utilisateur
// par contre, la destruction est à gerer en interne
popup = displayFunction.call(context, {
feature : feature,
geomType : geomType,
closeFunc : function () {
setAttValue(null, false);
},
saveFunc : function (message) {
setAttValue(message, true);
}
});
if (popup) {
// on est sûr que la popup customisée existe,
// donc on n'utilise pas celle par defaut...
popupByDefault = false;
// FIXME comment forcer le focus sur une div ?
popup.tabIndex = -1; // hack sur le focus sur une div ?
popup.onblur = function () {
context.getMap().removeOverlay(context.popupOvl);
context.popupOvl = null;
};
}
}
// use popup by default
if (popupByDefault) {
// function by default
popup = this._createLabelDiv({
applyFunc : setAttValue,
inputId : this._addUID("att-input"),
placeholder : "Saisir une description...",
measure : (this.options.tools.measure) ? feature.getProperties().measure : null,
geomType : geomType,
key : "description"
});
}
// un peu de menage...
if (this.popupOvl) {
this.getMap().removeOverlay(this.popupOvl);
this.popupOvl = null;
}
// creation de l'overlay
this.popupOvl = new Overlay({
element : popup,
// FIXME : autres valeurs.
positioning : "top-center"
// stopEvent : false
});
this.getMap().addOverlay(this.popupOvl);
var geomExtent = feature.getGeometry().getExtent();
this.popupOvl.setPosition([
(geomExtent[0] + geomExtent[2]) / 2, (geomExtent[1] + geomExtent[3]) / 2
]);
if (document.getElementById(this._addUID("att-input"))) {
document.getElementById(this._addUID("att-input")).focus();
}
}
};
/**
* Creates Interaction for features removal.
*
* @returns {SelectInteraction} created interaction.
* @private
*/
Drawing.prototype._createRemoveInteraction = function () {
var interaction = new SelectInteraction({
// features : this.layer.getSource().getFeaturesCollection(),
layers : [this.layer],
style : false
});
interaction.on("select", (seEv) => {
if (!seEv || !seEv.selected || seEv.selected.length === 0) {
return;
}
this.layer.getSource().removeFeature(seEv.selected[0]);
// suppression puis rajout de l'interaction pour appliquer le changement tout de suite...
this.getMap().removeInteraction(this.interactionCurrent);
this.interactionCurrent = this._createRemoveInteraction();
this.getMap().addInteraction(this.interactionCurrent);
});
return interaction;
};
/**
* Creates Interaction for features style definition.
*
* @returns {ol.interaction.Select} created interaction.
* @private
*/
Drawing.prototype._createStylingInteraction = function () {
var interaction = new SelectInteraction({
layers : [this.layer],
style : false
});
interaction.on("select", (seEv) => {
// suppression de toute popup existante
if (this.stylingOvl) {
this.getMap().removeOverlay(this.stylingOvl);
}
if (!seEv || !seEv.selected || seEv.selected.length === 0) {
return;
}
var valuesColor = null;
var hexColor = null;
var popupOvl = null;
var geomType = null;
var initValues = {};
// FIXME
// l'appel feature.getStyle() est parfois nul pour des geometries Point
// avec un style par defaut !
var geom = seEv.selected[0].getGeometry();
var style = seEv.selected[0].getStyle();
if (geom instanceof Point || geom instanceof MultiPoint) {
// INFO
// on determine si c'est un marker (ou cercle), un label ou les 2.
// un label a un pixel transparent comme icone
if (style &&
style.getImage() &&
typeof style.getImage().getSrc === "function" &&
style.getImage().getSrc() !== this.options.defaultStyles.textIcon1x1.src) {
geomType = "Point";
// on traite un marker
// mais si c'est un cercle !?
if (typeof style.getImage().getSrc === "function") {
initValues.markerSrc = style.getImage().getSrc();
initValues.markerSize = style.getImage().getScale() || 1;
initValues.markerAnchor = style.getImage().getAnchor();
if (style.getImage().getColor()) {
valuesColor = style.getImage().getColor();
if (Array.isArray(valuesColor)) { // FIXME Array !?
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.markerColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.markerColor = hexColor.hex;
initValues.markerOpacity = hexColor.opacity;
} else {
initValues.markerColor = this.options.markersList[0].color || "#ffffff";
}
} else {
initValues.markerSrc = this.options.markersList[0].src;
initValues.markerSize = this.options.markersList[0].scale || 1;
initValues.markerColor = this.options.markersList[0].color || "#ffffff";
initValues.markerAnchor = this.options.markersList[0].anchor;
}
initValues.markerCustom = !(this._getsMarkersOptionsFromSrc(initValues.markerSrc));
}
if (style && style.getText()) {
var labelName = seEv.selected[0].getProperties().name;
if (labelName) {
// test si on a un marker avec un label
geomType = (geomType === "Point") ? "Point&Text" : "Text";
if (style.getText().getStroke() && style.getText().getStroke().getColor()) {
valuesColor = style.getText().getStroke().getColor();
if (Array.isArray(valuesColor)) { // FIXME Array !?
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.strokeColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.strokeColor = hexColor.hex;
initValues.strokeOpacity = hexColor.opacity;
}
if (style.getText().getStroke() && style.getText().getStroke().getWidth()) {
initValues.strokeWidth = style.getText().getStroke().getWidth();
}
if (style.getText().getFill() && style.getText().getFill().getColor()) {
valuesColor = style.getText().getFill().getColor();
if (Array.isArray(valuesColor)) {
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.fillColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.fillColor = hexColor.hex;
initValues.fillOpacity = hexColor.opacity;
}
initValues.strokeColor = initValues.hasOwnProperty("strokeColor") ? initValues.strokeColor : this.options.defaultStyles.textStrokeColor;
initValues.strokeWidth = initValues.hasOwnProperty("strokeWidth") ? initValues.strokeWidth : this.options.defaultStyles.textStrokeWidth;
initValues.fillColor = initValues.hasOwnProperty("fillColor") ? initValues.fillColor : this.options.defaultStyles.textFillColor;
// Par defaut, pour un marker avec un label, on affiche le label si le tag "name" est renseigné.
if (geomType === "Point&Text") {
var value = style.getText().getText();
if (!value) {
style.getText().setText(labelName);
}
var checked = seEv.selected[0].get("checked");
initValues.labelDisplay = (checked === undefined) ? this.options.defaultStyles.labelDisplay : checked;
}
}
}
} else if (geom instanceof LineString || geom instanceof MultiLineString) {
geomType = "Line";
if (style && style.getStroke()) {
if (style.getStroke().getWidth()) {
initValues.strokeWidth = style.getStroke().getWidth();
}
if (style.getStroke().getColor()) {
valuesColor = style.getStroke().getColor();
if (Array.isArray(valuesColor)) {
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.strokeColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.strokeColor = hexColor.hex;
initValues.strokeOpacity = hexColor.opacity;
}
}
initValues.strokeWidth = initValues.hasOwnProperty("strokeWidth") ? initValues.strokeWidth : this.options.defaultStyles.strokeWidth;
initValues.strokeColor = initValues.hasOwnProperty("strokeColor") ? initValues.strokeColor : this.options.defaultStyles.strokeColor;
} else if (geom instanceof Polygon || geom instanceof MultiPolygon) {
geomType = "Polygon";
if (style && style.getStroke()) {
if (style.getStroke().getWidth()) {
initValues.strokeWidth = style.getStroke().getWidth();
}
if (style.getStroke().getColor()) {
valuesColor = style.getStroke().getColor();
if (Array.isArray(valuesColor)) {
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.strokeColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.strokeColor = hexColor.hex;
initValues.strokeOpacity = hexColor.opacity;
}
}
if (style && style.getFill()) {
if (style.getFill().getColor()) {
valuesColor = style.getFill().getColor();
if (Array.isArray(valuesColor)) {
valuesColor = "rgba(" + valuesColor.join() + ")";
} else {
initValues.fillColor = valuesColor;
}
hexColor = Color.isRGB(valuesColor) ? Color.rgbaToHex(valuesColor) : {
hex : valuesColor,
opacity : 1
};
initValues.fillColor = hexColor.hex;
initValues.fillOpacity = hexColor.opacity;
}
}
initValues.strokeWidth = initValues.hasOwnProperty("strokeWidth") ? initValues.strokeWidth : this.options.defaultStyles.polyStrokeWidth;
initValues.strokeColor = initValues.hasOwnProperty("strokeColor") ? initValues.strokeColor : this.options.defaultStyles.polyStrokeColor;
initValues.fillColor = initValues.hasOwnProperty("fillColor") ? initValues.fillColor : this.options.defaultStyles.polyFillColor;
initValues.fillOpacity = initValues.hasOwnProperty("fillOpacity") ? initValues.fillOpacity : this.options.defaultStyles.polyFillOpacity;
}
if (!geomType) {
logger.log("Unhandled geometry type for styling.");
return;
}
var dtObj = this;
/**
* function called when apply button is pressed.
*
* @param {String} action - "apply" (to selected object), "default" (set as default), "cancel" (do nothing).
*/
var applyStyle = function (action) {
if (action === "cancel") {
dtObj.getMap().removeOverlay(popupOvl);
return;
}
var setDefault = action !== "apply";
var fillColorElem = document.getElementById(dtObj._addUID("fillColor"));
var fillOpacityElem = document.getElementById(dtObj._addUID("fillOpacity"));
var strokeColorElem = document.getElementByI