ol-wfst 1.0.7 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +125 -48
  3. package/dist/@enums.d.ts +12 -0
  4. package/dist/@enums.d.ts.map +1 -0
  5. package/dist/assets/i18n/en.d.ts +3 -0
  6. package/dist/assets/i18n/en.d.ts.map +1 -0
  7. package/dist/assets/i18n/es.d.ts +3 -0
  8. package/dist/assets/i18n/es.d.ts.map +1 -0
  9. package/dist/assets/i18n/index.d.ts +3 -0
  10. package/dist/assets/i18n/index.d.ts.map +1 -0
  11. package/dist/css/ol-wfst.bootstrap5.css +863 -0
  12. package/dist/css/ol-wfst.bootstrap5.min.css +1 -0
  13. package/dist/css/ol-wfst.css +172 -127
  14. package/dist/css/ol-wfst.min.css +1 -1
  15. package/dist/ol-wfst.d.ts +143 -86
  16. package/dist/ol-wfst.d.ts.map +1 -0
  17. package/dist/ol-wfst.js +5313 -4575
  18. package/dist/ol-wfst.js.map +1 -0
  19. package/dist/ol-wfst.min.js +16 -1
  20. package/dist/ol-wfst.min.js.map +1 -0
  21. package/dist/tsconfig.tsbuildinfo +1 -0
  22. package/lib/@enums.d.ts +12 -0
  23. package/lib/@enums.d.ts.map +1 -0
  24. package/lib/assets/i18n/en.d.ts +3 -0
  25. package/lib/assets/i18n/en.d.ts.map +1 -0
  26. package/lib/assets/i18n/es.d.ts +3 -0
  27. package/lib/assets/i18n/es.d.ts.map +1 -0
  28. package/lib/assets/i18n/index.d.ts +3 -0
  29. package/lib/assets/i18n/index.d.ts.map +1 -0
  30. package/lib/ol-wfst.d.ts +143 -86
  31. package/lib/ol-wfst.d.ts.map +1 -0
  32. package/lib/ol-wfst.js +2126 -2195
  33. package/lib/ol-wfst.js.map +1 -0
  34. package/lib/style/css/ol-gisify.css +267 -0
  35. package/lib/style/css/ol-wfst.bootstrap5.css +863 -0
  36. package/lib/style/scss/-ol-wfst.bootstrap5.scss +10 -0
  37. package/lib/{css/ol-wfst.css → style/scss/ol-wfst.scss} +7 -2
  38. package/lib/tsconfig.tsbuildinfo +1 -0
  39. package/package.json +70 -66
  40. package/dist/css/bootstrap-modal.min.css +0 -7
  41. package/lib/css/bootstrap-modal.min.css +0 -7
package/lib/ol-wfst.js CHANGED
@@ -1,22 +1,45 @@
1
- import GeometryType from 'ol/geom/GeometryType';
2
- import OverlayPositioning from 'ol/OverlayPositioning';
3
1
  import TileState from 'ol/TileState';
4
- import { MultiPoint, MultiPolygon, MultiLineString } from 'ol/geom';
2
+ import { GeometryCollection, Circle as Circle$1, Polygon, MultiLineString, MultiPolygon, MultiPoint } from 'ol/geom';
5
3
  import { Style, Circle, Fill, Stroke } from 'ol/style';
6
4
  import { Control } from 'ol/control';
7
- import { Select, Modify, Snap, Draw } from 'ol/interaction';
8
- import { Feature, Overlay } from 'ol';
5
+ import { Modify, Snap, Select, Draw } from 'ol/interaction';
6
+ import { Collection, Feature, Overlay } from 'ol';
9
7
  import { WFS, GeoJSON, KML } from 'ol/format';
10
8
  import { Vector, Tile } from 'ol/layer';
11
9
  import { Vector as Vector$1, TileWMS } from 'ol/source';
12
10
  import { bbox, all } from 'ol/loadingstrategy';
13
11
  import { fromCircle } from 'ol/geom/Polygon';
14
12
  import { getCenter } from 'ol/extent';
15
- import { never, primaryAction } from 'ol/events/condition';
13
+ import { primaryAction, never } from 'ol/events/condition';
16
14
  import { transformExtent } from 'ol/proj';
17
15
  import { unByKey } from 'ol/Observable';
18
16
  import Modal from 'modal-vanilla';
19
17
 
18
+ /*! *****************************************************************************
19
+ Copyright (c) Microsoft Corporation.
20
+
21
+ Permission to use, copy, modify, and/or distribute this software for any
22
+ purpose with or without fee is hereby granted.
23
+
24
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
25
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
26
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
27
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
28
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
29
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
30
+ PERFORMANCE OF THIS SOFTWARE.
31
+ ***************************************************************************** */
32
+
33
+ function __awaiter(thisArg, _arguments, P, generator) {
34
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
35
+ return new (P || (P = Promise))(function (resolve, reject) {
36
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
37
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
38
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
39
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
40
+ });
41
+ }
42
+
20
43
  var img = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='768' height='768' viewBox='0 0 768 768'%3e %3cpath d='M663 225l-58.5 58.5-120-120 58.5-58.5q9-9 22.5-9t22.5 9l75 75q9 9 9 22.5t-9 22.5zM96 552l354-354 120 120-354 354h-120v-120z'%3e%3c/path%3e%3c/svg%3e";
21
44
 
22
45
  var img$1 = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='448' height='448' viewBox='0 0 448 448'%3e %3cpath d='M222 296l29-29-38-38-29 29v14h24v24h14zM332 116c-2.25-2.25-6-2-8.25 0.25l-87.5 87.5c-2.25 2.25-2.5 6-0.25 8.25s6 2 8.25-0.25l87.5-87.5c2.25-2.25 2.5-6 0.25-8.25zM352 264.5v47.5c0 39.75-32.25 72-72 72h-208c-39.75 0-72-32.25-72-72v-208c0-39.75 32.25-72 72-72h208c10 0 20 2 29.25 6.25 2.25 1 4 3.25 4.5 5.75 0.5 2.75-0.25 5.25-2.25 7.25l-12.25 12.25c-2.25 2.25-5.25 3-8 2-3.75-1-7.5-1.5-11.25-1.5h-208c-22 0-40 18-40 40v208c0 22 18 40 40 40h208c22 0 40-18 40-40v-31.5c0-2 0.75-4 2.25-5.5l16-16c2.5-2.5 5.75-3 8.75-1.75s5 4 5 7.25zM328 80l72 72-168 168h-72v-72zM439 113l-23 23-72-72 23-23c9.25-9.25 24.75-9.25 34 0l38 38c9.25 9.25 9.25 24.75 0 34z'%3e%3c/path%3e%3c/svg%3e";
@@ -29,120 +52,106 @@ var img$4 = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/20
29
52
 
30
53
  var img$5 = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='768' height='768' viewBox='0 0 768 768'%3e%3cpath d='M379.5 288h4.5q39 0 67.5 28.5t28.5 67.5v6zM241.5 313.5q-18 36-18 70.5 0 66 47.25 113.25t113.25 47.25q34.5 0 70.5-18l-49.5-49.5q-12 3-21 3-39 0-67.5-28.5t-28.5-67.5q0-9 3-21zM64.5 136.5l40.5-40.5 567 567-40.5 40.5q-7.5-7.5-47.25-46.5t-60.75-60q-64.5 27-139.5 27-118.5 0-214.5-66t-138-174q16.5-39 51.75-86.25t68.25-72.75q-18-18-50.25-51t-36.75-37.5zM384 223.5q-30 0-58.5 12l-69-69q58.5-22.5 127.5-22.5 118.5 0 213.75 66t137.25 174q-36 88.5-109.5 151.5l-93-93q12-28.5 12-58.5 0-66-47.25-113.25t-113.25-47.25z'%3e%3c/path%3e%3c/svg%3e";
31
54
 
32
- var es = {
33
- labels: {
34
- select: 'Seleccionar',
35
- addElement: 'Modo dibujo',
36
- editElement: 'Editar elemento',
37
- save: 'Guardar',
38
- delete: 'Eliminar',
39
- cancel: 'Cancelar',
40
- apply: 'Aplicar cambios',
41
- upload: 'Subir',
42
- editMode: 'Modo Edición',
43
- confirmDelete: '¿Estás seguro de borrar el elemento?',
44
- geomTypeNotSupported: 'Geometría no compatible con la capa',
45
- editFields: 'Editar campos',
46
- editGeom: 'Editar geometría',
47
- selectDrawType: 'Tipo de geometría para dibujar',
48
- uploadToLayer: 'Subir archivo a la capa seleccionada',
49
- uploadFeatures: 'Subida de elementos a la capa',
50
- validFeatures: 'Válidas',
51
- invalidFeatures: 'Invalidas',
52
- loading: 'Cargando...',
53
- toggleVisibility: 'Cambiar visibilidad de la capa'
54
- },
55
- errors: {
56
- capabilities: 'No se pudieron obtener las Capabilidades del GeoServer',
57
- wfst: 'El GeoServer no tiene soporte a Transacciones',
58
- layer: 'No se pudieron obtener datos de la capa',
59
- noValidGeometry: 'No se encontraron geometrías válidas para agregar a esta capa',
60
- geoserver: 'No se pudieron obtener datos desde el GeoServer',
61
- badFormat: 'Formato no soportado',
62
- badFile: 'Error al leer elementos del archivo',
63
- lockFeature: 'No se pudieron bloquear elementos en el GeoServer.',
64
- transaction: 'Error al hacer transacción con el GeoServer. HTTP status:',
65
- getFeatures: 'Error al obtener elemento desde el GeoServer. HTTP status:'
66
- }
55
+ var GeometryType;
56
+ (function (GeometryType) {
57
+ GeometryType["Point"] = "Point";
58
+ GeometryType["LineString"] = "LineString";
59
+ GeometryType["LinearRing"] = "LinearRing";
60
+ GeometryType["Polygon"] = "Polygon";
61
+ GeometryType["MultiPoint"] = "MultiPoint";
62
+ GeometryType["MultiLineString"] = "MultiLineString";
63
+ GeometryType["MultiPolygon"] = "MultiPolygon";
64
+ GeometryType["GeometryCollection"] = "GeometryCollection";
65
+ GeometryType["Circle"] = "Circle";
66
+ })(GeometryType || (GeometryType = {}));
67
+
68
+ const es = {
69
+ labels: {
70
+ select: 'Seleccionar',
71
+ addElement: 'Modo dibujo',
72
+ editElement: 'Editar elemento',
73
+ save: 'Guardar',
74
+ delete: 'Eliminar',
75
+ cancel: 'Cancelar',
76
+ apply: 'Aplicar cambios',
77
+ upload: 'Subir',
78
+ editMode: 'Modo Edición',
79
+ confirmDelete: '¿Estás seguro de borrar el elemento?',
80
+ geomTypeNotSupported: 'Geometría no compatible con la capa',
81
+ editFields: 'Editar campos',
82
+ editGeom: 'Editar geometría',
83
+ selectDrawType: 'Tipo de geometría para dibujar',
84
+ uploadToLayer: 'Subir archivo a la capa seleccionada',
85
+ uploadFeatures: 'Subida de elementos a la capa',
86
+ validFeatures: 'Válidas',
87
+ invalidFeatures: 'Invalidas',
88
+ loading: 'Cargando...',
89
+ toggleVisibility: 'Cambiar visibilidad de la capa',
90
+ close: 'Cerrar'
91
+ },
92
+ errors: {
93
+ capabilities: 'No se pudieron obtener las Capabilidades del GeoServer',
94
+ wfst: 'El GeoServer no tiene soporte a Transacciones',
95
+ layer: 'No se pudieron obtener datos de la capa',
96
+ noValidGeometry: 'No se encontraron geometrías válidas para agregar a esta capa',
97
+ geoserver: 'No se pudieron obtener datos desde el GeoServer',
98
+ badFormat: 'Formato no soportado',
99
+ badFile: 'Error al leer elementos del archivo',
100
+ lockFeature: 'No se pudieron bloquear elementos en el GeoServer.',
101
+ transaction: 'Error al hacer transacción con el GeoServer. HTTP status:',
102
+ getFeatures: 'Error al obtener elemento desde el GeoServer. HTTP status:'
103
+ }
67
104
  };
68
105
 
69
- var en = {
70
- labels: {
71
- select: 'Select',
72
- addElement: 'Toggle Draw mode',
73
- editElement: 'Edit feature',
74
- save: 'Save',
75
- delete: 'Delete',
76
- cancel: 'Cancel',
77
- apply: 'Apply changes',
78
- upload: 'Upload',
79
- editMode: 'Edit Mode',
80
- confirmDelete: 'Are you sure to delete the feature?',
81
- geomTypeNotSupported: 'Geometry not supported by layer',
82
- editFields: 'Edit fields',
83
- editGeom: 'Edit geometry',
84
- selectDrawType: 'Geometry type to draw',
85
- uploadToLayer: 'Upload file to selected layer',
86
- uploadFeatures: 'Uploaded features to layer',
87
- validFeatures: 'Valid geometries',
88
- invalidFeatures: 'Invalid',
89
- loading: 'Loading...',
90
- toggleVisibility: 'Toggle layer visibility'
91
- },
92
- errors: {
93
- capabilities: 'GeoServer Capabilities could not be downloaded.',
94
- wfst: 'The GeoServer does not support Transactions',
95
- layer: 'Could not get data from layer',
96
- noValidGeometry: 'No valid geometries found to add to this layer',
97
- geoserver: 'Could not get data from the GeoServer',
98
- badFormat: 'Unsupported format',
99
- badFile: 'Error reading items from file',
100
- lockFeature: 'No se pudieron bloquear elementos en el GeoServer. HTTP status:',
101
- transaction: 'Error when doing Transaction with GeoServer. HTTP status:',
102
- getFeatures: 'Error getting elements from GeoServer. HTTP status:'
103
- }
106
+ const en = {
107
+ labels: {
108
+ select: 'Select',
109
+ addElement: 'Toggle Draw mode',
110
+ editElement: 'Edit feature',
111
+ save: 'Save',
112
+ delete: 'Delete',
113
+ cancel: 'Cancel',
114
+ apply: 'Apply changes',
115
+ upload: 'Upload',
116
+ editMode: 'Edit Mode',
117
+ confirmDelete: 'Are you sure to delete the feature?',
118
+ geomTypeNotSupported: 'Geometry not supported by layer',
119
+ editFields: 'Edit fields',
120
+ editGeom: 'Edit geometry',
121
+ selectDrawType: 'Geometry type to draw',
122
+ uploadToLayer: 'Upload file to selected layer',
123
+ uploadFeatures: 'Uploaded features to layer',
124
+ validFeatures: 'Valid geometries',
125
+ invalidFeatures: 'Invalid',
126
+ loading: 'Loading...',
127
+ toggleVisibility: 'Toggle layer visibility',
128
+ close: 'Close'
129
+ },
130
+ errors: {
131
+ capabilities: 'GeoServer Capabilities could not be downloaded.',
132
+ wfst: 'The GeoServer does not support Transactions',
133
+ layer: 'Could not get data from layer',
134
+ noValidGeometry: 'No valid geometries found to add to this layer',
135
+ geoserver: 'Could not get data from the GeoServer',
136
+ badFormat: 'Unsupported format',
137
+ badFile: 'Error reading items from file',
138
+ lockFeature: 'No se pudieron bloquear elementos en el GeoServer. HTTP status:',
139
+ transaction: 'Error when doing Transaction with GeoServer. HTTP status:',
140
+ getFeatures: 'Error getting elements from GeoServer. HTTP status:'
141
+ }
104
142
  };
105
143
 
106
- var languages = /*#__PURE__*/Object.freeze({
107
- __proto__: null,
108
- es: es,
109
- en: en
144
+ var i18n = /*#__PURE__*/Object.freeze({
145
+ __proto__: null,
146
+ es: es,
147
+ en: en
110
148
  });
111
149
 
112
- var __awaiter = undefined && undefined.__awaiter || function (thisArg, _arguments, P, generator) {
113
- function adopt(value) {
114
- return value instanceof P ? value : new P(function (resolve) {
115
- resolve(value);
116
- });
117
- }
118
-
119
- return new (P || (P = Promise))(function (resolve, reject) {
120
- function fulfilled(value) {
121
- try {
122
- step(generator.next(value));
123
- } catch (e) {
124
- reject(e);
125
- }
126
- }
127
-
128
- function rejected(value) {
129
- try {
130
- step(generator["throw"](value));
131
- } catch (e) {
132
- reject(e);
133
- }
134
- }
135
-
136
- function step(result) {
137
- result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
138
- }
139
-
140
- step((generator = generator.apply(thisArg, _arguments || [])).next());
141
- });
142
- }; // Ol
143
- // Axis ordering: latitude/longitude
144
-
145
- var DEFAULT_GEOSERVER_SRS = 'urn:x-ogc:def:crs:EPSG:4326';
150
+ // https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html
151
+ // Axis ordering: latitude/longitude
152
+ const DEFAULT_GEOSERVER_SRS = 'EPSG:3857';
153
+ const DEFAULT_LANGUAGE = 'en';
154
+ const controlElement = document.createElement('div');
146
155
  /**
147
156
  * Tiny WFST-T client to insert (drawing/uploading), modify and delete
148
157
  * features on GeoServers using OpenLayers. Layers with these types
@@ -151,2108 +160,2030 @@ var DEFAULT_GEOSERVER_SRS = 'urn:x-ogc:def:crs:EPSG:4326';
151
160
  * "LineString", "MultiLineString", "Polygon" and "MultiPolygon".
152
161
  *
153
162
  * @constructor
154
- * @param map Instance of the created map
163
+ * @fires getCapabilities
164
+ * @fires describeFeatureType
165
+ * @fires allDescribeFeatureTypeLoaded
166
+ * @fires getFeature
167
+ * @fires modifystart
168
+ * @fires modifyend
169
+ * @fires drawstart
170
+ * @fires drawend
171
+ * @extends {ol/control/Control~Control}
155
172
  * @param opt_options Wfst options, see [Wfst Options](#options) for more details.
156
- */
157
-
158
- class Wfst {
159
- constructor(map, opt_options) {
160
- // Default options
161
- this.options = {
162
- geoServerUrl: null,
163
- headers: {},
164
- layers: null,
165
- evtType: 'singleclick',
166
- active: true,
167
- showControl: true,
168
- useLockFeature: true,
169
- minZoom: 9,
170
- language: 'en',
171
- uploadFormats: '.geojson,.json,.kml',
172
- processUpload: null,
173
- beforeInsertFeature: null
174
- }; // Assign user options
175
-
176
- this.options = Object.assign(Object.assign({}, this.options), opt_options); // Language support
177
-
178
- this._i18n = languages[this.options.language]; // GeoServer
179
-
180
- this._hasLockFeature = false;
181
- this._hasTransaction = false;
182
- this._geoServerCapabilities = null;
183
- this._geoServerData = {}; // Ol
184
-
185
- this.map = map;
186
- this.view = map.getView();
187
- this.viewport = map.getViewport();
188
- this._mapLayers = []; // Editing
189
-
190
- this._editedFeatures = new Set();
191
- this._layerToInsertElements = this.options.layers[0].name; // By default, the first layer is ready to accept new draws
192
-
193
- this._insertFeatures = [];
194
- this._updateFeatures = [];
195
- this._deleteFeatures = []; // Formats
196
-
197
- this._formatWFS = new WFS();
198
- this._formatGeoJSON = new GeoJSON();
199
- this._formatKml = new KML({
200
- extractStyles: false,
201
- showPointNames: false
202
- });
203
- this._xs = new XMLSerializer(); // State
204
-
205
- this._isVisible = this.view.getZoom() > this.options.minZoom;
206
- this._countRequests = 0;
207
- this._isEditModeOn = false;
208
-
209
- this._initAsyncOperations();
210
- }
211
- /**
212
- * Connect to the GeoServer and retrieve metadata about the service (GetCapabilities).
213
- * Get each layer specs (DescribeFeatureType) and create the layers and map controls.
214
- *
215
- * @param layers
216
- * @param showControl
217
- * @param active
218
- * @private
219
- */
220
-
221
-
222
- _initAsyncOperations() {
223
- return __awaiter(this, void 0, void 0, function* () {
224
- try {
225
- this._createBaseControl();
226
-
227
- this._showLoading();
228
-
229
- yield this._connectToGeoServerAndGetCapabilities();
230
-
231
- if (this.options.layers) {
232
- yield this._getGeoserverLayersData(this.options.layers, this.options.geoServerUrl);
233
-
234
- this._createLayers(this.options.layers);
235
- }
236
-
237
- this._initMapElements(this.options.showControl, this.options.active);
238
- } catch (err) {
239
- this._hideLoading();
240
-
241
- this._showError(err.message);
242
- }
243
- });
244
- }
245
- /**
246
- * Creates a base control
247
- *
248
- * @private
249
- */
250
-
251
-
252
- _createBaseControl() {
253
- this._controlWidgetToolsDiv = document.createElement('div');
254
- this._controlWidgetToolsDiv.className = 'ol-wfst--tools-control';
255
- this._controlWidgetTools = new Control({
256
- element: this._controlWidgetToolsDiv
257
- });
258
- this.map.addControl(this._controlWidgetTools);
259
- }
260
- /**
261
- * Get the capabilities from the GeoServer and check
262
- * all the available operations.
263
- *
264
- * @private
265
- */
266
-
267
-
268
- _connectToGeoServerAndGetCapabilities() {
269
- return __awaiter(this, void 0, void 0, function* () {
270
- /**
271
- * @private
272
- */
273
- var getCapabilities = () => __awaiter(this, void 0, void 0, function* () {
274
- var params = new URLSearchParams({
275
- service: 'wfs',
276
- version: '1.3.0',
277
- request: 'GetCapabilities',
278
- exceptions: 'application/json'
279
- });
280
- var url_fetch = this.options.geoServerUrl + '?' + params.toString();
281
-
282
- try {
283
- var response = yield fetch(url_fetch, {
284
- headers: this.options.headers
285
- });
286
-
287
- if (!response.ok) {
288
- throw new Error('');
289
- }
290
-
291
- var data = yield response.text();
292
- var capabilities = new window.DOMParser().parseFromString(data, 'text/xml');
293
- return capabilities;
294
- } catch (err) {
295
- throw new Error(this._i18n.errors.capabilities);
296
- }
297
- });
298
-
299
- this._geoServerCapabilities = yield getCapabilities(); // Available operations in the geoserver
300
-
301
- var operations = this._geoServerCapabilities.getElementsByTagName('ows:Operation');
302
-
303
- Array.from(operations).forEach(operation => {
304
- if (operation.getAttribute('name') === 'Transaction') {
305
- this._hasTransaction = true;
306
- } else if (operation.getAttribute('name') === 'LockFeature') {
307
- this._hasLockFeature = true;
308
- }
309
- });
310
-
311
- if (!this._hasTransaction) {
312
- throw new Error(this._i18n.errors.wfst);
313
- }
314
-
315
- return true;
316
- });
317
- }
318
- /**
319
- * Request and store data layers obtained by DescribeFeatureType
320
- *
321
- * @param layers
322
- * @param geoServerUrl
323
- * @private
324
- */
325
-
326
-
327
- _getGeoserverLayersData(layers, geoServerUrl) {
328
- return __awaiter(this, void 0, void 0, function* () {
329
- var getLayerData = layerName => __awaiter(this, void 0, void 0, function* () {
330
- var params = new URLSearchParams({
331
- service: 'wfs',
332
- version: '1.1.0',
333
- request: 'DescribeFeatureType',
334
- typeName: layerName,
335
- outputFormat: 'application/json',
336
- exceptions: 'application/json'
337
- });
338
- var url_fetch = geoServerUrl + '?' + params.toString();
339
- var response = yield fetch(url_fetch, {
340
- headers: this.options.headers
341
- });
342
-
343
- if (!response.ok) {
344
- throw new Error('');
345
- }
346
-
347
- return yield response.json();
348
- });
349
-
350
- for (var layer of layers) {
351
- var layerName = layer.name;
352
- var layerLabel = layer.label || layerName;
353
-
354
- try {
355
- var data = yield getLayerData(layerName);
356
-
357
- if (data) {
358
- console.log(data);
359
- var targetNamespace = data.targetNamespace;
360
- var properties = data.featureTypes[0].properties; // Find the geometry field
361
-
362
- var geom = properties.find(el => el.type.indexOf('gml:') >= 0);
363
- this._geoServerData[layerName] = {
364
- namespace: targetNamespace,
365
- properties: properties,
366
- geomType: geom.localType,
367
- geomField: geom.name
368
- };
369
- }
370
- } catch (err) {
371
- this._showError("".concat(this._i18n.errors.layer, " \"").concat(layerLabel, "\""));
372
- }
373
- }
374
- });
375
- }
376
- /**
377
- * Create map layers in wfs o wms modes.
378
- *
379
- * @param layers
380
- * @private
381
- */
382
-
383
-
384
- _createLayers(layers) {
385
- var _this = this;
386
-
387
- var layerLoaded = 0;
388
- var layersNumber = 0; // Only count visibles
389
-
173
+ */
174
+ class Wfst extends Control {
175
+ constructor(opt_options) {
176
+ super({
177
+ target: null,
178
+ element: controlElement
179
+ });
180
+ // Check if the selected language exists
181
+ this._i18n =
182
+ opt_options.language && opt_options.language in i18n
183
+ ? i18n[opt_options.language]
184
+ : i18n[DEFAULT_LANGUAGE];
185
+ if (opt_options.i18n) {
186
+ // Merge custom translations
187
+ this._i18n = Object.assign(Object.assign({}, this._i18n), opt_options.i18n);
188
+ }
189
+ // Default options
190
+ const defaultOptions = {
191
+ geoServerUrl: null,
192
+ geoServerAdvanced: {
193
+ getCapabilitiesVersion: '1.3.0',
194
+ getFeatureVersion: '1.0.0',
195
+ describeFeatureTypeVersion: '1.1.0',
196
+ lockFeatureVersion: '1.1.0',
197
+ wfsTransactionVersion: '1.1.0',
198
+ projection: DEFAULT_GEOSERVER_SRS
199
+ },
200
+ headers: {},
201
+ layers: null,
202
+ evtType: 'singleclick',
203
+ active: true,
204
+ showControl: true,
205
+ useLockFeature: true,
206
+ minZoom: 9,
207
+ language: DEFAULT_LANGUAGE,
208
+ uploadFormats: '.geojson,.json,.kml',
209
+ processUpload: null,
210
+ beforeInsertFeature: null,
211
+ modal: {
212
+ animateClass: 'fade',
213
+ animateInClass: 'show',
214
+ transition: 300,
215
+ backdropTransition: 150,
216
+ templates: {
217
+ dialog: '<div class="modal-dialog modal-dialog-centered"></div>',
218
+ headerClose: `<button type="button" class="btn-close" data-dismiss="modal" aria-label="${this._i18n.labels.close}"><span aria-hidden="true">×</span></button>`
219
+ }
220
+ }
221
+ };
222
+ this._options = deepObjectAssign(defaultOptions, opt_options);
223
+ this._mapLayers = [];
224
+ this._countRequests = 0;
225
+ this._isEditModeOn = false;
226
+ // GeoServer
227
+ this._hasLockFeature = false;
228
+ this._hasTransaction = false;
229
+ this._geoServerCapabilities = null;
230
+ this._geoServerData = {};
231
+ // Editing
232
+ this._editedFeatures = new Set();
233
+ this._layerToInsertElements = this._options.layers[0].name; // By default, the first layer is ready to accept new draws
234
+ this._insertFeatures = [];
235
+ this._updateFeatures = [];
236
+ this._deleteFeatures = [];
237
+ // Formats
238
+ this._formatWFS = new WFS();
239
+ this._formatGeoJSON = new GeoJSON();
240
+ this._formatKml = new KML({
241
+ extractStyles: false,
242
+ showPointNames: false
243
+ });
244
+ this._xs = new XMLSerializer();
245
+ this._controlWidgetToolsDiv = controlElement;
246
+ this._controlWidgetToolsDiv.className = 'ol-wfst--tools-control';
247
+ this._initAsyncOperations();
248
+ }
390
249
  /**
391
- * When all the data is loaded, hide the loading
392
250
  * @private
393
- */
394
-
395
- var addLayerLoaded = () => {
396
- layerLoaded++;
397
-
398
- if (layerLoaded === layersNumber) {
399
- this._hideLoading();
400
- }
401
- };
251
+ */
252
+ _onLoad() {
253
+ this._map = super.getMap();
254
+ this._view = this._map.getView();
255
+ this._viewport = this._map.getViewport();
256
+ // State
257
+ this._isVisible = this._view.getZoom() > this._options.minZoom;
258
+ this._createLayers(this._options.layers);
259
+ this._initMapElements(this._options.showControl, this._options.active);
260
+ }
402
261
  /**
262
+ * Connect to the GeoServer and retrieve metadata about the service (GetCapabilities).
263
+ * Get each layer specs (DescribeFeatureType) and create the layers and map controls.
403
264
  *
404
- * @param layerParams
265
+ * @param layers
266
+ * @param showControl
267
+ * @param active
405
268
  * @private
406
- */
407
-
408
-
409
- var newWmsLayer = layerParams => {
410
- var layerName = layerParams.name;
411
- var cqlFilter = layerParams.cqlFilter;
412
- var buffer = layerParams.tilesBuffer;
413
- var params = {
414
- SERVICE: 'WMS',
415
- LAYERS: layerName,
416
- TILED: true
417
- };
418
-
419
- if (cqlFilter) {
420
- params['CQL_FILTER'] = cqlFilter;
421
- }
422
-
423
- if (buffer) {
424
- params['BUFFER'] = buffer;
425
- }
426
-
427
- var source = new TileWMS({
428
- url: this.options.geoServerUrl,
429
- params: params,
430
- serverType: 'geoserver',
431
- tileLoadFunction: (tile, src) => __awaiter(this, void 0, void 0, function* () {
432
- try {
433
- var response = yield fetch(src, {
434
- headers: this.options.headers
435
- });
436
-
437
- if (!response.ok) {
438
- throw new Error('');
439
- }
440
-
441
- var data = yield response.blob();
442
-
443
- if (data !== undefined) {
444
- tile.getImage().src = URL.createObjectURL(data);
445
- } else {
446
- throw new Error('');
447
- }
448
- } catch (err) {
449
- tile.setState(TileState.ERROR);
450
- } finally {
451
- addLayerLoaded();
452
- }
453
- })
454
- });
455
- var layer_options = Object.assign({
456
- name: layerName,
457
- type: '_wms_',
458
- minZoom: this.options.minZoom,
459
- source: source,
460
- visible: true,
461
- zIndex: 4
462
- }, layerParams);
463
- var layer = new Tile(layer_options);
464
- return layer;
465
- };
269
+ */
270
+ _initAsyncOperations() {
271
+ return __awaiter(this, void 0, void 0, function* () {
272
+ try {
273
+ // @ts-expect-error
274
+ this.on('allDescribeFeatureTypeLoaded', this._onLoad);
275
+ this._showLoading();
276
+ yield this._connectToGeoServerAndGetCapabilities();
277
+ if (this._options.layers) {
278
+ yield this._getGeoserverLayersData(this._options.layers, this._options.geoServerUrl);
279
+ }
280
+ }
281
+ catch (err) {
282
+ this._hideLoading();
283
+ this._showError(err.message, err);
284
+ }
285
+ });
286
+ }
466
287
  /**
288
+ * Get the capabilities from the GeoServer and check
289
+ * all the available operations.
467
290
  *
468
- * @param layerParams
291
+ * @fires capabilitiesLoaded
469
292
  * @private
470
- */
471
-
472
-
473
- var newWfsLayer = layerParams => {
474
- var layerName = layerParams.name;
475
- var cqlFilter = layerParams.cqlFilter;
476
- var strategy = layerParams.wfsStrategy || 'bbox';
477
- var source = new Vector$1({
478
- format: new GeoJSON(),
479
- strategy: strategy === 'bbox' ? bbox : all,
480
- loader: extent => __awaiter(this, void 0, void 0, function* () {
481
- var params = new URLSearchParams({
482
- service: 'wfs',
483
- version: '1.0.0',
484
- request: 'GetFeature',
485
- typename: layerName,
486
- outputFormat: 'application/json',
487
- exceptions: 'application/json',
488
- srsName: DEFAULT_GEOSERVER_SRS
489
- });
490
-
491
- if (cqlFilter) {
492
- params.append('cql_filter', cqlFilter);
493
- } // If bbox, add extent to the request
494
-
495
-
496
- if (strategy === 'bbox') {
497
- var extentGeoServer = transformExtent(extent, this.view.getProjection().getCode(), DEFAULT_GEOSERVER_SRS); // https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
498
- // request features using a bounding box with CRS maybe different from featureTypes native CRS
499
-
500
- params.append('bbox', extentGeoServer.join(',') + ',EPSG:4326');
501
- }
502
-
503
- var url_fetch = this.options.geoServerUrl + '?' + params.toString();
504
-
505
- try {
506
- var response = yield fetch(url_fetch, {
507
- headers: this.options.headers
508
- });
509
-
510
- if (!response.ok) {
511
- throw new Error('');
512
- }
513
-
514
- var data = yield response.json();
515
- var features = source.getFormat().readFeatures(data, {
516
- featureProjection: this.view.getProjection().getCode(),
517
- dataProjection: DEFAULT_GEOSERVER_SRS
518
- });
519
- features.forEach(feature => {
520
- feature.set('_layerName_', layerName,
521
- /* silent = */
522
- true);
523
- });
524
- source.addFeatures(features);
525
- } catch (err) {
526
- this._showError(this._i18n.errors.geoserver);
527
-
528
- console.error(err);
529
- source.removeLoadedExtent(extent);
530
- } finally {
531
- addLayerLoaded();
532
- }
533
- })
534
- });
535
- var layer_options = Object.assign({
536
- name: layerName,
537
- type: '_wfs_',
538
- minZoom: this.options.minZoom,
539
- source: source,
540
- visible: true,
541
- zIndex: 2
542
- }, layerParams);
543
- var layer = new Vector(layer_options);
544
- return layer;
545
- };
546
-
547
- var _loop = function _loop(layerParams) {
548
- var layerName = layerParams.name; // Only create the layer if we can get the GeoserverData
549
-
550
- if (_this._geoServerData[layerName]) {
551
- var layer;
552
-
553
- var _layerParams = _this.options.layers.find(e => e.name === layerName);
554
-
555
- var mode = _layerParams.mode; // If mode is undefined, by default use wfs
556
-
557
- if (!mode) {
558
- _layerParams.mode = 'wfs';
559
- }
560
-
561
- if (_layerParams.mode === 'wfs') {
562
- layer = newWfsLayer(_layerParams);
563
- } else {
564
- layer = newWmsLayer(_layerParams);
565
- }
566
-
567
- if (layer.getVisible()) layersNumber++;
568
-
569
- _this.map.addLayer(layer);
570
-
571
- _this._mapLayers[layerName] = layer;
572
- }
573
- };
574
-
575
- for (var layerParams of layers) {
576
- _loop(layerParams);
577
- }
578
- }
579
- /**
580
- * Create the edit layer to allow modify elements, add interactions,
581
- * map controls and keyboard handlers.
582
- *
583
- * @param showControl
584
- * @param active
585
- * @private
586
- */
587
-
588
-
589
- _initMapElements(showControl, active) {
590
- return __awaiter(this, void 0, void 0, function* () {
591
- // VectorLayer to store features on editing and isnerting
592
- this._createEditLayer();
593
-
594
- this._addInteractions();
595
-
596
- this._addHandlers();
597
-
598
- if (showControl) {
599
- this._addMapControl();
600
- } // By default, init in edit mode
601
-
602
-
603
- this.activateEditMode(active);
604
- });
605
- }
606
- /**
607
- * @private
608
- */
609
-
610
-
611
- _addInteractions() {
612
- // Select the wfs feature already downloaded
613
- var prepareWfsInteraction = () => {
614
- // Interaction to select wfs layer elements
615
- this.interactionWfsSelect = new Select({
616
- hitTolerance: 10,
617
- style: feature => this._styleFunction(feature),
618
- toggleCondition: never,
619
- filter: (feature, layer) => {
620
- return !this._isEditModeOn && layer && layer.get('type') === '_wfs_';
621
- }
622
- });
623
- this.map.addInteraction(this.interactionWfsSelect);
624
- this.interactionWfsSelect.on('select', (_ref) => {
625
- var {
626
- selected,
627
- deselected,
628
- mapBrowserEvent
629
- } = _ref;
630
- var coordinate = mapBrowserEvent.coordinate;
631
-
632
- if (selected.length) {
633
- selected.forEach(feature => {
634
- if (!this._editedFeatures.has(String(feature.getId()))) {
635
- // Remove the feature from the original layer
636
- var layer = this.interactionWfsSelect.getLayer(feature);
637
- layer.getSource().removeFeature(feature);
638
-
639
- this._addFeatureToEdit(feature, coordinate);
640
- }
641
- });
642
- }
643
-
644
- if (deselected.length) {
645
- if (!this._isEditModeOn) {
646
- deselected.forEach(feature => {
647
- // Trigger deselect
648
- // This is necessary for those times where two features overlap.
649
- this.interactionSelectModify.getFeatures().remove(feature);
650
- });
651
- }
652
- }
653
- });
654
- };
293
+ */
294
+ _connectToGeoServerAndGetCapabilities() {
295
+ const _super = Object.create(null, {
296
+ dispatchEvent: { get: () => super.dispatchEvent }
297
+ });
298
+ return __awaiter(this, void 0, void 0, function* () {
299
+ /**
300
+ * @private
301
+ */
302
+ const getCapabilities = () => __awaiter(this, void 0, void 0, function* () {
303
+ const params = new URLSearchParams({
304
+ service: 'wfs',
305
+ version: this._options.geoServerAdvanced.getCapabilitiesVersion,
306
+ request: 'GetCapabilities',
307
+ exceptions: 'application/json'
308
+ });
309
+ const url_fetch = this._options.geoServerUrl + '?' + params.toString();
310
+ try {
311
+ const response = yield fetch(url_fetch, {
312
+ headers: this._options.headers
313
+ });
314
+ if (!response.ok) {
315
+ throw new Error('');
316
+ }
317
+ const data = yield response.text();
318
+ const capabilities = new window.DOMParser().parseFromString(data, 'text/xml');
319
+ return capabilities;
320
+ }
321
+ catch (err) {
322
+ throw new Error(this._i18n.errors.capabilities);
323
+ }
324
+ });
325
+ this._geoServerCapabilities = yield getCapabilities();
326
+ // Available operations in the geoserver
327
+ const operations = this._geoServerCapabilities.getElementsByTagName('ows:Operation');
328
+ Array.from(operations).forEach((operation) => {
329
+ if (operation.getAttribute('name') === 'Transaction') {
330
+ this._hasTransaction = true;
331
+ }
332
+ else if (operation.getAttribute('name') === 'LockFeature') {
333
+ this._hasLockFeature = true;
334
+ }
335
+ });
336
+ if (!this._hasTransaction) {
337
+ throw new Error(this._i18n.errors.wfst);
338
+ }
339
+ _super.dispatchEvent.call(this, {
340
+ type: 'getCapabilities',
341
+ // @ts-expect-error
342
+ data: this._geoServerCapabilities
343
+ });
344
+ });
345
+ }
655
346
  /**
656
- * Call the geoserver to get the clicked feature
347
+ * Request and store data layers obtained by DescribeFeatureType
348
+ *
349
+ * @param layers
350
+ * @param geoServerUrl
657
351
  * @private
658
- */
659
-
660
-
661
- var prepareWmsInteraction = () => {
662
- var getFeatures = evt => __awaiter(this, void 0, void 0, function* () {
663
- var _this2 = this;
664
-
665
- var _loop2 = function* _loop2(layerName) {
666
- var layer = _this2._mapLayers[layerName]; // If layer is hidden or is not a wms, skip
667
-
668
- if (!layer.getVisible() || !(layer.get('type') === '_wms_')) {
669
- return "continue";
670
- }
671
-
672
- var coordinate = evt.coordinate; // Si la vista es lejana, disminumos el buffer
673
- // Si es cercana, lo aumentamos, por ejemplo, para podeer clickear los vectores
674
- // y mejorar la sensibilidad en IOS
675
-
676
- var buffer = _this2.view.getZoom() > 10 ? 10 : 5;
677
- var url = layer.getSource().getFeatureInfoUrl(coordinate, _this2.view.getResolution(), _this2.view.getProjection().getCode(), {
678
- INFO_FORMAT: 'application/json',
679
- BUFFER: buffer,
680
- FEATURE_COUNT: 1,
681
- EXCEPTIONS: 'application/json'
682
- });
683
-
684
- try {
685
- var response = yield fetch(url, {
686
- headers: _this2.options.headers
687
- });
688
-
689
- if (!response.ok) {
690
- throw new Error(_this2._i18n.errors.getFeatures + ' ' + response.status);
691
- }
692
-
693
- var data = yield response.json();
694
-
695
- var features = _this2._formatGeoJSON.readFeatures(data);
696
-
697
- if (!features.length) {
698
- return "continue";
699
- }
700
-
701
- features.forEach(feature => _this2._addFeatureToEdit(feature, coordinate, layerName));
702
- } catch (err) {
703
- _this2._showError(err.message);
704
- }
705
- };
706
-
707
- for (var layerName in this._mapLayers) {
708
- var _ret = yield* _loop2(layerName);
709
-
710
- if (_ret === "continue") continue;
711
- }
712
- });
713
-
714
- this._keyClickWms = this.map.on(this.options.evtType, evt => __awaiter(this, void 0, void 0, function* () {
715
- if (this.map.hasFeatureAtPixel(evt.pixel)) {
716
- return;
717
- }
718
-
719
- if (!this._isVisible) {
720
- return;
721
- } // Only get other features if editmode is disabled
722
-
723
-
724
- if (!this._isEditModeOn) {
725
- yield getFeatures(evt);
726
- }
727
- }));
728
- };
729
-
730
- if (this.options.layers.find(layer => layer.mode === 'wfs')) {
731
- prepareWfsInteraction();
732
- }
733
-
734
- if (this.options.layers.find(layer => layer.mode === 'wms')) {
735
- prepareWmsInteraction();
736
- } // Interaction to allow select features in the edit layer
737
-
738
-
739
- this.interactionSelectModify = new Select({
740
- style: feature => this._styleFunction(feature),
741
- layers: [this._editLayer],
742
- toggleCondition: never,
743
- removeCondition: () => this._isEditModeOn ? true : false // Prevent deselect on clicking outside the feature
744
-
745
- });
746
- this.map.addInteraction(this.interactionSelectModify);
747
- this.interactionModify = new Modify({
748
- style: () => {
749
- if (this._isEditModeOn) {
750
- return new Style({
751
- image: new Circle({
752
- radius: 6,
753
- fill: new Fill({
754
- color: '#ff0000'
755
- }),
756
- stroke: new Stroke({
757
- width: 2,
758
- color: 'rgba(5, 5, 5, 0.9)'
759
- })
760
- })
761
- });
762
- } else {
763
- return;
764
- }
765
- },
766
- features: this.interactionSelectModify.getFeatures(),
767
- condition: evt => {
768
- return primaryAction(evt) && this._isEditModeOn;
769
- }
770
- });
771
- this.map.addInteraction(this.interactionModify);
772
- this.interactionSnap = new Snap({
773
- source: this._editLayer.getSource()
774
- });
775
- this.map.addInteraction(this.interactionSnap);
776
- }
777
- /**
778
- * Layer to store temporary the elements to be edited
779
- *
780
- * @private
781
- */
782
-
783
-
784
- _createEditLayer() {
785
- this._editLayer = new Vector({
786
- source: new Vector$1(),
787
- zIndex: 5,
788
- style: feature => this._styleFunction(feature)
789
- });
790
- this.map.addLayer(this._editLayer);
791
- }
792
- /**
793
- * Add map handlers
794
- *
795
- * @private
796
- */
797
-
798
-
799
- _addHandlers() {
352
+ */
353
+ _getGeoserverLayersData(layers, geoServerUrl) {
354
+ const _super = Object.create(null, {
355
+ dispatchEvent: { get: () => super.dispatchEvent }
356
+ });
357
+ return __awaiter(this, void 0, void 0, function* () {
358
+ /**
359
+ *
360
+ * @param layerName
361
+ * @fires describeFeatureType
362
+ * @fires allDescribeFeatureTypeLoaded
363
+ * @returns
364
+ * @private
365
+ */
366
+ const getLayerData = (layerName) => __awaiter(this, void 0, void 0, function* () {
367
+ const params = new URLSearchParams({
368
+ service: 'wfs',
369
+ version: this._options.geoServerAdvanced
370
+ .describeFeatureTypeVersion,
371
+ request: 'DescribeFeatureType',
372
+ typeName: layerName,
373
+ outputFormat: 'application/json',
374
+ exceptions: 'application/json'
375
+ });
376
+ const url_fetch = geoServerUrl + '?' + params.toString();
377
+ const response = yield fetch(url_fetch, {
378
+ headers: this._options.headers
379
+ });
380
+ if (!response.ok) {
381
+ throw new Error('');
382
+ }
383
+ return yield response.json();
384
+ });
385
+ for (const layer of layers) {
386
+ const layerName = layer.name;
387
+ const layerLabel = layer.label || layerName;
388
+ try {
389
+ const data = yield getLayerData(layerName);
390
+ if (!data) {
391
+ throw new Error('');
392
+ }
393
+ _super.dispatchEvent.call(this, {
394
+ type: 'describeFeatureType',
395
+ // @ts-expect-error
396
+ layer: layerName,
397
+ data: data
398
+ });
399
+ const targetNamespace = data.targetNamespace;
400
+ const properties = data.featureTypes[0].properties;
401
+ // Find the geometry field
402
+ const geom = properties.find((el) => el.type.indexOf('gml:') >= 0);
403
+ this._geoServerData[layerName] = {
404
+ namespace: targetNamespace,
405
+ properties: properties,
406
+ geomType: geom.localType,
407
+ geomField: geom.name
408
+ };
409
+ }
410
+ catch (err) {
411
+ throw new Error(`${this._i18n.errors.layer} "${layerLabel}"`);
412
+ }
413
+ }
414
+ _super.dispatchEvent.call(this, {
415
+ type: 'allDescribeFeatureTypeLoaded',
416
+ // @ts-expect-error
417
+ data: this._geoServerData
418
+ });
419
+ });
420
+ }
800
421
  /**
422
+ * Create map layers in wfs o wms modes.
423
+ *
424
+ * @param layers
801
425
  * @private
802
- */
803
- var keyboardEvents = () => {
804
- document.addEventListener('keydown', (_ref2) => {
805
- var {
806
- key
807
- } = _ref2;
808
- var inputFocus = document.querySelector('input:focus');
809
-
810
- if (inputFocus) {
811
- return;
812
- }
813
-
814
- if (key === 'Delete') {
815
- var selectedFeatures = this.interactionSelectModify.getFeatures();
816
-
817
- if (selectedFeatures) {
818
- selectedFeatures.forEach(feature => {
819
- this._deleteFeature(feature, true);
820
- });
821
- }
822
- }
823
- });
824
- }; // When a feature is modified, add this to a list.
825
- // This prevent events fired on select and deselect features that has no changes and should
826
- // not be updated in the geoserver
827
-
828
-
829
- this.interactionModify.on('modifystart', evt => {
830
- this._addFeatureToEditedList(evt.features.item(0));
831
- });
832
-
833
- this._onDeselectFeatureEvent();
834
-
835
- this._onRemoveFeatureEvent();
426
+ */
427
+ _createLayers(layers) {
428
+ let layerLoaded = 0;
429
+ let layersNumber = 0; // Only count visibles
430
+ /**
431
+ * When all the data is loaded, hide the loading
432
+ * @private
433
+ */
434
+ const addLayerLoaded = () => {
435
+ layerLoaded++;
436
+ if (layerLoaded >= layersNumber) {
437
+ this._hideLoading();
438
+ }
439
+ };
440
+ /**
441
+ *
442
+ * @param layerParams
443
+ * @private
444
+ */
445
+ const newWmsLayer = (layerParams) => {
446
+ const layerName = layerParams.name;
447
+ const cqlFilter = layerParams.cqlFilter;
448
+ const buffer = layerParams.tilesBuffer;
449
+ const params = {
450
+ SERVICE: 'WMS',
451
+ LAYERS: layerName,
452
+ TILED: true
453
+ };
454
+ if (cqlFilter) {
455
+ params['CQL_FILTER'] = cqlFilter;
456
+ }
457
+ if (buffer) {
458
+ params['BUFFER'] = buffer;
459
+ }
460
+ const source = new TileWMS({
461
+ url: this._options.geoServerUrl,
462
+ params: params,
463
+ serverType: 'geoserver',
464
+ tileLoadFunction: (tile, src) => __awaiter(this, void 0, void 0, function* () {
465
+ try {
466
+ const response = yield fetch(src, {
467
+ headers: this._options.headers
468
+ });
469
+ if (!response.ok) {
470
+ throw new Error('');
471
+ }
472
+ const data = yield response.blob();
473
+ if (data !== undefined) {
474
+ tile.getImage().src = URL.createObjectURL(data);
475
+ }
476
+ else {
477
+ throw new Error('');
478
+ }
479
+ tile.setState(TileState.LOADED);
480
+ }
481
+ catch (err) {
482
+ tile.setState(TileState.ERROR);
483
+ }
484
+ })
485
+ });
486
+ let loading = 0;
487
+ let loaded = 0;
488
+ source.on('tileloadstart', () => {
489
+ loading++;
490
+ this._showLoading();
491
+ });
492
+ source.on(['tileloadend', 'tileloaderror'], () => {
493
+ loaded++;
494
+ setTimeout(() => {
495
+ if (loading === loaded)
496
+ addLayerLoaded();
497
+ }, 300);
498
+ });
499
+ const layer_options = Object.assign({ name: layerName, type: '_wms_', minZoom: this._options.minZoom, source: source, visible: true, zIndex: 1 }, layerParams);
500
+ const layer = new Tile(layer_options);
501
+ return layer;
502
+ };
503
+ /**
504
+ *
505
+ * @param layerParams
506
+ * @private
507
+ */
508
+ const newWfsLayer = (layerParams) => {
509
+ const layerName = layerParams.name;
510
+ const cqlFilter = layerParams.cqlFilter;
511
+ const strategy = layerParams.wfsStrategy || 'bbox';
512
+ const source = new Vector$1({
513
+ format: new GeoJSON(),
514
+ strategy: strategy === 'bbox' ? bbox : all,
515
+ loader: (extent) => __awaiter(this, void 0, void 0, function* () {
516
+ try {
517
+ const params = new URLSearchParams({
518
+ service: 'wfs',
519
+ version: this._options.geoServerAdvanced
520
+ .getFeatureVersion,
521
+ request: 'GetFeature',
522
+ typename: layerName,
523
+ outputFormat: 'application/json',
524
+ exceptions: 'application/json',
525
+ srsName: this._options.geoServerAdvanced.projection.toString()
526
+ });
527
+ if (cqlFilter) {
528
+ params.append('cql_filter', cqlFilter);
529
+ }
530
+ // If bbox, add extent to the request
531
+ if (strategy === 'bbox') {
532
+ const extentGeoServer = transformExtent(extent, this._view.getProjection().getCode(), this._options.geoServerAdvanced.projection);
533
+ // https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
534
+ // request features using a bounding box with CRS maybe different from featureTypes native CRS
535
+ params.append('bbox', extentGeoServer.toString() +
536
+ `,${this._options.geoServerAdvanced.projection}`);
537
+ }
538
+ const url_fetch = this._options.geoServerUrl +
539
+ '?' +
540
+ params.toString();
541
+ const response = yield fetch(url_fetch, {
542
+ headers: this._options.headers
543
+ });
544
+ if (!response.ok) {
545
+ throw new Error('');
546
+ }
547
+ const data = yield response.json();
548
+ this.dispatchEvent({
549
+ type: 'getFeature',
550
+ // @ts-expect-error
551
+ layer: layerName,
552
+ data: data
553
+ });
554
+ const features = source.getFormat().readFeatures(data, {
555
+ featureProjection: this._view
556
+ .getProjection()
557
+ .getCode(),
558
+ dataProjection: this._options.geoServerAdvanced
559
+ .projection
560
+ });
561
+ features.forEach((feature) => {
562
+ feature.set('_layerName_', layerName,
563
+ /* silent = */ true);
564
+ });
565
+ source.addFeatures(features);
566
+ source.dispatchEvent('featuresloadend');
567
+ }
568
+ catch (err) {
569
+ source.dispatchEvent('featuresloaderror');
570
+ this._showError(this._i18n.errors.geoserver, err);
571
+ source.removeLoadedExtent(extent);
572
+ }
573
+ })
574
+ });
575
+ let loading = 0;
576
+ let loaded = 0;
577
+ source.on('featuresloadstart', () => {
578
+ loading++;
579
+ this._showLoading();
580
+ });
581
+ source.on(['featuresloadend', 'featuresloaderror'], () => {
582
+ loaded++;
583
+ setTimeout(() => {
584
+ if (loading === loaded)
585
+ addLayerLoaded();
586
+ }, 300);
587
+ });
588
+ const layer_options = Object.assign({ name: layerName, type: '_wfs_', minZoom: this._options.minZoom, source: source, visible: true, zIndex: 2 }, layerParams);
589
+ const layer = new Vector(layer_options);
590
+ return layer;
591
+ };
592
+ for (const layerParams of layers) {
593
+ const layerName = layerParams.name;
594
+ // Only create the layer if we can get the GeoserverData
595
+ if (this._geoServerData[layerName]) {
596
+ let layer;
597
+ const layerParams = this._options.layers.find((e) => e.name === layerName);
598
+ const mode = layerParams.mode;
599
+ // If mode is undefined, by default use wfs
600
+ if (!mode) {
601
+ layerParams.mode = 'wfs';
602
+ }
603
+ if (layerParams.mode === 'wfs') {
604
+ layer = newWfsLayer(layerParams);
605
+ }
606
+ else {
607
+ layer = newWmsLayer(layerParams);
608
+ }
609
+ if (layer.getVisible())
610
+ layersNumber++;
611
+ this._map.addLayer(layer);
612
+ this._mapLayers[layerName] = layer;
613
+ }
614
+ }
615
+ }
836
616
  /**
617
+ * Create the edit layer to allow modify elements, add interactions,
618
+ * map controls and keyboard handlers.
619
+ *
620
+ * @param showControl
621
+ * @param active
837
622
  * @private
838
- */
839
-
840
-
841
- var handleZoomEnd = () => {
842
- if (this._currentZoom > this.options.minZoom) {
843
- // Show the layers
844
- if (!this._isVisible) {
845
- this._isVisible = true;
846
- }
847
- } else {
848
- // Hide the layer
849
- if (this._isVisible) {
850
- this._isVisible = false;
851
- }
852
- }
853
- };
854
-
855
- this.map.on('moveend', () => {
856
- this._currentZoom = this.view.getZoom();
857
-
858
- if (this._currentZoom !== this._lastZoom) {
859
- handleZoomEnd();
860
- }
861
-
862
- this._lastZoom = this._currentZoom;
863
- });
864
- keyboardEvents();
865
- }
866
- /**
867
- * Add the widget on the map to allow change the tools and select active layers
868
- * @private
869
- */
870
-
871
-
872
- _addMapControl() {
873
- var createLayersControl = () => {
874
- var createLayerElements = layerParams => {
875
- var layerName = layerParams.name;
876
- var layerLabel = "<span title=\"".concat(this._geoServerData[layerName].geomType, "\">").concat(layerParams.label || layerName, "</span>");
877
- var visible = 'visible' in layerParams ? layerParams.visible : true;
878
- return "\n <div class=\"wfst--layer-control \n ".concat(visible ? 'ol-wfst--visible-on' : '', "\n ").concat(layerName === this._layerToInsertElements ? 'ol-wfst--selected-on' : '', "\n \" data-layer=\"").concat(layerName, "\">\n <div class=\"ol-wfst--tools-control-visible\">\n <span class=\"ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on\" title=\"").concat(this._i18n.labels.toggleVisibility, "\">\n <img src=\"").concat(img$4, "\"/>\n </span>\n <span class=\"ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off\" title=\"").concat(this._i18n.labels.toggleVisibility, "\">\n <img src=\"").concat(img$5, "\"/>\n </span>\n </div>\n <label for=\"wfst--").concat(layerName, "\">\n <input value=\"").concat(layerName, "\" id=\"wfst--").concat(layerName, "\" type=\"radio\" class=\"ol-wfst--tools-control-input\" name=\"wfst--select-layer\" ").concat(layerName === this._layerToInsertElements ? 'checked="checked"' : '', ">\n ").concat(layerLabel, "\n </label>\n </div>");
879
- };
880
-
881
- var htmlLayers = '';
882
- Object.keys(this._mapLayers).map(key => htmlLayers += createLayerElements(this.options.layers.find(el => el.name === key)));
883
- var selectLayers = document.createElement('div');
884
- selectLayers.className = 'wfst--tools-control--select-layers';
885
- selectLayers.innerHTML = htmlLayers; // Layer Selector
886
-
887
- var radioInputs = selectLayers.querySelectorAll('input');
888
- radioInputs.forEach(radioInput => {
889
- var parentDiv = radioInput.closest('.wfst--layer-control');
890
-
891
- radioInput.onchange = () => {
892
- // Deselect DOM previous layer
893
- var selected = selectLayers.querySelector('.ol-wfst--selected-on');
894
- if (selected) selected.classList.remove('ol-wfst--selected-on'); // Select this layer
895
-
896
- parentDiv.classList.add('ol-wfst--selected-on');
897
- this._layerToInsertElements = radioInput.value;
898
-
899
- this._changeStateSelect(this._layerToInsertElements);
900
- };
901
- }); // Visibility toggler
902
-
903
- var visibilityBtn = selectLayers.querySelectorAll('.ol-wfst--tools-control-visible-btn');
904
- visibilityBtn.forEach(btn => {
905
- var parentDiv = btn.closest('.wfst--layer-control');
906
- var layerName = parentDiv.dataset['layer'];
907
-
908
- btn.onclick = () => {
909
- parentDiv.classList.toggle('ol-wfst--visible-on');
910
- var layer = this._mapLayers[layerName];
911
-
912
- if (parentDiv.classList.contains('ol-wfst--visible-on')) {
913
- layer.setVisible(true);
914
- } else {
915
- layer.setVisible(false);
916
- }
917
- };
918
- });
919
- return selectLayers;
920
- };
921
-
922
- var createHeadControl = () => {
923
- /**
924
- * @private
925
- */
926
- var createUploadElements = () => {
927
- var container = document.createElement('div'); // Upload button Tool
928
-
929
- var uploadButton = document.createElement('label');
930
- uploadButton.className = 'ol-wfst--tools-control-btn ol-wfst--tools-control-btn-upload';
931
- uploadButton.htmlFor = 'ol-wfst--upload';
932
- uploadButton.innerHTML = "<img src=\"".concat(img$3, "\"/> ");
933
- uploadButton.title = this._i18n.labels.uploadToLayer; // Hidden Input form
934
-
935
- var uploadInput = document.createElement('input');
936
- uploadInput.id = 'ol-wfst--upload';
937
- uploadInput.type = 'file';
938
- uploadInput.accept = this.options.uploadFormats;
939
-
940
- uploadInput.onchange = evt => this._processUploadFile(evt);
941
-
942
- container.append(uploadInput);
943
- container.append(uploadButton);
944
- return container;
945
- };
946
-
947
- var createDrawContainer = () => {
948
- var drawContainer = document.createElement('div');
949
- drawContainer.className = 'ol-wfst--tools-control-draw-cnt'; // Draw Tool
950
-
951
- var drawButton = document.createElement('button');
952
- drawButton.className = 'ol-wfst--tools-control-btn ol-wfst--tools-control-btn-draw';
953
- drawButton.type = 'button';
954
- drawButton.innerHTML = "<img src=\"".concat(img, "\"/>");
955
- drawButton.title = this._i18n.labels.addElement;
956
-
957
- drawButton.onclick = () => {
958
- if (this._isDrawModeOn) {
959
- this._resetStateButtons();
960
-
961
- this.activateEditMode();
962
- } else {
963
- this.activateDrawMode(this._layerToInsertElements);
964
- }
965
- }; // Select geom type
966
-
967
-
968
- var select = document.createElement('select');
969
- select.title = this._i18n.labels.selectDrawType;
970
- select.className = 'wfst--tools-control--select-draw';
971
-
972
- select.onchange = () => {
973
- var selectedValue = select.value;
974
-
975
- this._changeStateSelect(this._layerToInsertElements, selectedValue);
976
-
977
- if (this._isDrawModeOn) {
978
- this.activateDrawMode(this._layerToInsertElements);
979
- }
980
- };
981
-
982
- var types = [GeometryType.POINT, GeometryType.MULTI_POINT, GeometryType.LINE_STRING, GeometryType.MULTI_LINE_STRING, GeometryType.POLYGON, GeometryType.MULTI_POLYGON, GeometryType.CIRCLE];
983
-
984
- for (var type of types) {
985
- var option = document.createElement('option');
986
- option.value = type;
987
- option.text = type;
988
- option.selected = this._geoServerData[this._layerToInsertElements].geomType === type || false;
989
- select.appendChild(option);
990
- }
991
-
992
- drawContainer.append(drawButton);
993
- drawContainer.append(select);
994
- this._selectDraw = select;
995
- return drawContainer;
996
- };
997
-
998
- var subControl = document.createElement('div');
999
- subControl.className = 'wfst--tools-control--head'; // Upload section
1000
-
1001
- if (this.options.showUpload) {
1002
- var uploadSection = createUploadElements();
1003
- subControl.append(uploadSection);
1004
- }
1005
-
1006
- var drawContainer = createDrawContainer();
1007
- subControl.append(drawContainer);
1008
- return subControl;
1009
- };
1010
-
1011
- var headControl = createHeadControl();
1012
-
1013
- this._controlWidgetToolsDiv.append(headControl);
1014
-
1015
- var htmlLayers = createLayersControl();
1016
-
1017
- this._controlWidgetToolsDiv.append(htmlLayers);
1018
- }
1019
- /**
1020
- * Show Loading modal
1021
- *
1022
- * @private
1023
- */
1024
-
1025
-
1026
- _showLoading() {
1027
- if (!this._modalLoading) {
1028
- this._modalLoading = document.createElement('div');
1029
- this._modalLoading.className = 'ol-wfst--tools-control--loading';
1030
- this._modalLoading.innerHTML = this._i18n.labels.loading;
1031
-
1032
- this._controlWidgetToolsDiv.append(this._modalLoading);
1033
- }
1034
-
1035
- this._modalLoading.classList.add('ol-wfst--tools-control--loading-show');
1036
- }
1037
-
1038
- _hideLoading() {
1039
- this._modalLoading.classList.remove('ol-wfst--tools-control--loading-show');
1040
- }
1041
- /**
1042
- * Lock a feature in the geoserver before edit
1043
- *
1044
- * @param featureId
1045
- * @param layerName
1046
- * @param retry
1047
- * @private
1048
- */
1049
-
1050
-
1051
- _lockFeature(featureId, layerName) {
1052
- var retry = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
1053
- return __awaiter(this, void 0, void 0, function* () {
1054
- var params = new URLSearchParams({
1055
- service: 'wfs',
1056
- version: '1.1.0',
1057
- request: 'LockFeature',
1058
- expiry: String(5),
1059
- LockId: 'GeoServer',
1060
- typeName: layerName,
1061
- releaseAction: 'SOME',
1062
- exceptions: 'application/json',
1063
- featureid: "".concat(featureId)
1064
- });
1065
- var url_fetch = this.options.geoServerUrl + '?' + params.toString();
1066
-
1067
- try {
1068
- var response = yield fetch(url_fetch, {
1069
- headers: this.options.headers
1070
- });
1071
-
1072
- if (!response.ok) {
1073
- throw new Error(this._i18n.errors.lockFeature);
1074
- }
1075
-
1076
- var data = yield response.text();
1077
-
1078
- try {
1079
- // First, check if is a JSON (with errors)
1080
- var dataParsed = JSON.parse(data);
1081
-
1082
- if ('exceptions' in dataParsed) {
1083
- var exceptions = dataParsed.exceptions;
1084
-
1085
- if (exceptions[0].code === 'CannotLockAllFeatures') {
1086
- // Maybe the Feature is already blocked, ant thats trigger error, so, we try one locking more time again
1087
- if (!retry) {
1088
- this._lockFeature(featureId, layerName, 1);
1089
- } else {
1090
- this._showError(this._i18n.errors.lockFeature);
1091
- }
1092
- } else {
1093
- this._showError(exceptions[0].text);
1094
- }
1095
- }
1096
- } catch (err) {
1097
- /*
1098
- let dataDoc = (new window.DOMParser()).parseFromString(data, 'text/xml');
1099
- let lockId = dataDoc.getElementsByTagName('wfs:LockId');
1100
- let featuresLocked: HTMLCollectionOf<Element> = dataDoc.getElementsByTagName('ogc:FeatureId');
1101
- for (let featureLocked of featuresLocked as any) {
1102
- console.log(featureLocked.getAttribute('fid'));
1103
- }
1104
- */
1105
- }
1106
-
1107
- return data;
1108
- } catch (err) {
1109
- this._showError(err.message);
1110
- }
1111
- });
1112
- }
1113
- /**
1114
- * Show modal with errors
1115
- *
1116
- * @param msg
1117
- * @private
1118
- */
1119
-
1120
-
1121
- _showError(msg) {
1122
- Modal.alert('Error: ' + msg, {
1123
- animateInClass: 'in'
1124
- }).show();
1125
- }
1126
- /**
1127
- * Make the WFS Transactions
1128
- *
1129
- * @param action
1130
- * @param features
1131
- * @param layerName
1132
- * @private
1133
- */
1134
-
1135
-
1136
- _transactWFS(action, features, layerName) {
1137
- return __awaiter(this, void 0, void 0, function* () {
1138
- var transformCircleToPolygon = (feature, geom) => {
1139
- var geomConverted = fromCircle(geom);
1140
- feature.setGeometry(geomConverted);
1141
- };
1142
-
1143
- var transformGeoemtryCollectionToGeometries = (feature, geom) => {
1144
- var geomConverted = geom.getGeometries()[0];
1145
-
1146
- if (geomConverted.getType() === GeometryType.CIRCLE) {
1147
- geomConverted = fromCircle(geomConverted);
1148
- }
1149
-
1150
- feature.setGeometry(geomConverted);
1151
- };
1152
-
1153
- features = Array.isArray(features) ? features : [features];
1154
-
1155
- var cloneFeature = feature => {
1156
- this._removeFeatureFromEditList(feature);
1157
-
1158
- var featureProperties = feature.getProperties();
1159
- delete featureProperties.boundedBy;
1160
- delete featureProperties._layerName_;
1161
- var clone = new Feature(featureProperties);
1162
- clone.setId(feature.getId());
1163
- return clone;
1164
- };
1165
-
1166
- var refreshWmsLayer = layer => {
1167
- var source = layer.getSource(); // Refrescamos el wms
1168
-
1169
- source.refresh(); // Force refresh the tiles
1170
-
1171
- var params = source.getParams();
1172
- params.t = new Date().getMilliseconds();
1173
- source.updateParams(params);
1174
- };
1175
-
1176
- var refreshWfsLayer = layer => {
1177
- var source = layer.getSource(); // Refrescamos el wms
1178
-
1179
- source.refresh();
1180
- };
1181
-
1182
- var clonedFeatures = [];
1183
-
1184
- for (var feature of features) {
1185
- var clone = cloneFeature(feature);
1186
- var cloneGeom = clone.getGeometry();
1187
- var cloneGeomType = cloneGeom.getType(); // Ugly fix to support GeometryCollection on GML
1188
- // See https://github.com/openlayers/openlayers/issues/4220
1189
-
1190
- if (cloneGeomType === GeometryType.GEOMETRY_COLLECTION) {
1191
- transformGeoemtryCollectionToGeometries(clone, cloneGeom);
1192
- } else if (cloneGeomType === GeometryType.CIRCLE) {
1193
- // Geoserver has no Support to Circles
1194
- transformCircleToPolygon(clone, cloneGeom);
1195
- }
1196
-
1197
- if (action === 'insert') {
1198
- // Filters
1199
- if (this.options.beforeInsertFeature) {
1200
- clone = this.options.beforeInsertFeature(clone);
1201
- }
1202
- }
1203
-
1204
- if (clone) {
1205
- clonedFeatures.push(clone);
1206
- }
1207
- }
1208
-
1209
- if (!clonedFeatures.length) {
1210
- return this._showError(this._i18n.errors.noValidGeometry);
1211
- }
1212
-
1213
- switch (action) {
1214
- case 'insert':
1215
- this._insertFeatures = [...this._insertFeatures, ...clonedFeatures];
1216
- break;
1217
-
1218
- case 'update':
1219
- this._updateFeatures = [...this._updateFeatures, ...clonedFeatures];
1220
- break;
1221
-
1222
- case 'delete':
1223
- this._deleteFeatures = [...this._deleteFeatures, ...clonedFeatures];
1224
- break;
1225
- }
1226
-
1227
- this._countRequests++;
1228
- var numberRequest = this._countRequests;
1229
- setTimeout(() => __awaiter(this, void 0, void 0, function* () {
1230
- // Prevent fire multiples times
1231
- if (numberRequest !== this._countRequests) {
1232
- return;
1233
- }
1234
-
1235
- var srs = this.view.getProjection().getCode(); // Force latitude/longitude order on transactions
1236
- // EPSG:4326 is longitude/latitude (assumption) and is not managed correctly by GML3
1237
-
1238
- srs = srs === 'EPSG:4326' ? DEFAULT_GEOSERVER_SRS : srs;
1239
- var options = {
1240
- featureNS: this._geoServerData[layerName].namespace,
1241
- featureType: layerName,
1242
- srsName: srs,
1243
- featurePrefix: null,
1244
- nativeElements: null
1245
- };
1246
-
1247
- var transaction = this._formatWFS.writeTransaction(this._insertFeatures, this._updateFeatures, this._deleteFeatures, options);
1248
-
1249
- var payload = this._xs.serializeToString(transaction);
1250
-
1251
- var geomType = this._geoServerData[layerName].geomType;
1252
- var geomField = this._geoServerData[layerName].geomField; // Ugly fix to support GeometryCollection on GML
1253
- // See https://github.com/openlayers/openlayers/issues/4220
1254
-
1255
- if (geomType === GeometryType.GEOMETRY_COLLECTION) {
1256
- if (action === 'insert') {
1257
- payload = payload.replace(/<geometry>/g, "<geometry><MultiGeometry xmlns=\"http://www.opengis.net/gml\" srsName=\"".concat(srs, "\"><geometryMember>"));
1258
- payload = payload.replace(/<\/geometry>/g, "</geometryMember></MultiGeometry></geometry>");
1259
- } else if (action === 'update') {
1260
- var gmemberIn = "<MultiGeometry xmlns=\"http://www.opengis.net/gml\" srsName=\"".concat(srs, "\"><geometryMember>");
1261
- var gmemberOut = "</geometryMember></MultiGeometry>";
1262
- payload = payload.replace(/(.*)(<Name>geometry<\/Name><Value>)(.*?)(<\/Value>)(.*)/g, "$1$2".concat(gmemberIn, "$3").concat(gmemberOut, "$4$5"));
1263
- }
1264
- } // Fixes geometry name, weird bug with GML:
1265
- // The property for the geometry column is always named "geometry"
1266
-
1267
-
1268
- if (action === 'insert') {
1269
- payload = payload.replace(/<(\/?)\bgeometry\b>/g, "<$1".concat(geomField, ">"));
1270
- } else {
1271
- payload = payload.replace(/<Name>geometry<\/Name>/g, "<Name>".concat(geomField, "</Name>"));
1272
- } // Add default LockId value
1273
-
1274
-
1275
- if (this._hasLockFeature && this._useLockFeature && action !== 'insert') {
1276
- payload = payload.replace("</Transaction>", "<LockId>GeoServer</LockId></Transaction>");
1277
- }
1278
-
1279
- try {
1280
- var headers = Object.assign({
1281
- 'Content-Type': 'text/xml'
1282
- }, this.options.headers);
1283
- var response = yield fetch(this.options.geoServerUrl, {
1284
- method: 'POST',
1285
- body: payload,
1286
- headers: headers
1287
- });
1288
-
1289
- if (!response.ok) {
1290
- throw new Error(this._i18n.errors.transaction + ' ' + response.status);
1291
- }
1292
-
1293
- var parseResponse = this._formatWFS.readTransactionResponse(response);
1294
-
1295
- if (!Object.keys(parseResponse).length) {
1296
- var responseStr = yield response.text();
1297
- var findError = String(responseStr).match(/<ows:ExceptionText>([\s\S]*?)<\/ows:ExceptionText>/);
1298
-
1299
- if (findError) {
1300
- this._showError(findError[1]);
1301
- }
1302
- }
1303
-
1304
- if (action !== 'delete') {
1305
- for (var _feature of features) {
1306
- this._editLayer.getSource().removeFeature(_feature);
1307
- }
1308
- }
1309
-
1310
- var {
1311
- mode
1312
- } = this.options.layers.find(layer => layer.name === layerName);
1313
-
1314
- if (mode === 'wfs') {
1315
- refreshWfsLayer(this._mapLayers[layerName]);
1316
- } else if (mode === 'wms') {
1317
- refreshWmsLayer(this._mapLayers[layerName]);
1318
- }
1319
-
1320
- this._hideLoading();
1321
- } catch (err) {
1322
- console.error(err);
1323
- }
1324
-
1325
- this._insertFeatures = [];
1326
- this._updateFeatures = [];
1327
- this._deleteFeatures = [];
1328
- this._countRequests = 0;
1329
- }), 0);
1330
- });
1331
- }
1332
- /**
1333
- *
1334
- * @param feature
1335
- * @private
1336
- */
1337
-
1338
-
1339
- _addFeatureToEditedList(feature) {
1340
- this._editedFeatures.add(String(feature.getId()));
1341
- }
1342
- /**
1343
- *
1344
- * @param feature
1345
- * @private
1346
- */
1347
-
1348
-
1349
- _removeFeatureFromEditList(feature) {
1350
- this._editedFeatures.delete(String(feature.getId()));
1351
- }
1352
- /**
1353
- *
1354
- * @param feature
1355
- * @private
1356
- */
1357
-
1358
-
1359
- _isFeatureEdited(feature) {
1360
- return this._editedFeatures.has(String(feature.getId()));
1361
- }
1362
- /**
1363
- *
1364
- * @param feature
1365
- * @private
1366
- */
1367
-
1368
-
1369
- _deselectEditFeature(feature) {
1370
- this._removeOverlayHelper(feature);
1371
- }
1372
- /**
1373
- *
1374
- * @param feature
1375
- * @param layerName
1376
- * @private
1377
- */
1378
-
1379
-
1380
- _restoreFeatureToLayer(feature, layerName) {
1381
- layerName = layerName || feature.get('_layerName_');
1382
- var layer = this._mapLayers[layerName];
1383
- layer.getSource().addFeature(feature);
1384
- }
1385
-
1386
- _removeFeatureFromTmpLayer(feature) {
1387
- // Remove element from the Layer
1388
- this._editLayer.getSource().removeFeature(feature);
1389
- }
1390
- /**
1391
- * Trigger on deselecting a feature from in the Edit layer
1392
- *
1393
- * @private
1394
- */
1395
-
1396
-
1397
- _onDeselectFeatureEvent() {
1398
- var checkIfFeatureIsChanged = feature => {
1399
- var layerName = feature.get('_layerName_');
1400
- var {
1401
- mode
1402
- } = this.options.layers.find(layer => layer.name === layerName);
1403
-
1404
- if (mode === 'wfs') {
1405
- this.interactionWfsSelect.getFeatures().remove(feature);
1406
- }
1407
-
1408
- if (this._isFeatureEdited(feature)) {
1409
- this._transactWFS('update', feature, layerName);
1410
- } else {
1411
- // Si es wfs y el elemento no tuvo cambios, lo devolvemos a la layer original
1412
- if (mode === 'wfs') {
1413
- this._restoreFeatureToLayer(feature, layerName);
1414
- }
1415
-
1416
- this._removeFeatureFromTmpLayer(feature);
1417
- }
1418
- }; // This is fired when a feature is deselected and fires the transaction process
1419
-
1420
-
1421
- this._keySelect = this.interactionSelectModify.getFeatures().on('remove', evt => {
1422
- var feature = evt.element;
1423
-
1424
- this._deselectEditFeature(feature);
1425
-
1426
- checkIfFeatureIsChanged(feature);
1427
-
1428
- this._editModeOff();
1429
- });
1430
- }
1431
- /**
1432
- * Trigger on removing a feature from the Edit layer
1433
- *
1434
- * @private
1435
- */
1436
-
1437
-
1438
- _onRemoveFeatureEvent() {
1439
- // If a feature is removed from the edit layer
1440
- this._keyRemove = this._editLayer.getSource().on('removefeature', evt => {
1441
- var feature = evt.feature;
1442
-
1443
- if (!feature.get('_delete_')) {
1444
- return;
1445
- }
1446
-
1447
- if (this._keySelect) {
1448
- unByKey(this._keySelect);
1449
- }
1450
-
1451
- var layerName = feature.get('_layerName_');
1452
-
1453
- this._transactWFS('delete', feature, layerName);
1454
-
1455
- this._deselectEditFeature(feature);
1456
-
1457
- this._editModeOff();
1458
-
1459
- if (this._keySelect) {
1460
- setTimeout(() => {
1461
- this._onDeselectFeatureEvent();
1462
- }, 150);
1463
- }
1464
- });
1465
- }
1466
- /**
1467
- * Master style that handles two modes on the Edit Layer:
1468
- * - one is the basic, showing only the vertices
1469
- * - and the other when modify is active, showing bigger vertices
1470
- *
1471
- * @param feature
1472
- * @private
1473
- */
1474
-
1475
-
1476
- _styleFunction(feature) {
1477
- var getVertexs = feature => {
1478
- var geometry = feature.getGeometry();
1479
- var type = geometry.getType();
1480
-
1481
- if (type === GeometryType.GEOMETRY_COLLECTION) {
1482
- geometry = geometry.getGeometries()[0];
1483
- type = geometry.getType();
1484
- }
1485
-
1486
- var coordinates = geometry.getCoordinates();
1487
- var flatCoordinates = null;
1488
-
1489
- if (type === GeometryType.POLYGON || type === GeometryType.MULTI_LINE_STRING) {
1490
- flatCoordinates = coordinates.flat(1);
1491
- } else if (type === GeometryType.MULTI_POLYGON) {
1492
- flatCoordinates = coordinates.flat(2);
1493
- } else {
1494
- flatCoordinates = coordinates;
1495
- }
1496
-
1497
- if (!flatCoordinates || !flatCoordinates.length) {
1498
- return;
1499
- }
1500
-
1501
- return new MultiPoint(flatCoordinates);
1502
- };
1503
-
1504
- var geometry = feature.getGeometry();
1505
- var type = geometry.getType();
1506
-
1507
- if (type === GeometryType.GEOMETRY_COLLECTION) {
1508
- geometry = geometry.getGeometries()[0];
1509
- type = geometry.getType();
1510
- }
1511
-
1512
- switch (type) {
1513
- case 'Point':
1514
- case 'MultiPoint':
1515
- if (this._isEditModeOn) {
1516
- return [new Style({
1517
- image: new Circle({
1518
- radius: 6,
1519
- fill: new Fill({
1520
- color: '#000000'
1521
- })
1522
- })
1523
- }), new Style({
1524
- image: new Circle({
1525
- radius: 4,
1526
- fill: new Fill({
1527
- color: '#ff0000'
1528
- })
1529
- })
1530
- })];
1531
- } else {
1532
- return [new Style({
1533
- image: new Circle({
1534
- radius: 5,
1535
- fill: new Fill({
1536
- color: '#ff0000'
1537
- })
1538
- })
1539
- }), new Style({
1540
- image: new Circle({
1541
- radius: 2,
1542
- fill: new Fill({
1543
- color: '#000000'
1544
- })
1545
- })
1546
- })];
1547
- }
1548
-
1549
- default:
1550
- // If editing mode is active, show bigger vertex
1551
- if (this._isEditModeOn || this._isDrawModeOn) {
1552
- return [new Style({
1553
- stroke: new Stroke({
1554
- color: 'rgba( 255, 0, 0, 1)',
1555
- width: 4
1556
- }),
1557
- fill: new Fill({
1558
- color: 'rgba(255, 0, 0, 0.7)'
1559
- })
1560
- }), new Style({
1561
- image: new Circle({
1562
- radius: 4,
1563
- fill: new Fill({
1564
- color: '#ff0000'
1565
- }),
1566
- stroke: new Stroke({
1567
- width: 2,
1568
- color: 'rgba(5, 5, 5, 0.9)'
1569
- })
1570
- }),
1571
- geometry: feature => getVertexs(feature)
1572
- }), new Style({
1573
- stroke: new Stroke({
1574
- color: 'rgba(255, 255, 255, 0.7)',
1575
- width: 2
1576
- })
1577
- })];
1578
- } else {
1579
- return [new Style({
1580
- image: new Circle({
1581
- radius: 2,
1582
- fill: new Fill({
1583
- color: '#000000'
1584
- })
1585
- }),
1586
- geometry: feature => getVertexs(feature)
1587
- }), new Style({
1588
- stroke: new Stroke({
1589
- color: '#ff0000',
1590
- width: 4
1591
- }),
1592
- fill: new Fill({
1593
- color: 'rgba(255, 0, 0, 0.7)'
1594
- })
1595
- })];
1596
- }
1597
-
1598
- }
1599
- }
1600
- /**
1601
- *
1602
- * @param feature
1603
- * @private
1604
- */
1605
-
1606
-
1607
- _editModeOn(feature) {
1608
- this._editFeatureOriginal = feature.clone();
1609
- this._isEditModeOn = true; // To refresh the style
1610
-
1611
- this._editLayer.getSource().changed();
1612
-
1613
- this._removeOverlayHelper(feature);
1614
-
1615
- var controlDiv = document.createElement('div');
1616
- controlDiv.className = 'ol-wfst--changes-control';
1617
- var elements = document.createElement('div');
1618
- elements.className = 'ol-wfst--changes-control-el';
1619
- var elementId = document.createElement('div');
1620
- elementId.className = 'ol-wfst--changes-control-id';
1621
- elementId.innerHTML = "<b>".concat(this._i18n.labels.editMode, "</b> - <i>").concat(String(feature.getId()), "</i>");
1622
- var acceptButton = document.createElement('button');
1623
- acceptButton.type = 'button';
1624
- acceptButton.textContent = this._i18n.labels.apply;
1625
- acceptButton.className = 'btn btn-primary';
1626
-
1627
- acceptButton.onclick = () => {
1628
- this._showLoading();
1629
-
1630
- this.interactionSelectModify.getFeatures().remove(feature);
1631
- };
1632
-
1633
- var cancelButton = document.createElement('button');
1634
- cancelButton.type = 'button';
1635
- cancelButton.textContent = this._i18n.labels.cancel;
1636
- cancelButton.className = 'btn btn-secondary';
1637
-
1638
- cancelButton.onclick = () => {
1639
- feature.setGeometry(this._editFeatureOriginal.getGeometry());
1640
-
1641
- this._removeFeatureFromEditList(feature);
1642
-
1643
- this.interactionSelectModify.getFeatures().remove(feature);
1644
- };
1645
-
1646
- elements.append(elementId);
1647
- elements.append(cancelButton);
1648
- elements.append(acceptButton);
1649
- controlDiv.append(elements);
1650
- this._controlApplyDiscardChanges = new Control({
1651
- element: controlDiv
1652
- });
1653
- this.map.addControl(this._controlApplyDiscardChanges);
1654
- }
1655
- /**
1656
- * @private
1657
- */
1658
-
1659
-
1660
- _editModeOff() {
1661
- this._isEditModeOn = false;
1662
- this.map.removeControl(this._controlApplyDiscardChanges);
1663
- }
1664
- /**
1665
- * Remove a feature from the edit Layer and from the Geoserver
1666
- *
1667
- * @param feature
1668
- * @private
1669
- */
1670
-
1671
-
1672
- _deleteFeature(feature, confirm) {
1673
- var deleteEl = () => {
1674
- var features = Array.isArray(feature) ? feature : [feature];
1675
- features.forEach(feature => {
1676
- feature.set('_delete_', true, true);
1677
-
1678
- this._editLayer.getSource().removeFeature(feature);
1679
- });
1680
- this.interactionSelectModify.getFeatures().clear();
1681
- var layerName = feature.get('_layerName_');
1682
- var {
1683
- mode
1684
- } = this.options.layers.find(layer => layer.name === layerName);
1685
-
1686
- if (mode === 'wfs') {
1687
- this.interactionWfsSelect.getFeatures().remove(feature);
1688
- }
1689
- };
1690
-
1691
- if (confirm) {
1692
- var confirmModal = Modal.confirm(this._i18n.labels.confirmDelete, {
1693
- animateInClass: 'in'
1694
- });
1695
- confirmModal.show().once('dismiss', function (modal, ev, button) {
1696
- if (button && button.value) {
1697
- deleteEl();
1698
- }
1699
- });
1700
- } else {
1701
- deleteEl();
1702
- }
1703
- }
1704
- /**
1705
- * Add a feature to the Edit Layer to allow editing, and creates an Overlay Helper to show options
1706
- *
1707
- * @param feature
1708
- * @param coordinate
1709
- * @param layerName
1710
- * @private
1711
- */
1712
-
1713
-
1714
- _addFeatureToEdit(feature) {
1715
- var coordinate = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
1716
- var layerName = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
1717
-
1718
- var prepareOverlay = () => {
1719
- var svgFields = "<img src=\"".concat(img$1, "\"/>");
1720
- var editFieldsEl = document.createElement('div');
1721
- editFieldsEl.className = 'ol-wfst--edit-button-cnt';
1722
- editFieldsEl.innerHTML = "<button class=\"ol-wfst--edit-button\" type=\"button\" title=\"".concat(this._i18n.labels.editFields, "\">").concat(svgFields, "</button>");
1723
-
1724
- editFieldsEl.onclick = () => {
1725
- this._initEditFieldsModal(feature);
1726
- };
1727
-
1728
- var buttons = document.createElement('div');
1729
- buttons.append(editFieldsEl);
1730
- var svgGeom = "<img src=\"".concat(img$2, "\"/>");
1731
- var editGeomEl = document.createElement('div');
1732
- editGeomEl.className = 'ol-wfst--edit-button-cnt';
1733
- editGeomEl.innerHTML = "<button class=\"ol-wfst--edit-button\" type=\"button\" title=\"".concat(this._i18n.labels.editGeom, "\">").concat(svgGeom, "</button>");
1734
-
1735
- editGeomEl.onclick = () => {
1736
- this._editModeOn(feature);
1737
- };
1738
-
1739
- buttons.append(editGeomEl);
1740
- var position = coordinate || getCenter(feature.getGeometry().getExtent());
1741
- var buttonsOverlay = new Overlay({
1742
- id: feature.getId(),
1743
- position: position,
1744
- positioning: OverlayPositioning.CENTER_CENTER,
1745
- element: buttons,
1746
- offset: [0, -40],
1747
- stopEvent: true
1748
- });
1749
- this.map.addOverlay(buttonsOverlay);
1750
- };
1751
-
1752
- if (layerName) {
1753
- // Guardamos el nombre de la capa de donde sale la feature
1754
- feature.set('_layerName_', layerName);
1755
- }
1756
-
1757
- var props = feature ? feature.getProperties() : '';
1758
-
1759
- if (props) {
1760
- if (feature.getGeometry()) {
1761
- this._editLayer.getSource().addFeature(feature);
1762
-
1763
- this.interactionSelectModify.getFeatures().push(feature);
1764
- prepareOverlay();
1765
-
1766
- if (this._useLockFeature && this._hasLockFeature) {
1767
- this._lockFeature(feature.getId(), feature.get('_layerName_'));
1768
- }
1769
- }
1770
- }
1771
- }
1772
- /**
1773
- * Removes in the DOM the class of the tools
1774
- * @private
1775
- */
1776
-
1777
-
1778
- _resetStateButtons() {
1779
- var activeBtn = document.querySelector('.ol-wfst--tools-control-btn.wfst--active');
1780
-
1781
- if (activeBtn) {
1782
- activeBtn.classList.remove('wfst--active');
1783
- }
1784
- }
1785
- /**
1786
- * Confirm modal before transact to the GeoServer the features in the file
1787
- *
1788
- * @param content
1789
- * @param featureToInsert
1790
- * @private
1791
- */
1792
-
1793
-
1794
- _initUploadFileModal(content, featuresToInsert) {
1795
- var footer = "\n <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">\n ".concat(this._i18n.labels.cancel, "\n </button>\n <button type=\"button\" class=\"btn btn-primary\" data-action=\"save\" data-dismiss=\"modal\">\n ").concat(this._i18n.labels.upload, "\n </button>\n ");
1796
- var modal = new Modal({
1797
- header: true,
1798
- headerClose: false,
1799
- title: this._i18n.labels.uploadFeatures + ' ' + this._layerToInsertElements,
1800
- content: content,
1801
- backdrop: 'static',
1802
- footer: footer,
1803
- animateInClass: 'in'
1804
- }).show();
1805
- modal.on('dismiss', (modal, event) => {
1806
- // On saving changes
1807
- if (event.target.dataset.action === 'save') {
1808
- this._transactWFS('insert', featuresToInsert, this._layerToInsertElements);
1809
- } else {
1810
- // On cancel button
1811
- this._editLayer.getSource().clear();
1812
- }
1813
- });
1814
- }
1815
- /**
1816
- * Parse and check geometry of uploaded files
1817
- *
1818
- * @param evt
1819
- * @private
1820
- */
1821
-
1822
-
1823
- _processUploadFile(evt) {
1824
- return __awaiter(this, void 0, void 0, function* () {
1825
- /**
1826
- * Read data file
1827
- * @param file
1828
- * @private
1829
- */
1830
- var fileReader = file => {
1831
- return new Promise((resolve, reject) => {
1832
- var reader = new FileReader();
1833
- reader.addEventListener('load', e => __awaiter(this, void 0, void 0, function* () {
1834
- var fileData = e.target.result;
1835
- resolve(fileData);
1836
- }));
1837
- reader.addEventListener('error', err => {
1838
- console.error('Error' + err);
1839
- reject();
1840
- });
1841
- reader.readAsText(file);
1842
- });
1843
- };
1844
- /**
1845
- * Attemp to change the geometry feature to the layer
1846
- * @param feature
1847
- * @private
1848
- */
1849
-
1850
-
1851
- var fixGeometry = feature => {
1852
- // Geometry of the layer
1853
- var geomTypeLayer = this._geoServerData[this._layerToInsertElements].geomType;
1854
- var geomTypeFeature = feature.getGeometry().getType();
1855
- var geom;
1856
-
1857
- switch (geomTypeFeature) {
1858
- case 'Point':
1859
- {
1860
- if (geomTypeLayer === 'MultiPoint') {
1861
- var coords = feature.getGeometry().getCoordinates();
1862
- geom = new MultiPoint([coords]);
1863
- }
1864
-
1865
- break;
1866
- }
1867
-
1868
- case 'LineString':
1869
- if (geomTypeLayer === 'MultiLineString') {
1870
- var _coords = feature.getGeometry().getCoordinates();
1871
-
1872
- geom = new MultiLineString([_coords]);
1873
- }
1874
-
1875
- break;
1876
-
1877
- case 'Polygon':
1878
- if (geomTypeLayer === 'MultiPolygon') {
1879
- var _coords2 = feature.getGeometry().getCoordinates();
1880
-
1881
- geom = new MultiPolygon([_coords2]);
1882
- }
1883
-
1884
- break;
1885
-
1886
- default:
1887
- geom = null;
1888
- }
1889
-
1890
- if (!geom) {
1891
- return null;
1892
- }
1893
-
1894
- feature.setGeometry(geom);
1895
- return feature;
1896
- };
1897
- /**
1898
- * Check if the feature has the same geometry as the target layer
1899
- * @param feature
1900
- * @private
1901
- */
1902
-
1903
-
1904
- var checkGeometry = feature => {
1905
- // Geometry of the layer
1906
- var geomTypeLayer = this._geoServerData[this._layerToInsertElements].geomType;
1907
- var geomTypeFeature = feature.getGeometry().getType(); // This geom accepts every type of geometry
1908
-
1909
- if (geomTypeLayer === GeometryType.GEOMETRY_COLLECTION) {
1910
- return true;
1911
- }
1912
-
1913
- return geomTypeFeature === geomTypeLayer;
1914
- };
1915
-
1916
- var file = evt.target.files[0];
1917
- var features;
1918
-
1919
- if (!file) {
1920
- return;
1921
- }
1922
-
1923
- var extension = file.name.split('.').pop().toLowerCase();
1924
-
1925
- try {
1926
- // If the user uses a custom fucntion...
1927
- if (this.options.processUpload) {
1928
- features = this.options.processUpload(file);
1929
- } // If the user functions return features, we dont process anything more
1930
-
1931
-
1932
- if (!features) {
1933
- var string = yield fileReader(file);
1934
-
1935
- if (extension === 'geojson' || extension === 'json') {
1936
- features = this._formatGeoJSON.readFeatures(string, {
1937
- featureProjection: this.view.getProjection().getCode()
1938
- });
1939
- } else if (extension === 'kml') {
1940
- features = this._formatKml.readFeatures(string, {
1941
- featureProjection: this.view.getProjection().getCode()
1942
- });
1943
- } else {
1944
- this._showError(this._i18n.errors.badFormat);
1945
- }
1946
- }
1947
- } catch (err) {
1948
- this._showError(this._i18n.errors.badFile);
1949
- }
1950
-
1951
- var invalidFeaturesCount = 0;
1952
- var validFeaturesCount = 0;
1953
- var featuresToInsert = [];
1954
-
1955
- for (var feature of features) {
1956
- // If the geometry doesn't correspond to the layer, try to fixit.
1957
- // If we can't, don't use it
1958
- if (!checkGeometry(feature)) {
1959
- feature = fixGeometry(feature);
1960
- }
1961
-
1962
- if (feature) {
1963
- featuresToInsert.push(feature);
1964
- validFeaturesCount++;
1965
- } else {
1966
- invalidFeaturesCount++;
1967
- continue;
1968
- }
1969
- }
1970
-
1971
- if (!validFeaturesCount) {
1972
- this._showError(this._i18n.errors.noValidGeometry);
1973
- } else {
1974
- this._resetStateButtons();
1975
-
1976
- this.activateEditMode();
1977
- var content = "\n ".concat(this._i18n.labels.validFeatures, ": ").concat(validFeaturesCount, "<br>\n ").concat(invalidFeaturesCount ? "".concat(this._i18n.labels.invalidFeatures, ": ").concat(invalidFeaturesCount) : '', "\n ");
1978
-
1979
- this._initUploadFileModal(content, featuresToInsert);
1980
-
1981
- this._editLayer.getSource().addFeatures(featuresToInsert);
1982
-
1983
- this.view.fit(this._editLayer.getSource().getExtent(), {
1984
- size: this.map.getSize(),
1985
- maxZoom: 21,
1986
- padding: [100, 100, 100, 100]
1987
- });
1988
- } // Reset the input to allow another onChange trigger
1989
-
1990
-
1991
- evt.target.value = null;
1992
- });
1993
- }
1994
- /**
1995
- * Update geom Types availibles to select for this layer
1996
- *
1997
- * @param layerName
1998
- * @param geomDrawTypeSelected
1999
- */
2000
-
2001
-
2002
- _changeStateSelect(layerName) {
2003
- var geomDrawTypeSelected = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
2004
-
623
+ */
624
+ _initMapElements(showControl, active) {
625
+ return __awaiter(this, void 0, void 0, function* () {
626
+ // VectorLayer to store features on editing and inserting
627
+ this._createEditLayer();
628
+ this._addInteractions();
629
+ this._addHandlers();
630
+ if (showControl) {
631
+ this._addMapControl();
632
+ }
633
+ // By default, init in edit mode
634
+ this.activateEditMode(active);
635
+ });
636
+ }
637
+ /**
638
+ * @private
639
+ */
640
+ _addInteractions() {
641
+ /**
642
+ * Select the wfs feature already downloaded
643
+ * @private
644
+ */
645
+ const prepareWfsInteraction = () => {
646
+ this._collectionModify = new Collection();
647
+ // Interaction to select wfs layer elements
648
+ this.interactionWfsSelect = new Select({
649
+ hitTolerance: 10,
650
+ style: (feature) => this._styleFunction(feature),
651
+ toggleCondition: never,
652
+ filter: (feature, layer) => {
653
+ return (!this._isEditModeOn &&
654
+ layer &&
655
+ layer.get('type') === '_wfs_');
656
+ }
657
+ });
658
+ this._map.addInteraction(this.interactionWfsSelect);
659
+ this.interactionWfsSelect.on('select', ({ selected, deselected, mapBrowserEvent }) => {
660
+ const coordinate = mapBrowserEvent.coordinate;
661
+ if (selected.length) {
662
+ selected.forEach((feature) => {
663
+ if (!this._editedFeatures.has(String(feature.getId()))) {
664
+ // Remove the feature from the original layer
665
+ const layer = this.interactionWfsSelect.getLayer(feature);
666
+ layer.getSource().removeFeature(feature);
667
+ this._addFeatureToEdit(feature, coordinate);
668
+ }
669
+ });
670
+ }
671
+ if (deselected.length) {
672
+ if (!this._isEditModeOn) {
673
+ deselected.forEach((feature) => {
674
+ // Trigger deselect
675
+ // This is necessary for those times where two features overlap.
676
+ this._collectionModify.remove(feature);
677
+ });
678
+ }
679
+ }
680
+ });
681
+ };
682
+ /**
683
+ * Call the geoserver to get the clicked feature
684
+ * @private
685
+ */
686
+ const prepareWmsInteraction = () => {
687
+ // Interaction to allow select features in the edit layer
688
+ this.interactionSelectModify = new Select({
689
+ style: (feature) => this._styleFunction(feature),
690
+ layers: [this._editLayer],
691
+ toggleCondition: never,
692
+ removeCondition: () => (this._isEditModeOn ? true : false) // Prevent deselect on clicking outside the feature
693
+ });
694
+ this._map.addInteraction(this.interactionSelectModify);
695
+ this._collectionModify = this.interactionSelectModify.getFeatures();
696
+ const getFeatures = (evt) => __awaiter(this, void 0, void 0, function* () {
697
+ for (const layerName in this._mapLayers) {
698
+ const layer = this._mapLayers[layerName];
699
+ // If layer is hidden or is not a wms, skip
700
+ if (!layer.getVisible() ||
701
+ !(layer.get('type') === '_wms_')) {
702
+ continue;
703
+ }
704
+ const coordinate = evt.coordinate;
705
+ // Si la vista es lejana, disminumos el buffer
706
+ // Si es cercana, lo aumentamos, por ejemplo, para podeer clickear los vectores
707
+ // y mejorar la sensibilidad en IOS
708
+ const buffer = this._view.getZoom() > 10 ? 10 : 5;
709
+ const source = layer.getSource();
710
+ // Fallback to support a bad name
711
+ // https://openlayers.org/en/v5.3.0/apidoc/module-ol_source_ImageWMS-ImageWMS.html#getGetFeatureInfoUrl
712
+ const fallbackOl5 = 'getFeatureInfoUrl' in source
713
+ ? 'getFeatureInfoUrl'
714
+ : 'getGetFeatureInfoUrl';
715
+ const url = source[fallbackOl5](coordinate, this._view.getResolution(), this._view.getProjection().getCode(), {
716
+ INFO_FORMAT: 'application/json',
717
+ BUFFER: buffer,
718
+ FEATURE_COUNT: 1,
719
+ EXCEPTIONS: 'application/json'
720
+ });
721
+ try {
722
+ const response = yield fetch(url, {
723
+ headers: this._options.headers
724
+ });
725
+ if (!response.ok) {
726
+ throw new Error(this._i18n.errors.getFeatures +
727
+ ' ' +
728
+ response.status);
729
+ }
730
+ const data = yield response.json();
731
+ const features = this._formatGeoJSON.readFeatures(data);
732
+ if (!features.length) {
733
+ continue;
734
+ }
735
+ features.forEach((feature) => this._addFeatureToEdit(feature, coordinate, layerName));
736
+ }
737
+ catch (err) {
738
+ this._showError(err.message, err);
739
+ }
740
+ }
741
+ });
742
+ this._keyClickWms = this._map.on(this._options.evtType, (evt) => __awaiter(this, void 0, void 0, function* () {
743
+ if (this._map.hasFeatureAtPixel(evt.pixel)) {
744
+ return;
745
+ }
746
+ if (!this._isVisible) {
747
+ return;
748
+ }
749
+ // Only get other features if editmode is disabled
750
+ if (!this._isEditModeOn) {
751
+ yield getFeatures(evt);
752
+ }
753
+ }));
754
+ };
755
+ if (this._options.layers.find((layer) => layer.mode === 'wfs')) {
756
+ prepareWfsInteraction();
757
+ }
758
+ if (this._options.layers.find((layer) => layer.mode === 'wms')) {
759
+ prepareWmsInteraction();
760
+ }
761
+ this.interactionModify = new Modify({
762
+ style: () => {
763
+ if (this._isEditModeOn) {
764
+ return new Style({
765
+ image: new Circle({
766
+ radius: 6,
767
+ fill: new Fill({
768
+ color: '#ff0000'
769
+ }),
770
+ stroke: new Stroke({
771
+ width: 2,
772
+ color: 'rgba(5, 5, 5, 0.9)'
773
+ })
774
+ })
775
+ });
776
+ }
777
+ else {
778
+ return;
779
+ }
780
+ },
781
+ features: this._collectionModify,
782
+ condition: (evt) => {
783
+ return primaryAction(evt) && this._isEditModeOn;
784
+ }
785
+ });
786
+ this._map.addInteraction(this.interactionModify);
787
+ this.interactionSnap = new Snap({
788
+ source: this._editLayer.getSource()
789
+ });
790
+ this._map.addInteraction(this.interactionSnap);
791
+ }
792
+ /**
793
+ * Layer to store temporary the elements to be edited
794
+ * @private
795
+ */
796
+ _createEditLayer() {
797
+ this._editLayer = new Vector({
798
+ source: new Vector$1(),
799
+ zIndex: 100
800
+ });
801
+ this._map.addLayer(this._editLayer);
802
+ }
803
+ /**
804
+ * Add map handlers
805
+ * @private
806
+ */
807
+ _addHandlers() {
808
+ /**
809
+ * @private
810
+ */
811
+ const keyboardEvents = () => {
812
+ document.addEventListener('keydown', ({ key }) => {
813
+ const inputFocus = document.querySelector('input:focus');
814
+ if (inputFocus) {
815
+ return;
816
+ }
817
+ if (key === 'Delete') {
818
+ const selectedFeatures = this._collectionModify;
819
+ if (selectedFeatures) {
820
+ selectedFeatures.forEach((feature) => {
821
+ this._deleteFeature(feature, true);
822
+ });
823
+ }
824
+ }
825
+ });
826
+ };
827
+ // When a feature is modified, add this to a list.
828
+ // This prevent events fired on select and deselect features that has no changes and should
829
+ // not be updated in the geoserver
830
+ this.interactionModify.on('modifyend', (evt) => {
831
+ const feature = evt.features.item(0);
832
+ this._addFeatureToEditedList(feature);
833
+ super.dispatchEvent(evt);
834
+ });
835
+ this.interactionModify.on('modifystart', (evt) => {
836
+ super.dispatchEvent(evt);
837
+ });
838
+ this._onDeselectFeatureEvent();
839
+ this._onRemoveFeatureEvent();
840
+ /**
841
+ * @private
842
+ */
843
+ const handleZoomEnd = () => {
844
+ if (this._currentZoom > this._options.minZoom) {
845
+ // Show the layers
846
+ if (!this._isVisible) {
847
+ this._isVisible = true;
848
+ }
849
+ }
850
+ else {
851
+ // Hide the layer
852
+ if (this._isVisible) {
853
+ this._isVisible = false;
854
+ }
855
+ }
856
+ };
857
+ this._map.on('moveend', () => {
858
+ this._currentZoom = this._view.getZoom();
859
+ if (this._currentZoom !== this._lastZoom) {
860
+ handleZoomEnd();
861
+ }
862
+ this._lastZoom = this._currentZoom;
863
+ });
864
+ keyboardEvents();
865
+ }
866
+ /**
867
+ * Add the widget on the map to allow change the tools and select active layers
868
+ * @private
869
+ */
870
+ _addMapControl() {
871
+ /**
872
+ * @private
873
+ * @returns
874
+ */
875
+ const createLayersControl = () => {
876
+ /**
877
+ *
878
+ * @param layerParams
879
+ * @private
880
+ * @returns
881
+ */
882
+ const createLayerElements = (layerParams) => {
883
+ const layerName = layerParams.name;
884
+ const layerLabel = `<span title="${this._geoServerData[layerName].geomType}">${layerParams.label || layerName}</span>`;
885
+ const visible = 'visible' in layerParams ? layerParams.visible : true;
886
+ return `
887
+ <div class="wfst--layer-control
888
+ ${visible ? 'ol-wfst--visible-on' : ''}
889
+ ${layerName === this._layerToInsertElements
890
+ ? 'ol-wfst--selected-on'
891
+ : ''}
892
+ " data-layer="${layerName}">
893
+ <div class="ol-wfst--tools-control-visible">
894
+ <span class="ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on" title="${this._i18n.labels.toggleVisibility}">
895
+ <img src="${img$4}"/>
896
+ </span>
897
+ <span class="ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off" title="${this._i18n.labels.toggleVisibility}">
898
+ <img src="${img$5}"/>
899
+ </span>
900
+ </div>
901
+ <label for="wfst--${layerName}">
902
+ <input value="${layerName}" id="wfst--${layerName}" type="radio" class="ol-wfst--tools-control-input" name="wfst--select-layer" ${layerName === this._layerToInsertElements
903
+ ? 'checked="checked"'
904
+ : ''}>
905
+ ${layerLabel}
906
+ </label>
907
+ </div>`;
908
+ };
909
+ let htmlLayers = '';
910
+ Object.keys(this._mapLayers).map((key) => (htmlLayers += createLayerElements(this._options.layers.find((el) => el.name === key))));
911
+ const selectLayers = document.createElement('div');
912
+ selectLayers.className = 'wfst--tools-control--select-layers';
913
+ selectLayers.innerHTML = htmlLayers;
914
+ // Layer Selector
915
+ const radioInputs = selectLayers.querySelectorAll('input');
916
+ radioInputs.forEach((radioInput) => {
917
+ const parentDiv = radioInput.closest('.wfst--layer-control');
918
+ radioInput.onchange = () => {
919
+ // Deselect DOM previous layer
920
+ const selected = selectLayers.querySelector('.ol-wfst--selected-on');
921
+ if (selected)
922
+ selected.classList.remove('ol-wfst--selected-on');
923
+ // Select this layer
924
+ parentDiv.classList.add('ol-wfst--selected-on');
925
+ this._layerToInsertElements = radioInput.value;
926
+ this._changeStateSelect(this._layerToInsertElements);
927
+ };
928
+ });
929
+ // Visibility toggler
930
+ const visibilityBtn = selectLayers.querySelectorAll('.ol-wfst--tools-control-visible-btn');
931
+ visibilityBtn.forEach((btn) => {
932
+ const parentDiv = btn.closest('.wfst--layer-control');
933
+ const layerName = parentDiv.dataset['layer'];
934
+ btn.onclick = () => {
935
+ parentDiv.classList.toggle('ol-wfst--visible-on');
936
+ const layer = this._mapLayers[layerName];
937
+ if (parentDiv.classList.contains('ol-wfst--visible-on')) {
938
+ layer.setVisible(true);
939
+ }
940
+ else {
941
+ layer.setVisible(false);
942
+ }
943
+ };
944
+ });
945
+ return selectLayers;
946
+ };
947
+ /**
948
+ * @private
949
+ * @returns
950
+ */
951
+ const createHeadControl = () => {
952
+ /**
953
+ * @private
954
+ */
955
+ const createUploadElements = () => {
956
+ const container = document.createElement('div');
957
+ // Upload button Tool
958
+ const uploadButton = document.createElement('label');
959
+ uploadButton.className =
960
+ 'ol-wfst--tools-control-btn ol-wfst--tools-control-btn-upload';
961
+ uploadButton.htmlFor = 'ol-wfst--upload';
962
+ uploadButton.innerHTML = `<img src="${img$3}"/> `;
963
+ uploadButton.title = this._i18n.labels.uploadToLayer;
964
+ // Hidden Input form
965
+ const uploadInput = document.createElement('input');
966
+ uploadInput.id = 'ol-wfst--upload';
967
+ uploadInput.type = 'file';
968
+ uploadInput.accept = this._options.uploadFormats;
969
+ uploadInput.onchange = (evt) => this._processUploadFile(evt);
970
+ container.append(uploadInput);
971
+ container.append(uploadButton);
972
+ return container;
973
+ };
974
+ /**
975
+ * @private
976
+ * @returns
977
+ */
978
+ const createDrawContainer = () => {
979
+ const drawContainer = document.createElement('div');
980
+ drawContainer.className = 'ol-wfst--tools-control-draw-cnt';
981
+ // Draw Tool
982
+ const drawButton = document.createElement('button');
983
+ drawButton.className =
984
+ 'ol-wfst--tools-control-btn ol-wfst--tools-control-btn-draw';
985
+ drawButton.type = 'button';
986
+ drawButton.innerHTML = `<img src="${img}"/>`;
987
+ drawButton.title = this._i18n.labels.addElement;
988
+ drawButton.onclick = () => {
989
+ if (this._isDrawModeOn) {
990
+ this._resetStateButtons();
991
+ this.activateEditMode();
992
+ }
993
+ else {
994
+ this.activateDrawMode(this._layerToInsertElements);
995
+ }
996
+ };
997
+ // Select geom type
998
+ const select = document.createElement('select');
999
+ select.title = this._i18n.labels.selectDrawType;
1000
+ select.className = 'wfst--tools-control--select-draw';
1001
+ select.onchange = () => {
1002
+ const selectedValue = select.value;
1003
+ this._changeStateSelect(this._layerToInsertElements, selectedValue);
1004
+ if (this._isDrawModeOn) {
1005
+ this.activateDrawMode(this._layerToInsertElements);
1006
+ }
1007
+ };
1008
+ const types = [
1009
+ GeometryType.Point,
1010
+ GeometryType.MultiPoint,
1011
+ GeometryType.LineString,
1012
+ GeometryType.MultiLineString,
1013
+ GeometryType.Polygon,
1014
+ GeometryType.MultiPolygon,
1015
+ GeometryType.Circle
1016
+ ];
1017
+ for (const type of types) {
1018
+ const option = document.createElement('option');
1019
+ option.value = type;
1020
+ option.text = type;
1021
+ option.selected =
1022
+ this._geoServerData[this._layerToInsertElements]
1023
+ .geomType === type || false;
1024
+ select.appendChild(option);
1025
+ }
1026
+ drawContainer.append(drawButton);
1027
+ drawContainer.append(select);
1028
+ this._selectDraw = select;
1029
+ return drawContainer;
1030
+ };
1031
+ const subControl = document.createElement('div');
1032
+ subControl.className = 'wfst--tools-control--head';
1033
+ // Upload section
1034
+ if (this._options.showUpload) {
1035
+ const uploadSection = createUploadElements();
1036
+ subControl.append(uploadSection);
1037
+ }
1038
+ const drawContainer = createDrawContainer();
1039
+ subControl.append(drawContainer);
1040
+ return subControl;
1041
+ };
1042
+ const headControl = createHeadControl();
1043
+ this._controlWidgetToolsDiv.append(headControl);
1044
+ const htmlLayers = createLayersControl();
1045
+ this._controlWidgetToolsDiv.append(htmlLayers);
1046
+ }
1047
+ /**
1048
+ * Show Loading
1049
+ * @private
1050
+ */
1051
+ _showLoading() {
1052
+ if (!this._modalLoading) {
1053
+ this._modalLoading = document.createElement('div');
1054
+ this._modalLoading.className = 'ol-wfst--tools-control--loading';
1055
+ this._modalLoading.innerHTML = this._i18n.labels.loading;
1056
+ this._controlWidgetToolsDiv.append(this._modalLoading);
1057
+ }
1058
+ this._modalLoading.classList.add('ol-wfst--tools-control--loading-show');
1059
+ }
1060
+ /**
1061
+ * Hide loading
1062
+ * @private
1063
+ */
1064
+ _hideLoading() {
1065
+ this._modalLoading.classList.remove('ol-wfst--tools-control--loading-show');
1066
+ }
1067
+ /**
1068
+ * Lock a feature in the geoserver before edit
1069
+ *
1070
+ * @param featureId
1071
+ * @param layerName
1072
+ * @param retry
1073
+ * @private
1074
+ */
1075
+ _lockFeature(featureId, layerName, retry = 0) {
1076
+ return __awaiter(this, void 0, void 0, function* () {
1077
+ const params = new URLSearchParams({
1078
+ service: 'wfs',
1079
+ version: this._options.geoServerAdvanced.lockFeatureVersion,
1080
+ request: 'LockFeature',
1081
+ expiry: String(5),
1082
+ LockId: 'GeoServer',
1083
+ typeName: layerName,
1084
+ releaseAction: 'SOME',
1085
+ exceptions: 'application/json',
1086
+ featureid: `${featureId}`
1087
+ });
1088
+ const url_fetch = this._options.geoServerUrl + '?' + params.toString();
1089
+ try {
1090
+ const response = yield fetch(url_fetch, {
1091
+ headers: this._options.headers
1092
+ });
1093
+ if (!response.ok) {
1094
+ throw new Error(this._i18n.errors.lockFeature);
1095
+ }
1096
+ const data = yield response.text();
1097
+ try {
1098
+ // First, check if is a JSON (with errors)
1099
+ const dataParsed = JSON.parse(data);
1100
+ if ('exceptions' in dataParsed) {
1101
+ const exceptions = dataParsed.exceptions;
1102
+ if (exceptions[0].code === 'CannotLockAllFeatures') {
1103
+ // Maybe the Feature is already blocked, ant thats trigger error, so, we try one locking more time again
1104
+ if (!retry) {
1105
+ this._lockFeature(featureId, layerName, 1);
1106
+ }
1107
+ else {
1108
+ this._showError(this._i18n.errors.lockFeature, exceptions);
1109
+ }
1110
+ }
1111
+ else {
1112
+ this._showError(exceptions[0].text, exceptions);
1113
+ }
1114
+ }
1115
+ }
1116
+ catch (err) {
1117
+ /*
1118
+
1119
+ let dataDoc = (new window.DOMParser()).parseFromString(data, 'text/xml');
1120
+
1121
+ let lockId = dataDoc.getElementsByTagName('wfs:LockId');
1122
+
1123
+ let featuresLocked: HTMLCollectionOf<Element> = dataDoc.getElementsByTagName('ogc:FeatureId');
1124
+
1125
+ for (let featureLocked of featuresLocked as any) {
1126
+
1127
+ console.log(featureLocked.getAttribute('fid'));
1128
+
1129
+ }
1130
+
1131
+ */
1132
+ }
1133
+ return data;
1134
+ }
1135
+ catch (err) {
1136
+ this._showError(err.message, err);
1137
+ }
1138
+ });
1139
+ }
2005
1140
  /**
2006
- * Set the geometry type in the select according to the geometry of
2007
- * the layer in the geoserver and disable what does not correspond.
1141
+ * Show modal with errors
2008
1142
  *
2009
- * @param value
2010
- * @param options
1143
+ * @param msg
2011
1144
  * @private
2012
- */
2013
- var setSelectState = (value, options) => {
2014
- Array.from(this._selectDraw.options).forEach(option => {
2015
- option.selected = option.value === value ? true : false;
2016
- option.disabled = options === 'all' ? false : options.includes(option.value) ? false : true;
2017
- option.title = option.disabled ? this._i18n.labels.geomTypeNotSupported : '';
2018
- });
2019
- };
2020
-
2021
- var drawType;
2022
-
2023
- if (this._selectDraw) {
2024
- var geomLayer = this._geoServerData[layerName].geomType;
2025
-
2026
- if (geomDrawTypeSelected) {
2027
- drawType = this._selectDraw.value;
2028
- } else {
2029
- if (geomLayer === GeometryType.GEOMETRY_COLLECTION) {
2030
- drawType = GeometryType.LINE_STRING; // Default drawing type for GeometryCollection
2031
-
2032
- setSelectState(drawType, 'all');
2033
- } else if (geomLayer === GeometryType.LINEAR_RING) {
2034
- drawType = GeometryType.LINE_STRING; // Default drawing type for GeometryCollection
2035
-
2036
- setSelectState(drawType, [GeometryType.CIRCLE, GeometryType.LINEAR_RING, GeometryType.POLYGON]);
2037
- this._selectDraw.value = drawType;
2038
- } else {
2039
- drawType = geomLayer;
2040
- setSelectState(drawType, [geomLayer]);
2041
- }
2042
- }
2043
- }
2044
-
2045
- return drawType;
2046
- }
2047
- /**
2048
- * Activate/deactivate the draw mode
2049
- *
2050
- * @param layerName
2051
- * @public
2052
- */
2053
-
2054
-
2055
- activateDrawMode(layerName) {
1145
+ */
1146
+ _showError(msg, originalError = null) {
1147
+ Modal.alert('Error: ' + msg, Object.assign({}, this._options.modal)).show();
1148
+ if (originalError)
1149
+ console.error(originalError);
1150
+ }
2056
1151
  /**
1152
+ * Make the WFS Transactions
2057
1153
  *
1154
+ * @param action
1155
+ * @param features
2058
1156
  * @param layerName
2059
1157
  * @private
2060
- */
2061
- var addDrawInteraction = layerName => {
2062
- this.activateEditMode(false); // If already exists, remove
2063
-
2064
- if (this.interactionDraw) {
2065
- this.map.removeInteraction(this.interactionDraw);
2066
- }
2067
-
2068
- var geomDrawType = this._selectDraw.value;
2069
- this.interactionDraw = new Draw({
2070
- source: this._editLayer.getSource(),
2071
- type: geomDrawType,
2072
- style: feature => this._styleFunction(feature)
2073
- });
2074
- this.map.addInteraction(this.interactionDraw);
2075
-
2076
- var drawHandler = () => {
2077
- this.interactionDraw.on('drawend', evt => {
2078
- var feature = evt.feature;
2079
-
2080
- this._transactWFS('insert', feature, layerName);
2081
- });
2082
- };
2083
-
2084
- drawHandler();
2085
- };
2086
-
2087
- if (!this.interactionDraw && !layerName) {
2088
- return;
2089
- }
2090
-
2091
- if (layerName) {
2092
- // If layer is set to invisible, show warning
2093
- if (!this._mapLayers[layerName].getVisible()) {
2094
- return;
2095
- }
2096
-
2097
- var btn = document.querySelector('.ol-wfst--tools-control-btn-draw');
2098
-
2099
- if (btn) {
2100
- btn.classList.add('wfst--active');
2101
- }
2102
-
2103
- this.viewport.classList.add('draw-mode');
2104
- addDrawInteraction(String(layerName));
2105
- } else {
2106
- this.map.removeInteraction(this.interactionDraw);
2107
- this.viewport.classList.remove('draw-mode');
2108
- }
2109
-
2110
- this._isDrawModeOn = layerName ? true : false;
2111
- }
2112
- /**
2113
- * Activate/desactivate the edit mode
2114
- *
2115
- * @param bool
2116
- * @public
2117
- */
2118
-
2119
-
2120
- activateEditMode() {
2121
- var bool = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
2122
-
2123
- if (bool) {
2124
- var btn = document.querySelector('.ol-wfst--tools-control-btn-edit');
2125
-
2126
- if (btn) {
2127
- btn.classList.add('wfst--active');
2128
- }
2129
-
2130
- this.activateDrawMode(false);
2131
- } else {
2132
- // Deselct features
2133
- this.interactionSelectModify.getFeatures().clear();
2134
- }
2135
-
2136
- this.interactionSelectModify.setActive(bool);
2137
- this.interactionModify.setActive(bool);
2138
- if (this.interactionWfsSelect) this.interactionWfsSelect.setActive(bool);
2139
- }
2140
- /**
2141
- * Add features directly to the geoserver, in a custom layer
2142
- * without checking geometry or showing modal to confirm.
2143
- *
2144
- * @param layerName
2145
- * @param features
2146
- * @public
2147
- */
2148
-
2149
-
2150
- insertFeaturesTo(layerName, features) {
2151
- this._transactWFS('insert', features, layerName);
2152
- }
2153
- /**
2154
- * Shows a fields form in a modal window to allow changes in the properties of the feature.
2155
- *
2156
- * @param feature
2157
- * @private
2158
- */
2159
-
2160
-
2161
- _initEditFieldsModal(feature) {
2162
- this._editFeature = feature;
2163
- var properties = feature.getProperties();
2164
- var layer = feature.get('_layerName_'); // Data schema from the geoserver
2165
-
2166
- var dataSchema = this._geoServerData[layer].properties;
2167
- var content = '<form autocomplete="false">';
2168
- Object.keys(properties).forEach(key => {
2169
- // If the feature field exists in the geoserver and is not added by openlayers
2170
- var field = dataSchema.find(data => data.name === key);
2171
-
2172
- if (field) {
2173
- var typeXsd = field.type;
2174
- var type;
2175
-
2176
- switch (typeXsd) {
2177
- case 'xsd:string':
2178
- type = 'text';
2179
- break;
2180
-
2181
- case 'xsd:number':
2182
- case 'xsd:int':
2183
- type = 'number';
2184
- break;
2185
-
2186
- case 'xsd:date-time':
2187
- type = 'datetime';
2188
- break;
2189
-
2190
- default:
2191
- type = 'text';
2192
- }
2193
-
2194
- if (type) {
2195
- content += "\n <div class=\"ol-wfst--input-field-container\">\n <label class=\"ol-wfst--input-field-label\" for=\"".concat(key, "\">").concat(key, "</label>\n <input placeholder=\"NULL\" class=\"ol-wfst--input-field-input\" type=\"").concat(type, "\" name=\"").concat(key, "\" value=\"").concat(properties[key] || '', "\">\n </div>");
2196
- }
2197
- }
2198
- });
2199
- content += '</form>';
2200
- var footer = "\n <button type=\"button\" class=\"btn btn-link btn-third\" data-action=\"delete\" data-dismiss=\"modal\">\n ".concat(this._i18n.labels.delete, "\n </button>\n <button type=\"button\" class=\"btn btn-secondary\" data-dismiss=\"modal\">\n ").concat(this._i18n.labels.cancel, "\n </button>\n <button type=\"button\" class=\"btn btn-primary\" data-action=\"save\" data-dismiss=\"modal\">\n ").concat(this._i18n.labels.save, "\n </button>\n ");
2201
- var modal = new Modal({
2202
- header: true,
2203
- headerClose: true,
2204
- title: "".concat(this._i18n.labels.editElement, " ").concat(this._editFeature.getId(), " "),
2205
- content: content,
2206
- footer: footer,
2207
- animateInClass: 'in'
2208
- }).show();
2209
- modal.on('dismiss', (modal, event) => {
2210
- // On saving changes
2211
- if (event.target.dataset.action === 'save') {
2212
- var inputs = modal.el.querySelectorAll('input');
2213
- inputs.forEach(el => {
2214
- var value = el.value;
2215
- var field = el.name;
2216
-
2217
- this._editFeature.set(field, value,
2218
- /*isSilent = */
2219
- true);
2220
- });
2221
-
2222
- this._editFeature.changed();
2223
-
2224
- this._addFeatureToEditedList(this._editFeature); // Force deselect to trigger handler
2225
-
2226
-
2227
- this.interactionSelectModify.getFeatures().remove(this._editFeature);
2228
- } else if (event.target.dataset.action === 'delete') {
2229
- this._deleteFeature(this._editFeature, true);
2230
- }
2231
- });
2232
- }
2233
- /**
2234
- * Remove the overlay helper atttached to a specify feature
2235
- * @param feature
2236
- * @private
2237
- */
2238
-
2239
-
2240
- _removeOverlayHelper(feature) {
2241
- var featureId = feature.getId();
2242
-
2243
- if (!featureId) {
2244
- return;
2245
- }
2246
-
2247
- var overlay = this.map.getOverlayById(featureId);
2248
-
2249
- if (!overlay) {
2250
- return;
2251
- }
2252
-
2253
- this.map.removeOverlay(overlay);
2254
- }
2255
-
2256
- }
1158
+ */
1159
+ _transactWFS(action, features, layerName) {
1160
+ return __awaiter(this, void 0, void 0, function* () {
1161
+ const transformCircleToPolygon = (feature, geom) => {
1162
+ const geomConverted = fromCircle(geom);
1163
+ feature.setGeometry(geomConverted);
1164
+ };
1165
+ const transformGeoemtryCollectionToGeometries = (feature, geom) => {
1166
+ let geomConverted = geom.getGeometries()[0];
1167
+ if (geomConverted instanceof Circle$1) {
1168
+ geomConverted = fromCircle(geomConverted);
1169
+ }
1170
+ feature.setGeometry(geomConverted);
1171
+ };
1172
+ features = Array.isArray(features) ? features : [features];
1173
+ const cloneFeature = (feature) => {
1174
+ this._removeFeatureFromEditList(feature);
1175
+ const featureProperties = feature.getProperties();
1176
+ delete featureProperties.boundedBy;
1177
+ delete featureProperties._layerName_;
1178
+ const clone = new Feature(featureProperties);
1179
+ clone.setId(feature.getId());
1180
+ return clone;
1181
+ };
1182
+ const refreshWmsLayer = (layer) => {
1183
+ const source = layer.getSource();
1184
+ // Refrescamos el wms
1185
+ source.refresh();
1186
+ // Force refresh the tiles
1187
+ const params = source.getParams();
1188
+ params.t = new Date().getMilliseconds();
1189
+ source.updateParams(params);
1190
+ };
1191
+ const refreshWfsLayer = (layer) => {
1192
+ const source = layer.getSource();
1193
+ // Refrescamos el wms
1194
+ source.refresh();
1195
+ };
1196
+ const clonedFeatures = [];
1197
+ for (const feature of features) {
1198
+ let clone = cloneFeature(feature);
1199
+ const cloneGeom = clone.getGeometry();
1200
+ // Ugly fix to support GeometryCollection on GML
1201
+ // See https://github.com/openlayers/openlayers/issues/4220
1202
+ if (cloneGeom instanceof GeometryCollection) {
1203
+ transformGeoemtryCollectionToGeometries(clone, cloneGeom);
1204
+ }
1205
+ else if (cloneGeom instanceof Circle$1) {
1206
+ // Geoserver has no Support to Circles
1207
+ transformCircleToPolygon(clone, cloneGeom);
1208
+ }
1209
+ if (action === 'insert') {
1210
+ // Filters
1211
+ if (this._options.beforeInsertFeature) {
1212
+ clone = this._options.beforeInsertFeature(clone);
1213
+ }
1214
+ }
1215
+ if (clone) {
1216
+ clonedFeatures.push(clone);
1217
+ }
1218
+ }
1219
+ if (!clonedFeatures.length) {
1220
+ return this._showError(this._i18n.errors.noValidGeometry);
1221
+ }
1222
+ switch (action) {
1223
+ case 'insert':
1224
+ this._insertFeatures = [
1225
+ ...this._insertFeatures,
1226
+ ...clonedFeatures
1227
+ ];
1228
+ break;
1229
+ case 'update':
1230
+ this._updateFeatures = [
1231
+ ...this._updateFeatures,
1232
+ ...clonedFeatures
1233
+ ];
1234
+ break;
1235
+ case 'delete':
1236
+ this._deleteFeatures = [
1237
+ ...this._deleteFeatures,
1238
+ ...clonedFeatures
1239
+ ];
1240
+ break;
1241
+ }
1242
+ this._countRequests++;
1243
+ const numberRequest = this._countRequests;
1244
+ setTimeout(() => __awaiter(this, void 0, void 0, function* () {
1245
+ try {
1246
+ // Prevent fire multiples times
1247
+ if (numberRequest !== this._countRequests) {
1248
+ return;
1249
+ }
1250
+ let srs = this._view.getProjection().getCode();
1251
+ // Force latitude/longitude order on transactions
1252
+ // EPSG:4326 is longitude/latitude (assumption) and is not managed correctly by GML3
1253
+ srs = srs === 'EPSG:4326' ? 'urn:x-ogc:def:crs:EPSG:4326' : srs;
1254
+ const options = {
1255
+ featureNS: this._geoServerData[layerName].namespace,
1256
+ featureType: layerName,
1257
+ srsName: srs,
1258
+ featurePrefix: null,
1259
+ nativeElements: null,
1260
+ version: this._options.geoServerAdvanced
1261
+ .wfsTransactionVersion
1262
+ };
1263
+ const transaction = this._formatWFS.writeTransaction(this._insertFeatures, this._updateFeatures, this._deleteFeatures, options);
1264
+ let payload = this._xs.serializeToString(transaction);
1265
+ const geomType = this._geoServerData[layerName].geomType;
1266
+ const geomField = this._geoServerData[layerName].geomField;
1267
+ // Ugly fix to support GeometryCollection on GML
1268
+ // See https://github.com/openlayers/openlayers/issues/4220
1269
+ if (geomType === GeometryType.GeometryCollection) {
1270
+ if (action === 'insert') {
1271
+ payload = payload.replace(/<geometry>/g, `<geometry><MultiGeometry xmlns="http://www.opengis.net/gml" srsName="${srs}"><geometryMember>`);
1272
+ payload = payload.replace(/<\/geometry>/g, `</geometryMember></MultiGeometry></geometry>`);
1273
+ }
1274
+ else if (action === 'update') {
1275
+ const gmemberIn = `<MultiGeometry xmlns="http://www.opengis.net/gml" srsName="${srs}"><geometryMember>`;
1276
+ const gmemberOut = `</geometryMember></MultiGeometry>`;
1277
+ payload = payload.replace(/(.*)(<Name>geometry<\/Name><Value>)(.*?)(<\/Value>)(.*)/g, `$1$2${gmemberIn}$3${gmemberOut}$4$5`);
1278
+ }
1279
+ }
1280
+ // Fixes geometry name, weird bug with GML:
1281
+ // The property for the geometry column is always named "geometry"
1282
+ if (action === 'insert') {
1283
+ payload = payload.replace(/<(\/?)\bgeometry\b>/g, `<$1${geomField}>`);
1284
+ }
1285
+ else {
1286
+ payload = payload.replace(/<Name>geometry<\/Name>/g, `<Name>${geomField}</Name>`);
1287
+ }
1288
+ // Add default LockId value
1289
+ if (this._hasLockFeature &&
1290
+ this._useLockFeature &&
1291
+ action !== 'insert') {
1292
+ payload = payload.replace(`</Transaction>`, `<LockId>GeoServer</LockId></Transaction>`);
1293
+ }
1294
+ const headers = Object.assign({ 'Content-Type': 'text/xml' }, this._options.headers);
1295
+ const response = yield fetch(this._options.geoServerUrl, {
1296
+ method: 'POST',
1297
+ body: payload,
1298
+ headers: headers
1299
+ });
1300
+ if (!response.ok) {
1301
+ throw new Error(this._i18n.errors.transaction + ' ' + response.status);
1302
+ }
1303
+ const parseResponse = this._formatWFS.readTransactionResponse(response);
1304
+ if (!Object.keys(parseResponse).length) {
1305
+ const responseStr = yield response.text();
1306
+ const findError = String(responseStr).match(/<ows:ExceptionText>([\s\S]*?)<\/ows:ExceptionText>/);
1307
+ if (findError) {
1308
+ this._showError(findError[1]);
1309
+ }
1310
+ }
1311
+ if (action !== 'delete') {
1312
+ for (const feature of features) {
1313
+ this._editLayer.getSource().removeFeature(feature);
1314
+ }
1315
+ }
1316
+ const { mode } = this._options.layers.find((layer) => layer.name === layerName);
1317
+ if (mode === 'wfs') {
1318
+ refreshWfsLayer(this._mapLayers[layerName]);
1319
+ }
1320
+ else if (mode === 'wms') {
1321
+ refreshWmsLayer(this._mapLayers[layerName]);
1322
+ }
1323
+ this._hideLoading();
1324
+ }
1325
+ catch (err) {
1326
+ this._showError(err.message, err);
1327
+ }
1328
+ this._insertFeatures = [];
1329
+ this._updateFeatures = [];
1330
+ this._deleteFeatures = [];
1331
+ this._countRequests = 0;
1332
+ }), 0);
1333
+ });
1334
+ }
1335
+ /**
1336
+ *
1337
+ * @param feature
1338
+ * @private
1339
+ */
1340
+ _addFeatureToEditedList(feature) {
1341
+ this._editedFeatures.add(String(feature.getId()));
1342
+ }
1343
+ /**
1344
+ *
1345
+ * @param feature
1346
+ * @private
1347
+ */
1348
+ _removeFeatureFromEditList(feature) {
1349
+ this._editedFeatures.delete(String(feature.getId()));
1350
+ }
1351
+ /**
1352
+ *
1353
+ * @param feature
1354
+ * @private
1355
+ */
1356
+ _isFeatureEdited(feature) {
1357
+ return this._editedFeatures.has(String(feature.getId()));
1358
+ }
1359
+ /**
1360
+ *
1361
+ * @param feature
1362
+ * @private
1363
+ */
1364
+ _deselectEditFeature(feature) {
1365
+ this._removeOverlayHelper(feature);
1366
+ }
1367
+ /**
1368
+ *
1369
+ * @param feature
1370
+ * @param layerName
1371
+ * @private
1372
+ */
1373
+ _restoreFeatureToLayer(feature, layerName) {
1374
+ layerName = layerName || feature.get('_layerName_');
1375
+ const layer = this._mapLayers[layerName];
1376
+ layer.getSource().addFeature(feature);
1377
+ }
1378
+ /**
1379
+ * @private
1380
+ * @param feature
1381
+ */
1382
+ _removeFeatureFromTmpLayer(feature) {
1383
+ // Remove element from the Layer
1384
+ this._editLayer.getSource().removeFeature(feature);
1385
+ }
1386
+ /**
1387
+ * Trigger on deselecting a feature from in the Edit layer
1388
+ *
1389
+ * @private
1390
+ */
1391
+ _onDeselectFeatureEvent() {
1392
+ const checkIfFeatureIsChanged = (feature) => {
1393
+ const layerName = feature.get('_layerName_');
1394
+ const { mode } = this._options.layers.find((layer) => layer.name === layerName);
1395
+ if (mode === 'wfs') {
1396
+ this.interactionWfsSelect.getFeatures().remove(feature);
1397
+ }
1398
+ if (this._isFeatureEdited(feature)) {
1399
+ this._transactWFS('update', feature, layerName);
1400
+ }
1401
+ else {
1402
+ // Si es wfs y el elemento no tuvo cambios, lo devolvemos a la layer original
1403
+ if (mode === 'wfs') {
1404
+ this._restoreFeatureToLayer(feature, layerName);
1405
+ }
1406
+ this._removeFeatureFromTmpLayer(feature);
1407
+ }
1408
+ };
1409
+ // This is fired when a feature is deselected and fires the transaction process
1410
+ this._keySelect = this._collectionModify.on('remove', (evt) => {
1411
+ const feature = evt.element;
1412
+ this._deselectEditFeature(feature);
1413
+ checkIfFeatureIsChanged(feature);
1414
+ this._editModeOff();
1415
+ });
1416
+ }
1417
+ /**
1418
+ * Trigger on removing a feature from the Edit layer
1419
+ *
1420
+ * @private
1421
+ */
1422
+ _onRemoveFeatureEvent() {
1423
+ // If a feature is removed from the edit layer
1424
+ this._keyRemove = this._editLayer
1425
+ .getSource()
1426
+ .on('removefeature', (evt) => {
1427
+ const feature = evt.feature;
1428
+ if (!feature.get('_delete_')) {
1429
+ return;
1430
+ }
1431
+ if (this._keySelect) {
1432
+ unByKey(this._keySelect);
1433
+ }
1434
+ const layerName = feature.get('_layerName_');
1435
+ this._transactWFS('delete', feature, layerName);
1436
+ this._deselectEditFeature(feature);
1437
+ this._editModeOff();
1438
+ if (this._keySelect) {
1439
+ setTimeout(() => {
1440
+ this._onDeselectFeatureEvent();
1441
+ }, 150);
1442
+ }
1443
+ });
1444
+ }
1445
+ /**
1446
+ * Master style that handles two modes on the Edit Layer:
1447
+ * - one is the basic, showing only the vertices
1448
+ * - and the other when modify is active, showing bigger vertices
1449
+ *
1450
+ * @param feature
1451
+ * @private
1452
+ */
1453
+ _styleFunction(feature) {
1454
+ const getVertexs = (feature) => {
1455
+ let geometry = feature.getGeometry();
1456
+ if (geometry instanceof GeometryCollection) {
1457
+ geometry = geometry.getGeometries()[0];
1458
+ }
1459
+ const coordinates = geometry.getCoordinates();
1460
+ let flatCoordinates = null;
1461
+ if (geometry instanceof Polygon ||
1462
+ geometry instanceof MultiLineString) {
1463
+ flatCoordinates = coordinates.flat(1);
1464
+ }
1465
+ else if (geometry instanceof MultiPolygon) {
1466
+ flatCoordinates = coordinates.flat(2);
1467
+ }
1468
+ else {
1469
+ flatCoordinates = coordinates;
1470
+ }
1471
+ if (!flatCoordinates || !flatCoordinates.length) {
1472
+ return;
1473
+ }
1474
+ return new MultiPoint(flatCoordinates);
1475
+ };
1476
+ let geometry = feature.getGeometry();
1477
+ let type = geometry.getType();
1478
+ if (geometry instanceof GeometryCollection) {
1479
+ geometry = geometry.getGeometries()[0];
1480
+ type = geometry.getType();
1481
+ }
1482
+ switch (type) {
1483
+ case GeometryType.Point:
1484
+ case GeometryType.MultiPoint:
1485
+ if (this._isEditModeOn) {
1486
+ return [
1487
+ new Style({
1488
+ image: new Circle({
1489
+ radius: 6,
1490
+ fill: new Fill({
1491
+ color: '#000000'
1492
+ })
1493
+ })
1494
+ }),
1495
+ new Style({
1496
+ image: new Circle({
1497
+ radius: 4,
1498
+ fill: new Fill({
1499
+ color: '#ff0000'
1500
+ })
1501
+ })
1502
+ })
1503
+ ];
1504
+ }
1505
+ else {
1506
+ return [
1507
+ new Style({
1508
+ image: new Circle({
1509
+ radius: 5,
1510
+ fill: new Fill({
1511
+ color: '#ff0000'
1512
+ })
1513
+ })
1514
+ }),
1515
+ new Style({
1516
+ image: new Circle({
1517
+ radius: 2,
1518
+ fill: new Fill({
1519
+ color: '#000000'
1520
+ })
1521
+ })
1522
+ })
1523
+ ];
1524
+ }
1525
+ default:
1526
+ // If editing mode is active, show bigger vertex
1527
+ if (this._isEditModeOn || this._isDrawModeOn) {
1528
+ return [
1529
+ new Style({
1530
+ stroke: new Stroke({
1531
+ color: 'rgba( 255, 0, 0, 1)',
1532
+ width: 4
1533
+ }),
1534
+ fill: new Fill({
1535
+ color: 'rgba(255, 0, 0, 0.7)'
1536
+ })
1537
+ }),
1538
+ new Style({
1539
+ image: new Circle({
1540
+ radius: 4,
1541
+ fill: new Fill({
1542
+ color: '#ff0000'
1543
+ }),
1544
+ stroke: new Stroke({
1545
+ width: 2,
1546
+ color: 'rgba(5, 5, 5, 0.9)'
1547
+ })
1548
+ }),
1549
+ geometry: (feature) => getVertexs(feature)
1550
+ }),
1551
+ new Style({
1552
+ stroke: new Stroke({
1553
+ color: 'rgba(255, 255, 255, 0.7)',
1554
+ width: 2
1555
+ })
1556
+ })
1557
+ ];
1558
+ }
1559
+ else {
1560
+ return [
1561
+ new Style({
1562
+ image: new Circle({
1563
+ radius: 2,
1564
+ fill: new Fill({
1565
+ color: '#000000'
1566
+ })
1567
+ }),
1568
+ geometry: (feature) => getVertexs(feature)
1569
+ }),
1570
+ new Style({
1571
+ stroke: new Stroke({
1572
+ color: '#ff0000',
1573
+ width: 4
1574
+ }),
1575
+ fill: new Fill({
1576
+ color: 'rgba(255, 0, 0, 0.7)'
1577
+ })
1578
+ })
1579
+ ];
1580
+ }
1581
+ }
1582
+ }
1583
+ /**
1584
+ *
1585
+ * @param feature
1586
+ * @private
1587
+ */
1588
+ _editModeOn(feature) {
1589
+ this._editFeatureOriginal = feature.clone();
1590
+ this._isEditModeOn = true;
1591
+ // To refresh the style
1592
+ this._editLayer.getSource().changed();
1593
+ this._removeOverlayHelper(feature);
1594
+ const controlDiv = document.createElement('div');
1595
+ controlDiv.className = 'ol-wfst--changes-control';
1596
+ const elements = document.createElement('div');
1597
+ elements.className = 'ol-wfst--changes-control-el';
1598
+ const elementId = document.createElement('div');
1599
+ elementId.className = 'ol-wfst--changes-control-id';
1600
+ elementId.innerHTML = `<b>${this._i18n.labels.editMode}</b> - <i>${String(feature.getId())}</i>`;
1601
+ const acceptButton = document.createElement('button');
1602
+ acceptButton.type = 'button';
1603
+ acceptButton.textContent = this._i18n.labels.apply;
1604
+ acceptButton.className = 'btn btn-sm btn-primary';
1605
+ acceptButton.onclick = () => {
1606
+ this._showLoading();
1607
+ this._collectionModify.remove(feature);
1608
+ };
1609
+ const cancelButton = document.createElement('button');
1610
+ cancelButton.type = 'button';
1611
+ cancelButton.textContent = this._i18n.labels.cancel;
1612
+ cancelButton.className = 'btn btn-sm btn-secondary';
1613
+ cancelButton.onclick = () => {
1614
+ feature.setGeometry(this._editFeatureOriginal.getGeometry());
1615
+ this._removeFeatureFromEditList(feature);
1616
+ this._collectionModify.remove(feature);
1617
+ };
1618
+ const deleteButton = document.createElement('button');
1619
+ deleteButton.type = 'button';
1620
+ deleteButton.textContent = this._i18n.labels.delete;
1621
+ deleteButton.className = 'btn btn-sm btn-danger-outline';
1622
+ deleteButton.onclick = () => {
1623
+ this._deleteFeature(feature, true);
1624
+ };
1625
+ elements.append(elementId);
1626
+ elements.append(cancelButton);
1627
+ elements.append(acceptButton);
1628
+ elements.append(deleteButton);
1629
+ controlDiv.append(elements);
1630
+ this._controlApplyDiscardChanges = new Control({
1631
+ element: controlDiv
1632
+ });
1633
+ this._map.addControl(this._controlApplyDiscardChanges);
1634
+ }
1635
+ /**
1636
+ * @private
1637
+ */
1638
+ _editModeOff() {
1639
+ this._isEditModeOn = false;
1640
+ this._map.removeControl(this._controlApplyDiscardChanges);
1641
+ }
1642
+ /**
1643
+ * Remove a feature from the edit Layer and from the Geoserver
1644
+ *
1645
+ * @param feature
1646
+ * @private
1647
+ */
1648
+ _deleteFeature(feature, confirm) {
1649
+ const deleteEl = () => {
1650
+ const features = Array.isArray(feature) ? feature : [feature];
1651
+ features.forEach((feature) => {
1652
+ feature.set('_delete_', true, true);
1653
+ this._editLayer.getSource().removeFeature(feature);
1654
+ });
1655
+ this._collectionModify.clear();
1656
+ const layerName = feature.get('_layerName_');
1657
+ const { mode } = this._options.layers.find((layer) => layer.name === layerName);
1658
+ if (mode === 'wfs') {
1659
+ this.interactionWfsSelect.getFeatures().remove(feature);
1660
+ }
1661
+ };
1662
+ if (confirm) {
1663
+ const confirmModal = Modal.confirm(this._i18n.labels.confirmDelete, Object.assign({}, this._options.modal));
1664
+ confirmModal.show().once('dismiss', function (modal, ev, button) {
1665
+ if (button && button.value) {
1666
+ deleteEl();
1667
+ }
1668
+ });
1669
+ }
1670
+ else {
1671
+ deleteEl();
1672
+ }
1673
+ }
1674
+ /**
1675
+ * Add a feature to the Edit Layer to allow editing, and creates an Overlay Helper to show options
1676
+ *
1677
+ * @param feature
1678
+ * @param coordinate
1679
+ * @param layerName
1680
+ * @private
1681
+ */
1682
+ _addFeatureToEdit(feature, coordinate = null, layerName = null) {
1683
+ const prepareOverlay = () => {
1684
+ const svgFields = `<img src="${img$1}"/>`;
1685
+ const editFieldsEl = document.createElement('div');
1686
+ editFieldsEl.className = 'ol-wfst--edit-button-cnt';
1687
+ editFieldsEl.innerHTML = `<button class="ol-wfst--edit-button" type="button" title="${this._i18n.labels.editFields}">${svgFields}</button>`;
1688
+ editFieldsEl.onclick = () => {
1689
+ this._initEditFieldsModal(feature);
1690
+ };
1691
+ const buttons = document.createElement('div');
1692
+ buttons.append(editFieldsEl);
1693
+ const svgGeom = `<img src="${img$2}"/>`;
1694
+ const editGeomEl = document.createElement('div');
1695
+ editGeomEl.className = 'ol-wfst--edit-button-cnt';
1696
+ editGeomEl.innerHTML = `<button class="ol-wfst--edit-button" type="button" title="${this._i18n.labels.editGeom}">${svgGeom}</button>`;
1697
+ editGeomEl.onclick = () => {
1698
+ this._editModeOn(feature);
1699
+ };
1700
+ buttons.append(editGeomEl);
1701
+ const position = coordinate || getCenter(feature.getGeometry().getExtent());
1702
+ const buttonsOverlay = new Overlay({
1703
+ id: feature.getId(),
1704
+ position: position,
1705
+ positioning: 'center-center',
1706
+ element: buttons,
1707
+ offset: [0, -40],
1708
+ stopEvent: true
1709
+ });
1710
+ this._map.addOverlay(buttonsOverlay);
1711
+ };
1712
+ if (layerName) {
1713
+ // Guardamos el nombre de la capa de donde sale la feature
1714
+ feature.set('_layerName_', layerName);
1715
+ }
1716
+ const props = feature ? feature.getProperties() : '';
1717
+ if (props) {
1718
+ if (feature.getGeometry()) {
1719
+ this._editLayer.getSource().addFeature(feature);
1720
+ this._collectionModify.push(feature);
1721
+ prepareOverlay();
1722
+ if (this._useLockFeature && this._hasLockFeature) {
1723
+ this._lockFeature(feature.getId(), feature.get('_layerName_'));
1724
+ }
1725
+ }
1726
+ }
1727
+ }
1728
+ /**
1729
+ * Removes in the DOM the class of the tools
1730
+ * @private
1731
+ */
1732
+ _resetStateButtons() {
1733
+ const activeBtn = document.querySelector('.ol-wfst--tools-control-btn.wfst--active');
1734
+ if (activeBtn) {
1735
+ activeBtn.classList.remove('wfst--active');
1736
+ }
1737
+ }
1738
+ /**
1739
+ * Confirm modal before transact to the GeoServer the features in the file
1740
+ *
1741
+ * @param content
1742
+ * @param featureToInsert
1743
+ * @private
1744
+ */
1745
+ _initUploadFileModal(content, featuresToInsert) {
1746
+ const footer = `
1747
+ <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
1748
+ ${this._i18n.labels.cancel}
1749
+ </button>
1750
+ <button type="button" class="btn btn-sm btn-primary" data-action="save" data-dismiss="modal">
1751
+ ${this._i18n.labels.upload}
1752
+ </button>
1753
+ `;
1754
+ const modal = new Modal(Object.assign(Object.assign({}, this._options.modal), { header: true, headerClose: false, title: this._i18n.labels.uploadFeatures +
1755
+ ' ' +
1756
+ this._layerToInsertElements, content: content, backdrop: 'static', footer: footer })).show();
1757
+ modal.on('dismiss', (modal, event) => {
1758
+ // On saving changes
1759
+ if (event.target.dataset.action === 'save') {
1760
+ this._transactWFS('insert', featuresToInsert, this._layerToInsertElements);
1761
+ }
1762
+ else {
1763
+ // On cancel button
1764
+ this._editLayer.getSource().clear();
1765
+ }
1766
+ });
1767
+ }
1768
+ /**
1769
+ * Parse and check geometry of uploaded files
1770
+ *
1771
+ * @param evt
1772
+ * @private
1773
+ */
1774
+ _processUploadFile(evt) {
1775
+ return __awaiter(this, void 0, void 0, function* () {
1776
+ /**
1777
+ * Read data file
1778
+ * @param file
1779
+ * @private
1780
+ */
1781
+ const fileReader = (file) => {
1782
+ return new Promise((resolve, reject) => {
1783
+ const reader = new FileReader();
1784
+ reader.addEventListener('load', (e) => __awaiter(this, void 0, void 0, function* () {
1785
+ const fileData = e.target.result;
1786
+ resolve(fileData);
1787
+ }));
1788
+ reader.addEventListener('error', (err) => {
1789
+ console.error('Error' + err);
1790
+ reject();
1791
+ });
1792
+ reader.readAsText(file);
1793
+ });
1794
+ };
1795
+ /**
1796
+ * Attemp to change the geometry feature to the layer
1797
+ * @param feature
1798
+ * @private
1799
+ */
1800
+ const fixGeometry = (feature) => {
1801
+ // Geometry of the layer
1802
+ const geomTypeLayer = this._geoServerData[this._layerToInsertElements].geomType;
1803
+ const geomTypeFeature = feature.getGeometry().getType();
1804
+ let geom;
1805
+ switch (geomTypeFeature) {
1806
+ case 'Point': {
1807
+ if (geomTypeLayer === 'MultiPoint') {
1808
+ const coords = feature.getGeometry().getCoordinates();
1809
+ geom = new MultiPoint([coords]);
1810
+ }
1811
+ break;
1812
+ }
1813
+ case 'LineString':
1814
+ if (geomTypeLayer === 'MultiLineString') {
1815
+ const coords = feature.getGeometry().getCoordinates();
1816
+ geom = new MultiLineString([coords]);
1817
+ }
1818
+ break;
1819
+ case 'Polygon':
1820
+ if (geomTypeLayer === 'MultiPolygon') {
1821
+ const coords = feature.getGeometry().getCoordinates();
1822
+ geom = new MultiPolygon([coords]);
1823
+ }
1824
+ break;
1825
+ default:
1826
+ geom = null;
1827
+ }
1828
+ if (!geom) {
1829
+ return null;
1830
+ }
1831
+ feature.setGeometry(geom);
1832
+ return feature;
1833
+ };
1834
+ /**
1835
+ * Check if the feature has the same geometry as the target layer
1836
+ * @param feature
1837
+ * @private
1838
+ */
1839
+ const checkGeometry = (feature) => {
1840
+ // Geometry of the layer
1841
+ const geomTypeLayer = this._geoServerData[this._layerToInsertElements].geomType;
1842
+ const geomTypeFeature = feature.getGeometry().getType();
1843
+ // This geom accepts every type of geometry
1844
+ if (geomTypeLayer === GeometryType.GeometryCollection) {
1845
+ return true;
1846
+ }
1847
+ return geomTypeFeature === geomTypeLayer;
1848
+ };
1849
+ const file = evt.target.files[0];
1850
+ let features;
1851
+ if (!file) {
1852
+ return;
1853
+ }
1854
+ const extension = file.name.split('.').pop().toLowerCase();
1855
+ try {
1856
+ // If the user uses a custom fucntion...
1857
+ if (this._options.processUpload) {
1858
+ features = this._options.processUpload(file);
1859
+ }
1860
+ // If the user functions return features, we dont process anything more
1861
+ if (!features) {
1862
+ const string = yield fileReader(file);
1863
+ if (extension === 'geojson' || extension === 'json') {
1864
+ features = this._formatGeoJSON.readFeatures(string, {
1865
+ featureProjection: this._view.getProjection().getCode()
1866
+ });
1867
+ }
1868
+ else if (extension === 'kml') {
1869
+ features = this._formatKml.readFeatures(string, {
1870
+ featureProjection: this._view.getProjection().getCode()
1871
+ });
1872
+ }
1873
+ else {
1874
+ this._showError(this._i18n.errors.badFormat);
1875
+ }
1876
+ }
1877
+ }
1878
+ catch (err) {
1879
+ this._showError(this._i18n.errors.badFile, err);
1880
+ }
1881
+ let invalidFeaturesCount = 0;
1882
+ let validFeaturesCount = 0;
1883
+ const featuresToInsert = [];
1884
+ for (let feature of features) {
1885
+ // If the geometry doesn't correspond to the layer, try to fixit.
1886
+ // If we can't, don't use it
1887
+ if (!checkGeometry(feature)) {
1888
+ feature = fixGeometry(feature);
1889
+ }
1890
+ if (feature) {
1891
+ featuresToInsert.push(feature);
1892
+ validFeaturesCount++;
1893
+ }
1894
+ else {
1895
+ invalidFeaturesCount++;
1896
+ continue;
1897
+ }
1898
+ }
1899
+ if (!validFeaturesCount) {
1900
+ this._showError(this._i18n.errors.noValidGeometry);
1901
+ }
1902
+ else {
1903
+ this._resetStateButtons();
1904
+ this.activateEditMode();
1905
+ const content = `
1906
+ ${this._i18n.labels.validFeatures}: ${validFeaturesCount}<br>
1907
+ ${invalidFeaturesCount
1908
+ ? `${this._i18n.labels.invalidFeatures}: ${invalidFeaturesCount}`
1909
+ : ''}
1910
+ `;
1911
+ this._editLayer.getSource().addFeatures(featuresToInsert);
1912
+ this._initUploadFileModal(content, featuresToInsert);
1913
+ this._view.fit(this._editLayer.getSource().getExtent(), {
1914
+ size: this._map.getSize(),
1915
+ maxZoom: 21,
1916
+ padding: [100, 100, 100, 100]
1917
+ });
1918
+ }
1919
+ // Reset the input to allow another onChange trigger
1920
+ evt.target.value = null;
1921
+ });
1922
+ }
1923
+ /**
1924
+ * Update geom Types availibles to select for this layer
1925
+ *
1926
+ * @param layerName
1927
+ * @param geomDrawTypeSelected
1928
+ * @private
1929
+ */
1930
+ _changeStateSelect(layerName, geomDrawTypeSelected = null) {
1931
+ /**
1932
+ * Set the geometry type in the select according to the geometry of
1933
+ * the layer in the geoserver and disable what does not correspond.
1934
+ *
1935
+ * @param value
1936
+ * @param options
1937
+ * @private
1938
+ */
1939
+ const setSelectState = (value, options) => {
1940
+ Array.from(this._selectDraw.options).forEach((option) => {
1941
+ option.selected = option.value === value ? true : false;
1942
+ option.disabled =
1943
+ options === 'all'
1944
+ ? false
1945
+ : options.includes(option.value)
1946
+ ? false
1947
+ : true;
1948
+ option.title = option.disabled
1949
+ ? this._i18n.labels.geomTypeNotSupported
1950
+ : '';
1951
+ });
1952
+ };
1953
+ let drawType;
1954
+ if (this._selectDraw) {
1955
+ const geomLayer = this._geoServerData[layerName].geomType;
1956
+ if (geomDrawTypeSelected) {
1957
+ drawType = this._selectDraw.value;
1958
+ }
1959
+ else {
1960
+ if (geomLayer === GeometryType.GeometryCollection) {
1961
+ drawType = GeometryType.LineString; // Default drawing type for GeometryCollection
1962
+ setSelectState(drawType, 'all');
1963
+ }
1964
+ else if (geomLayer === GeometryType.LinearRing) {
1965
+ drawType = GeometryType.LineString; // Default drawing type for GeometryCollection
1966
+ setSelectState(drawType, [
1967
+ GeometryType.Circle,
1968
+ GeometryType.LinearRing,
1969
+ GeometryType.Polygon
1970
+ ]);
1971
+ this._selectDraw.value = drawType;
1972
+ }
1973
+ else {
1974
+ drawType = geomLayer;
1975
+ setSelectState(drawType, [geomLayer]);
1976
+ }
1977
+ }
1978
+ }
1979
+ return drawType;
1980
+ }
1981
+ /**
1982
+ * Activate/deactivate the draw mode
1983
+ *
1984
+ * @param layerName
1985
+ * @public
1986
+ */
1987
+ activateDrawMode(layerName) {
1988
+ /**
1989
+ *
1990
+ * @param layerName
1991
+ * @private
1992
+ */
1993
+ const addDrawInteraction = (layerName) => {
1994
+ this.activateEditMode(false);
1995
+ // If already exists, remove
1996
+ if (this.interactionDraw) {
1997
+ this._map.removeInteraction(this.interactionDraw);
1998
+ }
1999
+ const geomDrawType = this._selectDraw.value;
2000
+ this.interactionDraw = new Draw({
2001
+ source: this._editLayer.getSource(),
2002
+ type: geomDrawType,
2003
+ style: (feature) => this._styleFunction(feature)
2004
+ });
2005
+ this._map.addInteraction(this.interactionDraw);
2006
+ this.interactionDraw.on('drawstart', (evt) => {
2007
+ super.dispatchEvent(evt);
2008
+ });
2009
+ this.interactionDraw.on('drawend', (evt) => {
2010
+ const feature = evt.feature;
2011
+ this._transactWFS('insert', feature, layerName);
2012
+ super.dispatchEvent(evt);
2013
+ });
2014
+ };
2015
+ if (!this.interactionDraw && !layerName) {
2016
+ return;
2017
+ }
2018
+ if (layerName) {
2019
+ // If layer is set to invisible, show warning
2020
+ if (!this._mapLayers[layerName].getVisible()) {
2021
+ return;
2022
+ }
2023
+ const btn = document.querySelector('.ol-wfst--tools-control-btn-draw');
2024
+ if (btn) {
2025
+ btn.classList.add('wfst--active');
2026
+ }
2027
+ this._viewport.classList.add('draw-mode');
2028
+ addDrawInteraction(String(layerName));
2029
+ }
2030
+ else {
2031
+ this._map.removeInteraction(this.interactionDraw);
2032
+ this._viewport.classList.remove('draw-mode');
2033
+ }
2034
+ this._isDrawModeOn = layerName ? true : false;
2035
+ }
2036
+ /**
2037
+ * Activate/desactivate the edit mode
2038
+ *
2039
+ * @param bool
2040
+ * @public
2041
+ */
2042
+ activateEditMode(bool = true) {
2043
+ if (bool) {
2044
+ const btn = document.querySelector('.ol-wfst--tools-control-btn-edit');
2045
+ if (btn) {
2046
+ btn.classList.add('wfst--active');
2047
+ }
2048
+ this.activateDrawMode(false);
2049
+ }
2050
+ else {
2051
+ // Deselct features
2052
+ this._collectionModify.clear();
2053
+ }
2054
+ if (this.interactionSelectModify) {
2055
+ this.interactionSelectModify.setActive(bool);
2056
+ }
2057
+ this.interactionModify.setActive(bool);
2058
+ if (this.interactionWfsSelect)
2059
+ this.interactionWfsSelect.setActive(bool);
2060
+ }
2061
+ /**
2062
+ * Add features directly to the geoserver, in a custom layer
2063
+ * without checking geometry or showing modal to confirm.
2064
+ *
2065
+ * @param layerName
2066
+ * @param features
2067
+ * @public
2068
+ */
2069
+ insertFeaturesTo(layerName, features) {
2070
+ this._transactWFS('insert', features, layerName);
2071
+ }
2072
+ /**
2073
+ * Shows a fields form in a modal window to allow changes in the properties of the feature.
2074
+ *
2075
+ * @param feature
2076
+ * @private
2077
+ */
2078
+ _initEditFieldsModal(feature) {
2079
+ this._editFeature = feature;
2080
+ const properties = feature.getProperties();
2081
+ const layer = feature.get('_layerName_');
2082
+ // Data schema from the geoserver
2083
+ const dataSchema = this._geoServerData[layer].properties;
2084
+ let content = '<form autocomplete="false">';
2085
+ Object.keys(properties).forEach((key) => {
2086
+ // If the feature field exists in the geoserver and is not added by openlayers
2087
+ const field = dataSchema.find((data) => data.name === key);
2088
+ if (field) {
2089
+ const typeXsd = field.type;
2090
+ let type;
2091
+ switch (typeXsd) {
2092
+ case 'xsd:string':
2093
+ type = 'text';
2094
+ break;
2095
+ case 'xsd:number':
2096
+ case 'xsd:int':
2097
+ type = 'number';
2098
+ break;
2099
+ case 'xsd:date-time':
2100
+ type = 'datetime';
2101
+ break;
2102
+ default:
2103
+ type = 'text';
2104
+ }
2105
+ if (type) {
2106
+ content += `
2107
+ <div class="ol-wfst--input-field-container">
2108
+ <label class="ol-wfst--input-field-label" for="${key}">${key}</label>
2109
+ <input placeholder="NULL" class="ol-wfst--input-field-input" type="${type}" name="${key}" value="${properties[key] || ''}">
2110
+ </div>`;
2111
+ }
2112
+ }
2113
+ });
2114
+ content += '</form>';
2115
+ const footer = `
2116
+ <button type="button" class="btn btn-sm btn-link btn-third" data-action="delete" data-dismiss="modal">
2117
+ ${this._i18n.labels.delete}
2118
+ </button>
2119
+ <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
2120
+ ${this._i18n.labels.cancel}
2121
+ </button>
2122
+ <button type="button" class="btn btn-sm btn-primary" data-action="save" data-dismiss="modal">
2123
+ ${this._i18n.labels.save}
2124
+ </button>
2125
+ `;
2126
+ const modal = new Modal(Object.assign(Object.assign({}, this._options.modal), { header: true, headerClose: true, title: `${this._i18n.labels.editElement} ${this._editFeature.getId()} `, content: content, footer: footer })).show();
2127
+ modal.on('dismiss', (modal, event) => {
2128
+ // On saving changes
2129
+ if (event.target.dataset.action === 'save') {
2130
+ const inputs = modal.el.querySelectorAll('input');
2131
+ inputs.forEach((el) => {
2132
+ const value = el.value;
2133
+ const field = el.name;
2134
+ this._editFeature.set(field, value, /*isSilent = */ true);
2135
+ });
2136
+ this._editFeature.changed();
2137
+ this._addFeatureToEditedList(this._editFeature);
2138
+ // Force deselect to trigger handler
2139
+ this._collectionModify.remove(this._editFeature);
2140
+ }
2141
+ else if (event.target.dataset.action === 'delete') {
2142
+ this._deleteFeature(this._editFeature, true);
2143
+ }
2144
+ });
2145
+ }
2146
+ /**
2147
+ * Remove the overlay helper atttached to a specify feature
2148
+ * @param feature
2149
+ * @private
2150
+ */
2151
+ _removeOverlayHelper(feature) {
2152
+ const featureId = feature.getId();
2153
+ if (!featureId) {
2154
+ return;
2155
+ }
2156
+ const overlay = this._map.getOverlayById(featureId);
2157
+ if (!overlay) {
2158
+ return;
2159
+ }
2160
+ this._map.removeOverlay(overlay);
2161
+ }
2162
+ }
2163
+ /**
2164
+ *
2165
+ * @param target
2166
+ * @param sources
2167
+ * @returns
2168
+ * @private
2169
+ */
2170
+ const deepObjectAssign = (target, ...sources) => {
2171
+ sources.forEach((source) => {
2172
+ Object.keys(source).forEach((key) => {
2173
+ const s_val = source[key];
2174
+ const t_val = target[key];
2175
+ target[key] =
2176
+ t_val &&
2177
+ s_val &&
2178
+ typeof t_val === 'object' &&
2179
+ typeof s_val === 'object' &&
2180
+ !Array.isArray(t_val) // Don't merge arrays
2181
+ ? deepObjectAssign(t_val, s_val)
2182
+ : s_val;
2183
+ });
2184
+ });
2185
+ return target;
2186
+ };
2257
2187
 
2258
2188
  export default Wfst;
2189
+ //# sourceMappingURL=ol-wfst.js.map