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