ol-wfst 3.0.5 → 4.0.1

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 (133) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/LICENSE.md +21 -0
  3. package/README.md +640 -196
  4. package/dist/@enums.d.ts +5 -0
  5. package/dist/@enums.d.ts.map +1 -1
  6. package/dist/@types.d.ts +195 -0
  7. package/dist/@types.d.ts.map +1 -0
  8. package/dist/Geoserver.d.ts +260 -0
  9. package/dist/Geoserver.d.ts.map +1 -0
  10. package/dist/WfsLayer.d.ts +53 -0
  11. package/dist/WfsLayer.d.ts.map +1 -0
  12. package/dist/WmsLayer.d.ts +61 -0
  13. package/dist/WmsLayer.d.ts.map +1 -0
  14. package/dist/css/ol-wfst.css +19 -18
  15. package/dist/css/ol-wfst.min.css +1 -1
  16. package/dist/defaults.d.ts +4 -0
  17. package/dist/defaults.d.ts.map +1 -0
  18. package/dist/modules/EditControlChanges.d.ts +18 -0
  19. package/dist/modules/EditControlChanges.d.ts.map +1 -0
  20. package/dist/modules/EditFieldsModal.d.ts +18 -0
  21. package/dist/modules/EditFieldsModal.d.ts.map +1 -0
  22. package/dist/modules/EditOverlay.d.ts +7 -0
  23. package/dist/modules/EditOverlay.d.ts.map +1 -0
  24. package/dist/modules/LayersControl.d.ts +35 -0
  25. package/dist/modules/LayersControl.d.ts.map +1 -0
  26. package/dist/modules/Uploads.d.ts +47 -0
  27. package/dist/modules/Uploads.d.ts.map +1 -0
  28. package/dist/modules/base/BaseLayer.d.ts +62 -0
  29. package/dist/modules/base/BaseLayer.d.ts.map +1 -0
  30. package/dist/modules/base/WfsSource.d.ts +48 -0
  31. package/dist/modules/base/WfsSource.d.ts.map +1 -0
  32. package/dist/modules/base/WmsSource.d.ts +47 -0
  33. package/dist/modules/base/WmsSource.d.ts.map +1 -0
  34. package/dist/modules/editLayer.d.ts +4 -0
  35. package/dist/modules/editLayer.d.ts.map +1 -0
  36. package/dist/modules/errors.d.ts +11 -0
  37. package/dist/modules/errors.d.ts.map +1 -0
  38. package/dist/modules/helpers.d.ts +7 -0
  39. package/dist/modules/helpers.d.ts.map +1 -0
  40. package/dist/{assets → modules}/i18n/en.d.ts +0 -0
  41. package/dist/modules/i18n/en.d.ts.map +1 -0
  42. package/dist/{assets → modules}/i18n/es.d.ts +0 -0
  43. package/dist/modules/i18n/es.d.ts.map +1 -0
  44. package/dist/modules/i18n/index.d.ts +15 -0
  45. package/dist/modules/i18n/index.d.ts.map +1 -0
  46. package/dist/{assets → modules}/i18n/zh.d.ts +0 -0
  47. package/dist/modules/i18n/zh.d.ts.map +1 -0
  48. package/dist/modules/loading.d.ts +3 -0
  49. package/dist/modules/loading.d.ts.map +1 -0
  50. package/dist/modules/state.d.ts +22 -0
  51. package/dist/modules/state.d.ts.map +1 -0
  52. package/dist/modules/styleFunction.d.ts +13 -0
  53. package/dist/modules/styleFunction.d.ts.map +1 -0
  54. package/dist/myPragma.d.ts +2 -0
  55. package/dist/myPragma.d.ts.map +1 -0
  56. package/dist/ol-wfst.d.ts +83 -326
  57. package/dist/ol-wfst.d.ts.map +1 -1
  58. package/dist/ol-wfst.js +6126 -5139
  59. package/dist/ol-wfst.js.map +1 -1
  60. package/dist/ol-wfst.min.js +1 -15
  61. package/dist/ol-wfst.min.js.map +1 -1
  62. package/lib/@enums.d.ts +5 -0
  63. package/lib/@enums.d.ts.map +1 -1
  64. package/lib/@types.d.ts +195 -0
  65. package/lib/@types.d.ts.map +1 -0
  66. package/lib/Geoserver.d.ts +260 -0
  67. package/lib/Geoserver.d.ts.map +1 -0
  68. package/lib/WfsLayer.d.ts +53 -0
  69. package/lib/WfsLayer.d.ts.map +1 -0
  70. package/lib/WmsLayer.d.ts +61 -0
  71. package/lib/WmsLayer.d.ts.map +1 -0
  72. package/lib/defaults.d.ts +4 -0
  73. package/lib/defaults.d.ts.map +1 -0
  74. package/lib/index-es.js +2765 -0
  75. package/lib/index-es.js.map +1 -0
  76. package/lib/modules/EditControlChanges.d.ts +18 -0
  77. package/lib/modules/EditControlChanges.d.ts.map +1 -0
  78. package/lib/modules/EditFieldsModal.d.ts +18 -0
  79. package/lib/modules/EditFieldsModal.d.ts.map +1 -0
  80. package/lib/modules/EditOverlay.d.ts +7 -0
  81. package/lib/modules/EditOverlay.d.ts.map +1 -0
  82. package/lib/modules/LayersControl.d.ts +35 -0
  83. package/lib/modules/LayersControl.d.ts.map +1 -0
  84. package/lib/modules/Uploads.d.ts +47 -0
  85. package/lib/modules/Uploads.d.ts.map +1 -0
  86. package/lib/modules/base/BaseLayer.d.ts +62 -0
  87. package/lib/modules/base/BaseLayer.d.ts.map +1 -0
  88. package/lib/modules/base/WfsSource.d.ts +48 -0
  89. package/lib/modules/base/WfsSource.d.ts.map +1 -0
  90. package/lib/modules/base/WmsSource.d.ts +47 -0
  91. package/lib/modules/base/WmsSource.d.ts.map +1 -0
  92. package/lib/modules/editLayer.d.ts +4 -0
  93. package/lib/modules/editLayer.d.ts.map +1 -0
  94. package/lib/modules/errors.d.ts +11 -0
  95. package/lib/modules/errors.d.ts.map +1 -0
  96. package/lib/modules/helpers.d.ts +7 -0
  97. package/lib/modules/helpers.d.ts.map +1 -0
  98. package/lib/{assets → modules}/i18n/en.d.ts +0 -0
  99. package/lib/modules/i18n/en.d.ts.map +1 -0
  100. package/lib/{assets → modules}/i18n/es.d.ts +0 -0
  101. package/lib/modules/i18n/es.d.ts.map +1 -0
  102. package/lib/modules/i18n/index.d.ts +15 -0
  103. package/lib/modules/i18n/index.d.ts.map +1 -0
  104. package/lib/{assets → modules}/i18n/zh.d.ts +0 -0
  105. package/lib/modules/i18n/zh.d.ts.map +1 -0
  106. package/lib/modules/loading.d.ts +3 -0
  107. package/lib/modules/loading.d.ts.map +1 -0
  108. package/lib/modules/state.d.ts +22 -0
  109. package/lib/modules/state.d.ts.map +1 -0
  110. package/lib/modules/styleFunction.d.ts +13 -0
  111. package/lib/modules/styleFunction.d.ts.map +1 -0
  112. package/lib/myPragma.d.ts +2 -0
  113. package/lib/myPragma.d.ts.map +1 -0
  114. package/lib/ol-wfst.d.ts +83 -326
  115. package/lib/ol-wfst.d.ts.map +1 -1
  116. package/lib/style/css/ol-gisify.css +19 -18
  117. package/lib/style/scss/_variables.scss +17 -0
  118. package/lib/style/scss/ol-wfst.scss +31 -28
  119. package/lib/tsconfig.tsbuildinfo +1 -1
  120. package/package.json +6 -5
  121. package/dist/assets/i18n/en.d.ts.map +0 -1
  122. package/dist/assets/i18n/es.d.ts.map +0 -1
  123. package/dist/assets/i18n/index.d.ts +0 -4
  124. package/dist/assets/i18n/index.d.ts.map +0 -1
  125. package/dist/assets/i18n/zh.d.ts.map +0 -1
  126. package/dist/tsconfig.tsbuildinfo +0 -1
  127. package/lib/assets/i18n/en.d.ts.map +0 -1
  128. package/lib/assets/i18n/es.d.ts.map +0 -1
  129. package/lib/assets/i18n/index.d.ts +0 -4
  130. package/lib/assets/i18n/index.d.ts.map +0 -1
  131. package/lib/assets/i18n/zh.d.ts.map +0 -1
  132. package/lib/ol-wfst.js +0 -2276
  133. package/lib/ol-wfst.js.map +0 -1
@@ -0,0 +1,2765 @@
1
+ import { Style, Stroke, Fill, Circle } from 'ol/style';
2
+ import { Control } from 'ol/control';
3
+ import { Modify, Snap, Select, Draw } from 'ol/interaction';
4
+ import { Observable, Feature, Overlay, Collection } from 'ol';
5
+ import BaseEvent from 'ol/events/Event';
6
+ import { primaryAction, never } from 'ol/events/condition';
7
+ import { unByKey } from 'ol/Observable';
8
+ import Modal from 'modal-vanilla';
9
+ import VectorLayer from 'ol/layer/Vector';
10
+ import { Mixin } from 'ts-mixer';
11
+ import Layer from 'ol/layer/Base';
12
+ import GeoJSON from 'ol/format/GeoJSON';
13
+ import VectorSource, { VectorSourceEvent } from 'ol/source/Vector';
14
+ import { transformExtent } from 'ol/proj';
15
+ import { bbox } from 'ol/loadingstrategy';
16
+ import TileLayer from 'ol/layer/Tile';
17
+ import { GeoJSON as GeoJSON$1, WFS, KML } from 'ol/format';
18
+ import TileWMS from 'ol/source/TileWMS';
19
+ import TileState from 'ol/TileState';
20
+ import { MultiPolygon, MultiLineString, MultiPoint, GeometryCollection, Polygon, Circle as Circle$1 } from 'ol/geom';
21
+ import BaseObject from 'ol/Object';
22
+ import { fromCircle } from 'ol/geom/Polygon';
23
+ import { getCenter } from 'ol/extent';
24
+
25
+ // External
26
+ let options = {};
27
+ // Store layerNames that has errors
28
+ const isError = new Set();
29
+ const initModal = (opts) => {
30
+ options = opts;
31
+ };
32
+ const parseError = (geoserverError) => {
33
+ if ('exceptions' in geoserverError) {
34
+ return geoserverError.exceptions.map((e) => e.text).join(',');
35
+ }
36
+ else {
37
+ return '';
38
+ }
39
+ };
40
+ /**
41
+ * Show modal with errors
42
+ *
43
+ * @param msg
44
+ * @private
45
+ */
46
+ const showError = (msg, originalError = null, layerName = '') => {
47
+ // Prevent multiples modals error in the same layer
48
+ if (isError.has(layerName)) {
49
+ return;
50
+ }
51
+ isError.add(layerName);
52
+ let err_msg = `<b>Error: ${msg}</b>`;
53
+ if (originalError) {
54
+ err_msg += `. ${originalError.message}`;
55
+ }
56
+ const al = Modal.alert(err_msg, options);
57
+ al.show();
58
+ al.on('hidden', () => {
59
+ isError.delete(layerName);
60
+ });
61
+ };
62
+
63
+ const es = {
64
+ labels: {
65
+ select: 'Seleccionar',
66
+ addElement: 'Modo dibujo',
67
+ editElement: 'Editar elemento',
68
+ save: 'Guardar',
69
+ delete: 'Eliminar',
70
+ cancel: 'Cancelar',
71
+ apply: 'Aplicar cambios',
72
+ upload: 'Subir',
73
+ editMode: 'Modo Edición',
74
+ confirmDelete: '¿Estás seguro de borrar el elemento?',
75
+ geomTypeNotSupported: 'Geometría no compatible con la capa',
76
+ editFields: 'Editar campos',
77
+ editGeom: 'Editar geometría',
78
+ selectDrawType: 'Tipo de geometría para dibujar',
79
+ uploadToLayer: 'Subir archivo a la capa seleccionada',
80
+ uploadFeatures: 'Subida de elementos a la capa',
81
+ validFeatures: 'Válidas',
82
+ invalidFeatures: 'Invalidas',
83
+ loading: 'Cargando...',
84
+ toggleVisibility: 'Cambiar visibilidad de la capa',
85
+ close: 'Cerrar'
86
+ },
87
+ errors: {
88
+ capabilities: 'No se pudieron obtener las Capabilidades del GeoServer',
89
+ wfst: 'El GeoServer no tiene soporte a Transacciones',
90
+ layer: 'No se pudieron obtener datos de la capa',
91
+ layerNotFound: 'Capa no encontrada',
92
+ layerNotVisible: 'La capa no está visible',
93
+ noValidGeometry: 'No se encontraron geometrías válidas para agregar a esta capa',
94
+ geoserver: 'No se pudieron obtener datos desde el GeoServer',
95
+ badFormat: 'Formato no soportado',
96
+ badFile: 'Error al leer elementos del archivo',
97
+ lockFeature: 'No se pudieron bloquear elementos en el GeoServer.',
98
+ transaction: 'Error al hacer transacción con el GeoServer.',
99
+ getFeatures: 'Error al obtener elemento desde el GeoServer.'
100
+ }
101
+ };
102
+
103
+ const en = {
104
+ labels: {
105
+ select: 'Select',
106
+ addElement: 'Toggle Draw mode',
107
+ editElement: 'Edit feature',
108
+ save: 'Save',
109
+ delete: 'Delete',
110
+ cancel: 'Cancel',
111
+ apply: 'Apply changes',
112
+ upload: 'Upload',
113
+ editMode: 'Edit Mode',
114
+ confirmDelete: 'Are you sure to delete the feature?',
115
+ geomTypeNotSupported: 'Geometry not supported by layer',
116
+ editFields: 'Edit fields',
117
+ editGeom: 'Edit geometry',
118
+ selectDrawType: 'Geometry type to draw',
119
+ uploadToLayer: 'Upload file to selected layer',
120
+ uploadFeatures: 'Uploaded features to layer',
121
+ validFeatures: 'Valid geometries',
122
+ invalidFeatures: 'Invalid',
123
+ loading: 'Loading...',
124
+ toggleVisibility: 'Toggle layer visibility',
125
+ close: 'Close'
126
+ },
127
+ errors: {
128
+ capabilities: 'GeoServer Capabilities could not be downloaded.',
129
+ wfst: 'The GeoServer does not support Transactions',
130
+ layer: 'Could not get data from layer',
131
+ layerNotFound: 'Layer not found',
132
+ layerNotVisible: 'Layer is not visible',
133
+ noValidGeometry: 'No valid geometries found to add to this layer',
134
+ geoserver: 'Failed to get data from GeoServer',
135
+ badFormat: 'Unsupported format',
136
+ badFile: 'Error reading items from file',
137
+ lockFeature: 'Could not lock items on the GeoServer.',
138
+ transaction: 'Error when doing Transaction with GeoServer.',
139
+ getFeatures: 'Error getting elements from GeoServer.'
140
+ }
141
+ };
142
+
143
+ const zh = {
144
+ labels: {
145
+ select: '选择',
146
+ addElement: '切换绘图类型',
147
+ editElement: '编辑元素',
148
+ save: '保存',
149
+ delete: '删除',
150
+ cancel: '取消',
151
+ apply: '确认并应用改变',
152
+ upload: '上传',
153
+ editMode: '编辑模式',
154
+ confirmDelete: '确认删除元素?',
155
+ geomTypeNotSupported: '图层不支持该几何',
156
+ editFields: '编辑区域',
157
+ editGeom: '编辑几何',
158
+ selectDrawType: '几何类型',
159
+ uploadToLayer: '通过文件上传图层',
160
+ uploadFeatures: '上传元素到图层',
161
+ validFeatures: '合法的几何类型',
162
+ invalidFeatures: '不合法',
163
+ loading: '加载中...',
164
+ toggleVisibility: '切换图层透明度',
165
+ close: '关闭'
166
+ },
167
+ errors: {
168
+ capabilities: '无法加载GeoServer服务所支持的能力.',
169
+ wfst: 'GeoServer不支持事务',
170
+ layer: '无法从图层获得数据',
171
+ layerNotFound: 'Layer not found',
172
+ layerNotVisible: 'Layer is not visible',
173
+ noValidGeometry: '不支持的几何类型无法加载到图层',
174
+ geoserver: '无法从GeoServer获取数据',
175
+ badFormat: '不支持的格式',
176
+ badFile: '读取文件数据出错',
177
+ lockFeature: '无法锁定GeoServer上的元素.',
178
+ transaction: 'GeoServer处理事务出错.',
179
+ getFeatures: '从GeoServer获取元素出错.'
180
+ }
181
+ };
182
+
183
+ const langs = {
184
+ es,
185
+ en,
186
+ zh
187
+ };
188
+ // Set default Language
189
+ let I18N = en;
190
+ const setLang = (lang = 'en', customI18n = null) => {
191
+ // Check if language exists
192
+ if (lang in langs) {
193
+ I18N = langs[lang];
194
+ }
195
+ // Check if customs translations are provided
196
+ if (customI18n) {
197
+ I18N = { ...I18N, ...customI18n };
198
+ }
199
+ };
200
+
201
+ let loadingDiv;
202
+ const initLoading = () => {
203
+ loadingDiv = document.createElement('div');
204
+ loadingDiv.className = 'ol-wfst--tools-control--loading';
205
+ loadingDiv.innerHTML = I18N.labels.loading;
206
+ return loadingDiv;
207
+ };
208
+ const showLoading = (bool = true) => {
209
+ if (bool) {
210
+ loadingDiv.classList.add('ol-wfst--tools-control--loading-show');
211
+ }
212
+ else {
213
+ loadingDiv.classList.remove('ol-wfst--tools-control--loading-show');
214
+ }
215
+ };
216
+
217
+ var GeometryType;
218
+ (function (GeometryType) {
219
+ GeometryType["Point"] = "Point";
220
+ GeometryType["LineString"] = "LineString";
221
+ GeometryType["LinearRing"] = "LinearRing";
222
+ GeometryType["Polygon"] = "Polygon";
223
+ GeometryType["MultiPoint"] = "MultiPoint";
224
+ GeometryType["MultiLineString"] = "MultiLineString";
225
+ GeometryType["MultiPolygon"] = "MultiPolygon";
226
+ GeometryType["GeometryCollection"] = "GeometryCollection";
227
+ GeometryType["Circle"] = "Circle";
228
+ })(GeometryType || (GeometryType = {}));
229
+ var TransactionType;
230
+ (function (TransactionType) {
231
+ TransactionType["Insert"] = "insert";
232
+ TransactionType["Delete"] = "delete";
233
+ TransactionType["Update"] = "update";
234
+ })(TransactionType || (TransactionType = {}));
235
+
236
+ let map;
237
+ let layerToInsertElements = null;
238
+ let mode = null;
239
+ var Modes;
240
+ (function (Modes) {
241
+ Modes["Edit"] = "EDIT";
242
+ Modes["Draw"] = "DRAW";
243
+ })(Modes || (Modes = {}));
244
+ function activateMode(m = null) {
245
+ mode = m;
246
+ }
247
+ function getMode() {
248
+ return mode;
249
+ }
250
+ const editedFeatures = new Set();
251
+ const mapLayers = {};
252
+ function setMap(m) {
253
+ map = m;
254
+ }
255
+ function getMap() {
256
+ return map;
257
+ }
258
+ function setActiveLayerToInsertEls(layer) {
259
+ layerToInsertElements = layer;
260
+ }
261
+ function getActiveLayerToInsertEls() {
262
+ return layerToInsertElements;
263
+ }
264
+ function setMapLayers(data) {
265
+ Object.assign(mapLayers, data);
266
+ }
267
+ function getStoredMapLayers() {
268
+ return mapLayers;
269
+ }
270
+ function getStoredLayer(layerName) {
271
+ return getStoredMapLayers()[layerName];
272
+ }
273
+ function addFeatureToEditedList(feature) {
274
+ editedFeatures.add(String(feature.getId()));
275
+ }
276
+ function removeFeatureFromEditList(feature) {
277
+ editedFeatures.delete(String(feature.getId()));
278
+ }
279
+ function isFeatureEdited(feature) {
280
+ return editedFeatures.has(String(feature.getId()));
281
+ }
282
+
283
+ /**
284
+ * Base class from which all layer types are derived.
285
+ */
286
+ class BaseLayer extends Layer {
287
+ /**
288
+ * @private
289
+ */
290
+ _init() {
291
+ const geoserver = this.getGeoserver();
292
+ if (geoserver.isLoaded()) {
293
+ this.getAndUpdateDescribeFeatureType();
294
+ }
295
+ else {
296
+ geoserver.on('change:capabilities', () => {
297
+ this.getAndUpdateDescribeFeatureType();
298
+ });
299
+ }
300
+ }
301
+ /**
302
+ * Request and store data layers obtained by DescribeFeatureType
303
+ *
304
+ * @public
305
+ */
306
+ async getAndUpdateDescribeFeatureType() {
307
+ const layerName = this.get(BaseLayerProperty.NAME);
308
+ const layerLabel = this.get(BaseLayerProperty.LABEL);
309
+ try {
310
+ const geoserver = this.getGeoserver();
311
+ const params = new URLSearchParams({
312
+ service: 'wfs',
313
+ version: geoserver.getAdvanced().describeFeatureTypeVersion,
314
+ request: 'DescribeFeatureType',
315
+ typeName: layerName,
316
+ outputFormat: 'application/json',
317
+ exceptions: 'application/json'
318
+ });
319
+ const url_fetch = geoserver.getUrl() + '?' + params.toString();
320
+ const response = await fetch(url_fetch, {
321
+ headers: geoserver.getHeaders(),
322
+ credentials: geoserver.getCredentials()
323
+ });
324
+ if (!response.ok) {
325
+ throw new Error('');
326
+ }
327
+ const data = await response.json();
328
+ if (!data) {
329
+ throw new Error('');
330
+ }
331
+ const targetNamespace = data.targetNamespace;
332
+ const properties = data.featureTypes[0].properties;
333
+ // Find the geometry field
334
+ const geom = properties.find((el) => el.type.indexOf('gml:') >= 0);
335
+ data._parsed = {
336
+ namespace: targetNamespace,
337
+ properties: properties,
338
+ geomType: geom.localType,
339
+ geomField: geom.name
340
+ };
341
+ this.set(BaseLayerProperty.DESCRIBEFEATURETYPE, data);
342
+ }
343
+ catch (err) {
344
+ console.error(err);
345
+ throw new Error(`${I18N.errors.layer} "${layerLabel}"`);
346
+ }
347
+ }
348
+ /**
349
+ * @public
350
+ * @returns
351
+ */
352
+ isVisibleByZoom() {
353
+ return getMap().getView().getZoom() > this.getMinZoom();
354
+ }
355
+ /**
356
+ *
357
+ * @param mode
358
+ * @param features
359
+ * @public
360
+ */
361
+ async transactFeatures(mode, features) {
362
+ const geoserver = this.getGeoserver();
363
+ return geoserver.transact(mode, features, this.get(BaseLayerProperty.NAME));
364
+ }
365
+ async insertFeatures(features) {
366
+ return this.transactFeatures(TransactionType.Insert, features);
367
+ }
368
+ /**
369
+ * @public
370
+ * @param featureId
371
+ * @returns
372
+ */
373
+ async maybeLockFeature(featureId) {
374
+ const geoserver = this.getGeoserver();
375
+ if (geoserver.getUseLockFeature() && geoserver.hasLockFeature()) {
376
+ return await geoserver.lockFeature(featureId, this.get(BaseLayerProperty.NAME));
377
+ }
378
+ return null;
379
+ }
380
+ /**
381
+ *
382
+ * @returns
383
+ * @public
384
+ */
385
+ getGeoserver() {
386
+ return this.get(BaseLayerProperty.GEOSERVER);
387
+ }
388
+ /**
389
+ *
390
+ * @returns
391
+ * @public
392
+ */
393
+ getDescribeFeatureType() {
394
+ return this.get(BaseLayerProperty.DESCRIBEFEATURETYPE);
395
+ }
396
+ }
397
+ var BaseLayerProperty;
398
+ (function (BaseLayerProperty) {
399
+ BaseLayerProperty["NAME"] = "name";
400
+ BaseLayerProperty["LABEL"] = "label";
401
+ BaseLayerProperty["DESCRIBEFEATURETYPE"] = "describeFeatureType";
402
+ BaseLayerProperty["ISVISIBLE"] = "isVisible";
403
+ BaseLayerProperty["GEOSERVER"] = "geoserver";
404
+ })(BaseLayerProperty || (BaseLayerProperty = {}));
405
+
406
+ /**
407
+ * Layer source to retrieve WFS features from geoservers
408
+ * https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
409
+ *
410
+ * @extends {ol/source/Vector~VectorSource}
411
+ * @param options
412
+ */
413
+ class WfsSource extends VectorSource {
414
+ urlParams = new URLSearchParams({
415
+ SERVICE: 'wfs',
416
+ REQUEST: 'GetFeature',
417
+ OUTPUTFORMAT: 'application/json',
418
+ EXCEPTIONS: 'application/json'
419
+ });
420
+ constructor(options) {
421
+ super({
422
+ ...options,
423
+ format: new GeoJSON(),
424
+ loader: async (extent, resolution, projection, success, failure) => {
425
+ try {
426
+ // If bbox, add extent to the request
427
+ if (options.strategy == bbox) {
428
+ const extentGeoServer = transformExtent(extent, projection.getCode(), options.geoServerAdvanced.projection);
429
+ // https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
430
+ // request features using a bounding box with CRS maybe different from featureTypes native CRS
431
+ this.urlParams.set('bbox', extentGeoServer.toString() +
432
+ `,${options.geoServerAdvanced.projection}`);
433
+ }
434
+ const url_fetch = options.geoserverUrl + '?' + this.urlParams.toString();
435
+ const response = await fetch(url_fetch, {
436
+ headers: options.headers,
437
+ credentials: options.credentials
438
+ });
439
+ if (!response.ok) {
440
+ throw new Error('');
441
+ }
442
+ const data = await response.json();
443
+ if (data.exceptions) {
444
+ throw new Error(parseError(data));
445
+ }
446
+ const features = this.getFormat().readFeatures(data, {
447
+ featureProjection: projection.getCode(),
448
+ dataProjection: options.geoServerAdvanced.projection
449
+ });
450
+ features.forEach((feature) => {
451
+ feature.set('_layerName_', options.name,
452
+ /* silent = */ true);
453
+ });
454
+ this.addFeatures(features);
455
+ success(features);
456
+ }
457
+ catch (err) {
458
+ this.removeLoadedExtent(extent);
459
+ showError(I18N.errors.geoserver, err, options.name);
460
+ failure();
461
+ }
462
+ }
463
+ });
464
+ this.urlParams.set('version', options.geoServerAdvanced.getFeatureVersion);
465
+ this.urlParams.set('typename', options.name);
466
+ this.urlParams.set('srsName', options.geoServerAdvanced.projection.toString());
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Layer to retrieve WFS features from geoservers
472
+ * https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
473
+ *
474
+ * @fires layerRendered
475
+ * @extends {ol/layer/Vector~VectorLayer}
476
+ * @param options
477
+ */
478
+ class WfsLayer extends Mixin(BaseLayer, (VectorLayer)) {
479
+ _loadingCount = 0;
480
+ _loadedCount = 0;
481
+ beforeTransactFeature;
482
+ constructor(options) {
483
+ super({
484
+ name: options.name,
485
+ label: options.label || options.name,
486
+ minZoom: options.minZoom,
487
+ ...options
488
+ });
489
+ if (options.beforeTransactFeature) {
490
+ this.beforeTransactFeature = options.beforeTransactFeature;
491
+ }
492
+ const geoserver = options.geoserver;
493
+ const source = new WfsSource({
494
+ name: options.name,
495
+ geoserverUrl: geoserver.getUrl(),
496
+ geoServerAdvanced: geoserver.getAdvanced(),
497
+ ...(options.strategy && { strategy: options.strategy }),
498
+ geoserverVendor: options.geoserverVendor
499
+ });
500
+ this._loadingCount = 0;
501
+ this._loadedCount = 0;
502
+ source.on('featuresloadstart', () => {
503
+ this._loadingCount++;
504
+ if (this._loadingCount === 1 && this.isVisibleByZoom()) {
505
+ showLoading();
506
+ }
507
+ });
508
+ source.on(['featuresloadend', 'featuresloaderror'], () => {
509
+ this._loadedCount++;
510
+ if (this._loadingCount === this._loadedCount) {
511
+ this._loadingCount = 0;
512
+ this._loadedCount = 0;
513
+ setTimeout(() => {
514
+ this.dispatchEvent('layerRendered');
515
+ }, 300);
516
+ }
517
+ });
518
+ this.setSource(source);
519
+ const geoserverOptions = options.geoserverVendor;
520
+ Object.keys(geoserverOptions).forEach((param) => {
521
+ source.urlParams.set(param, geoserverOptions[param]);
522
+ });
523
+ }
524
+ /**
525
+ * @public
526
+ */
527
+ refresh() {
528
+ const source = this.getSource();
529
+ // Refrescamos el wms
530
+ source.refresh();
531
+ }
532
+ /**
533
+ * Use this to update Geoserver Wms Vendors (https://docs.geoserver.org/latest/en/user/services/wms/vendor.html)
534
+ * and other arguements (https://docs.geoserver.org/stable/en/user/services/wms/reference.html#getmap)
535
+ * in all the getMap requests.
536
+ *
537
+ * Example: you can use this to change the style of the WMS, add a custom sld, set a cql_filter, etc.
538
+ *
539
+ * @public
540
+ * @param paramName
541
+ * @param value
542
+ * @param refresh
543
+ */
544
+ setCustomParam(paramName, value = null, refresh = true) {
545
+ const source = this.getSource();
546
+ source.urlParams.set(paramName, value);
547
+ if (refresh) {
548
+ this.refresh();
549
+ }
550
+ return source.urlParams;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Layer source to retrieve WMS information from geoservers
556
+ * https://docs.geoserver.org/stable/en/user/services/wms/reference.html
557
+ *
558
+ * @extends {ol/source/TieWMS~TileWMS}
559
+ * @param options
560
+ */
561
+ class WmsSource extends TileWMS {
562
+ constructor(options) {
563
+ super({
564
+ url: options.geoserverUrl,
565
+ serverType: 'geoserver',
566
+ params: {
567
+ SERVICE: 'wms',
568
+ TILED: true,
569
+ LAYERS: options.name,
570
+ EXCEPTIONS: 'application/json',
571
+ ...(options.geoserverVendor && options.geoserverVendor)
572
+ },
573
+ tileLoadFunction: async (tile, src) => {
574
+ const blobToJson = (blob) => {
575
+ return new Promise((resolve) => {
576
+ const reader = new FileReader();
577
+ reader.onloadend = () => resolve(JSON.parse(reader.result));
578
+ reader.readAsText(blob);
579
+ });
580
+ };
581
+ try {
582
+ const response = await fetch(src, {
583
+ headers: options.headers,
584
+ credentials: options.credentials
585
+ });
586
+ if (!response.ok) {
587
+ throw new Error('');
588
+ }
589
+ let data = await response.blob();
590
+ // Check if the response has an error
591
+ if (data.type == 'application/json') {
592
+ const parsedError = await blobToJson(data);
593
+ throw new Error(parseError(parsedError));
594
+ }
595
+ tile.getImage().src =
596
+ URL.createObjectURL(data);
597
+ tile.setState(TileState.LOADED);
598
+ }
599
+ catch (err) {
600
+ showError(I18N.errors.geoserver, err, options.name);
601
+ tile.setState(TileState.ERROR);
602
+ }
603
+ },
604
+ ...options
605
+ });
606
+ }
607
+ }
608
+
609
+ /**
610
+ * Layer to retrieve WMS information from geoservers
611
+ * https://docs.geoserver.org/stable/en/user/services/wms/reference.html
612
+ *
613
+ * @fires layerRendered
614
+ * @extends {ol/layer/Tile~TileLayer}
615
+ * @param options
616
+ */
617
+ class WmsLayer extends Mixin(BaseLayer, (TileLayer)) {
618
+ _loadingCount = 0;
619
+ _loadedCount = 0;
620
+ beforeTransactFeature;
621
+ // Formats
622
+ _formatGeoJSON;
623
+ constructor(options) {
624
+ super({
625
+ name: options.name,
626
+ label: options.label || options.name,
627
+ minZoom: options.minZoom,
628
+ ...options
629
+ });
630
+ if (options.beforeTransactFeature) {
631
+ this.beforeTransactFeature = options.beforeTransactFeature;
632
+ }
633
+ this._formatGeoJSON = new GeoJSON$1();
634
+ const geoserver = options.geoserver;
635
+ const source = new WmsSource({
636
+ name: options.name,
637
+ geoserverUrl: geoserver.getUrl(),
638
+ geoServerAdvanced: geoserver.getAdvanced(),
639
+ geoserverVendor: options.geoserverVendor
640
+ });
641
+ this._loadingCount = 0;
642
+ this._loadedCount = 0;
643
+ source.on('tileloadstart', () => {
644
+ this._loadingCount++;
645
+ if (this._loadingCount === 1 && this.isVisibleByZoom()) {
646
+ showLoading();
647
+ }
648
+ });
649
+ source.on(['tileloadend', 'tileloaderror'], () => {
650
+ this._loadedCount++;
651
+ if (this._loadingCount === this._loadedCount) {
652
+ this._loadingCount = 0;
653
+ this._loadedCount = 0;
654
+ setTimeout(() => {
655
+ this.dispatchEvent('layerRendered');
656
+ }, 300);
657
+ }
658
+ });
659
+ this.setSource(source);
660
+ }
661
+ /**
662
+ * Get the features on the click area
663
+ * @param evt
664
+ * @returns
665
+ * @private
666
+ */
667
+ async _getFeaturesByClickEvent(evt) {
668
+ const coordinate = evt.coordinate;
669
+ const view = getMap().getView();
670
+ // Si la vista es lejana, disminumos el buffer
671
+ // Si es cercana, lo aumentamos, por ejemplo, para podeer clickear los vectores
672
+ // y mejorar la sensibilidad en IOS
673
+ const buffer = view.getZoom() > 10 ? 10 : 5;
674
+ const source = this.getSource();
675
+ // Fallback to support a bad name
676
+ // https://openlayers.org/en/v5.3.0/apidoc/module-ol_source_ImageWMS-ImageWMS.html#getGetFeatureInfoUrl
677
+ const fallbackOl5 = 'getFeatureInfoUrl' in source
678
+ ? 'getFeatureInfoUrl'
679
+ : 'getGetFeatureInfoUrl';
680
+ const url = source[fallbackOl5](coordinate, view.getResolution(), view.getProjection().getCode(), {
681
+ INFO_FORMAT: 'application/json',
682
+ BUFFER: buffer,
683
+ FEATURE_COUNT: 1,
684
+ EXCEPTIONS: 'application/json'
685
+ });
686
+ const geoserver = this.getGeoserver();
687
+ try {
688
+ const response = await fetch(url, {
689
+ headers: geoserver.getHeaders(),
690
+ credentials: geoserver.getCredentials()
691
+ });
692
+ if (!response.ok) {
693
+ throw new Error(`${I18N.errors.getFeatures} ${response.status}`);
694
+ }
695
+ const data = await response.json();
696
+ const features = this._formatGeoJSON.readFeatures(data);
697
+ return features;
698
+ }
699
+ catch (err) {
700
+ showError(err.message, err);
701
+ }
702
+ }
703
+ /**
704
+ * @public
705
+ */
706
+ refresh() {
707
+ const source = this.getSource();
708
+ // Refrescamos el wms
709
+ source.refresh();
710
+ // Force refresh the tiles
711
+ const params = source.getParams();
712
+ params.t = new Date().getMilliseconds();
713
+ source.updateParams(params);
714
+ }
715
+ /**
716
+ * Use this to update Geoserver Wfs Vendors (https://docs.geoserver.org/latest/en/user/services/wfs/vendor.html)
717
+ * and other arguements (https://docs.geoserver.org/stable/en/user/services/wfs/reference.html)
718
+ * in all the getFeature requests.
719
+ *
720
+ * Example: you can use this to set a cql_filter, limit the numbers of features, etc.
721
+ *
722
+ * @public
723
+ * @param paramName
724
+ * @param value
725
+ * @param refresh
726
+ */
727
+ setCustomParam(paramName, value = null, refresh = true) {
728
+ const source = this.getSource();
729
+ source.updateParams({
730
+ [paramName]: value
731
+ });
732
+ if (refresh) {
733
+ this.refresh();
734
+ }
735
+ return source.getParams();
736
+ }
737
+ }
738
+
739
+ function createElement(tagName, attrs = {}, ...children) {
740
+ if (typeof tagName === 'function')
741
+ return tagName(attrs, children);
742
+ const elem = tagName === null
743
+ ? new DocumentFragment()
744
+ : document.createElement(tagName);
745
+ Object.entries(attrs || {}).forEach(([name, value]) => {
746
+ if (typeof value !== undefined &&
747
+ value !== null &&
748
+ value !== undefined) {
749
+ if (name.startsWith('on') && name.toLowerCase() in window)
750
+ elem.addEventListener(name.toLowerCase().substr(2), value);
751
+ else {
752
+ if (name === 'className')
753
+ elem.setAttribute('class', value.toString());
754
+ else if (name === 'htmlFor')
755
+ elem.setAttribute('for', value.toString());
756
+ else
757
+ elem.setAttribute(name, value.toString());
758
+ }
759
+ }
760
+ });
761
+ for (const child of children) {
762
+ if (!child)
763
+ continue;
764
+ if (Array.isArray(child))
765
+ elem.append(...child);
766
+ else {
767
+ if (child.nodeType === undefined)
768
+ elem.innerHTML += child;
769
+ else
770
+ elem.appendChild(child);
771
+ }
772
+ }
773
+ return elem;
774
+ }
775
+
776
+ var img$5 = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='512' height='512' viewBox='0 0 512 512'%3e%3cpath d='M240 352h-240v128h480v-128h-240zM448 416h-64v-32h64v32zM112 160l128-128 128 128h-80v160h-96v-160z'%3e%3c/path%3e%3c/svg%3e";
777
+
778
+ var img$4 = "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";
779
+
780
+ var img$3 = "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='M384 288q39 0 67.5 28.5t28.5 67.5-28.5 67.5-67.5 28.5-67.5-28.5-28.5-67.5 28.5-67.5 67.5-28.5zM384 544.5q66 0 113.25-47.25t47.25-113.25-47.25-113.25-113.25-47.25-113.25 47.25-47.25 113.25 47.25 113.25 113.25 47.25zM384 144q118.5 0 214.5 66t138 174q-42 108-138 174t-214.5 66-214.5-66-138-174q42-108 138-174t214.5-66z'%3e%3c/path%3e%3c/svg%3e";
781
+
782
+ var img$2 = "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";
783
+
784
+ /**
785
+ * Removes in the DOM the class of the tools
786
+ * @private
787
+ */
788
+ const resetStateButtons = () => {
789
+ const activeBtn = document.querySelector('.ol-wfst--tools-control-btn.wfst--active');
790
+ if (activeBtn) {
791
+ activeBtn.classList.remove('wfst--active');
792
+ }
793
+ };
794
+ const activateModeButtons = () => {
795
+ const btn = document.querySelector('.ol-wfst--tools-control-btn-edit');
796
+ if (btn) {
797
+ btn.classList.add('wfst--active');
798
+ }
799
+ };
800
+ const activateDrawButton = () => {
801
+ const btn = document.querySelector('.ol-wfst--tools-control-btn-draw');
802
+ if (btn) {
803
+ btn.classList.add('wfst--active');
804
+ }
805
+ };
806
+ class LayersControl extends Observable {
807
+ _uploads;
808
+ _uploadFormats;
809
+ constructor(uploads, uploadFormats) {
810
+ super();
811
+ this._uploads = uploads;
812
+ this._uploadFormats = uploadFormats;
813
+ }
814
+ /**
815
+ *
816
+ * @param layer
817
+ * @public
818
+ */
819
+ addLayerEl(layer) {
820
+ const container = document.querySelector('.wfst--tools-control--select-layers');
821
+ const layerName = layer.get(BaseLayerProperty.NAME);
822
+ const checked = layer === getActiveLayerToInsertEls() ? { checked: 'checked' } : {};
823
+ const input = (createElement("input", { value: layerName, id: `wfst--${layerName}`, type: "radio", className: "ol-wfst--tools-control-input", name: "wfst--select-layer", ...checked, onchange: (evt) => this._layerChangeHandler(evt, layer) }));
824
+ const layerDom = (createElement("div", { className: `wfst--layer-control
825
+ ${layer.getVisible() ? 'ol-wfst--visible-on' : ''}
826
+ ${layer === getActiveLayerToInsertEls()
827
+ ? 'ol-wfst--selected-on'
828
+ : ''}`, "data-layer": layerName },
829
+ createElement("div", { className: "ol-wfst--tools-control-visible" },
830
+ createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on", title: I18N.labels.toggleVisibility, onclick: (evt) => this._visibilityClickHandler(evt) },
831
+ createElement("img", { src: img$3 })),
832
+ createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off", title: I18N.labels.toggleVisibility, onclick: (evt) => this._visibilityClickHandler(evt) },
833
+ createElement("img", { src: img$2 }))),
834
+ createElement("label", { htmlFor: `wfst--${layerName}` },
835
+ input,
836
+ createElement("span", { title: layer.getDescribeFeatureType()._parsed.geomType }, layer.get(BaseLayerProperty.LABEL)))));
837
+ container.appendChild(layerDom);
838
+ if (layer === getActiveLayerToInsertEls()) {
839
+ input.dispatchEvent(new Event('change'));
840
+ }
841
+ return layerDom;
842
+ }
843
+ /**
844
+ * Update geom Types availibles to select for this layer
845
+ *
846
+ * @param layerName
847
+ * @param geomDrawTypeSelected
848
+ * @private
849
+ */
850
+ _changeStateSelect(layer, geomDrawTypeSelected = null) {
851
+ /**
852
+ * Set the geometry type in the select according to the geometry of
853
+ * the layer in the geoserver and disable what does not correspond.
854
+ *
855
+ * @param value
856
+ * @param options
857
+ * @private
858
+ */
859
+ const setSelectState = (value, options) => {
860
+ Array.from(selectDraw.options).forEach((option) => {
861
+ option.selected = option.value === value ? true : false;
862
+ option.disabled =
863
+ options === 'all'
864
+ ? false
865
+ : options.includes(option.value)
866
+ ? false
867
+ : true;
868
+ option.title = option.disabled
869
+ ? I18N.labels.geomTypeNotSupported
870
+ : '';
871
+ });
872
+ };
873
+ const selectDraw = document.querySelector('.wfst--tools-control--select-draw');
874
+ let drawType;
875
+ if (selectDraw) {
876
+ const geomLayer = layer.getDescribeFeatureType()._parsed.geomType;
877
+ if (geomDrawTypeSelected) {
878
+ drawType = selectDraw.value;
879
+ }
880
+ else {
881
+ if (geomLayer === GeometryType.GeometryCollection) {
882
+ drawType = GeometryType.LineString; // Default drawing type for GeometryCollection
883
+ setSelectState(drawType, 'all');
884
+ }
885
+ else if (geomLayer === GeometryType.LinearRing) {
886
+ drawType = GeometryType.LineString; // Default drawing type for GeometryCollection
887
+ setSelectState(drawType, [
888
+ GeometryType.Circle,
889
+ GeometryType.LinearRing,
890
+ GeometryType.Polygon
891
+ ]);
892
+ selectDraw.value = drawType;
893
+ }
894
+ else {
895
+ drawType = geomLayer;
896
+ setSelectState(drawType, [geomLayer]);
897
+ }
898
+ }
899
+ }
900
+ return drawType;
901
+ }
902
+ _visibilityClickHandler(evt) {
903
+ const btn = evt.currentTarget;
904
+ const parentDiv = btn.closest('.wfst--layer-control');
905
+ const layerName = parentDiv.dataset['layer'];
906
+ parentDiv.classList.toggle('ol-wfst--visible-on');
907
+ const layer = getStoredMapLayers()[layerName];
908
+ if (parentDiv.classList.contains('ol-wfst--visible-on')) {
909
+ layer.setVisible(true);
910
+ }
911
+ else {
912
+ layer.setVisible(false);
913
+ }
914
+ }
915
+ _layerChangeHandler(evt, layer) {
916
+ const radioInput = evt.currentTarget;
917
+ const parentDiv = radioInput.closest('.wfst--layer-control');
918
+ // Deselect DOM previous layer
919
+ const selected = document.querySelector('.ol-wfst--selected-on');
920
+ if (selected)
921
+ selected.classList.remove('ol-wfst--selected-on');
922
+ // Select this layer
923
+ parentDiv.classList.add('ol-wfst--selected-on');
924
+ setActiveLayerToInsertEls(layer);
925
+ this._changeStateSelect(layer);
926
+ }
927
+ render() {
928
+ return (createElement(null, null,
929
+ createElement("div", { className: "wfst--tools-control--head" },
930
+ this._uploads && (createElement("div", null,
931
+ createElement("input", { id: "ol-wfst--upload", type: "file", accept: this._uploadFormats, onchange: (evt) => this._uploads.process(evt) }),
932
+ createElement("label", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-upload", htmlFor: "ol-wfst--upload", title: I18N.labels.uploadToLayer },
933
+ createElement("img", { src: img$5 })))),
934
+ createElement("div", { className: "ol-wfst--tools-control-draw-cnt" },
935
+ createElement("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-draw", type: "button", title: I18N.labels.addElement, onclick: () => {
936
+ this.dispatchEvent('drawMode');
937
+ } },
938
+ createElement("img", { src: img$4 })),
939
+ createElement("select", { title: I18N.labels.selectDrawType, className: "wfst--tools-control--select-draw", onchange: (evt) => {
940
+ const selectedValue = evt.target
941
+ .value;
942
+ this._changeStateSelect(getActiveLayerToInsertEls(), selectedValue);
943
+ this.dispatchEvent('changeGeom');
944
+ } }, [
945
+ GeometryType.Point,
946
+ GeometryType.MultiPoint,
947
+ GeometryType.LineString,
948
+ GeometryType.MultiLineString,
949
+ GeometryType.Polygon,
950
+ GeometryType.MultiPolygon,
951
+ GeometryType.Circle
952
+ ].map((type) => {
953
+ // Show all options, but enable only the accepted ones
954
+ return createElement("option", { value: type }, type);
955
+ })))),
956
+ createElement("div", { className: "wfst--tools-control--select-layers" })));
957
+ }
958
+ }
959
+
960
+ let editLayer = new VectorLayer({
961
+ source: new VectorSource(),
962
+ zIndex: 100
963
+ });
964
+ const getEditLayer = () => {
965
+ return editLayer;
966
+ };
967
+
968
+ // Ol
969
+ class Uploads extends Observable {
970
+ _options;
971
+ // Formats
972
+ _formatWFS;
973
+ _formatGeoJSON;
974
+ _formatKml;
975
+ _xs;
976
+ _processUpload;
977
+ constructor(options) {
978
+ super();
979
+ this._options = options;
980
+ this._processUpload = options.processUpload;
981
+ // Formats
982
+ this._formatWFS = new WFS();
983
+ this._formatGeoJSON = new GeoJSON$1();
984
+ this._formatKml = new KML({
985
+ extractStyles: false,
986
+ showPointNames: false
987
+ });
988
+ this._xs = new XMLSerializer();
989
+ }
990
+ /**
991
+ * Parse and check geometry of uploaded files
992
+ *
993
+ * @param evt
994
+ * @public
995
+ */
996
+ async process(evt) {
997
+ const map = getMap();
998
+ const view = map.getView();
999
+ const file = evt.target.files[0];
1000
+ let features;
1001
+ if (!file) {
1002
+ return;
1003
+ }
1004
+ const extension = file.name.split('.').pop().toLowerCase();
1005
+ try {
1006
+ // If the user uses a custom fucntion...
1007
+ if (this._processUpload) {
1008
+ features = this._processUpload(file);
1009
+ }
1010
+ // If the user functions return features, we dont process anything more
1011
+ if (!features) {
1012
+ const string = await this._fileReader(file);
1013
+ if (extension === 'geojson' || extension === 'json') {
1014
+ features = this._formatGeoJSON.readFeatures(string, {
1015
+ featureProjection: view.getProjection().getCode()
1016
+ });
1017
+ }
1018
+ else if (extension === 'kml') {
1019
+ features = this._formatKml.readFeatures(string, {
1020
+ featureProjection: view.getProjection().getCode()
1021
+ });
1022
+ }
1023
+ else {
1024
+ showError(I18N.errors.badFormat);
1025
+ }
1026
+ }
1027
+ let invalidFeaturesCount = 0;
1028
+ let validFeaturesCount = 0;
1029
+ const featuresToInsert = [];
1030
+ for (let feature of features) {
1031
+ // If the geometry doesn't correspond to the layer, try to fixit.
1032
+ // If we can't, don't use it
1033
+ if (!this._checkGeometry(feature)) {
1034
+ feature = this._fixGeometry(feature);
1035
+ }
1036
+ if (feature) {
1037
+ featuresToInsert.push(feature);
1038
+ validFeaturesCount++;
1039
+ }
1040
+ else {
1041
+ invalidFeaturesCount++;
1042
+ continue;
1043
+ }
1044
+ }
1045
+ if (!validFeaturesCount) {
1046
+ showError(I18N.errors.noValidGeometry);
1047
+ }
1048
+ else {
1049
+ resetStateButtons();
1050
+ this.dispatchEvent(new VectorSourceEvent('loadedFeatures', null, featuresToInsert));
1051
+ const content = `
1052
+ ${I18N.labels.validFeatures}: ${validFeaturesCount}<br>
1053
+ ${invalidFeaturesCount
1054
+ ? `${I18N.labels.invalidFeatures}: ${invalidFeaturesCount}`
1055
+ : ''}`;
1056
+ this._initModal(content, featuresToInsert);
1057
+ }
1058
+ // Reset the input to allow another onChange trigger
1059
+ evt.target.value = null;
1060
+ }
1061
+ catch (err) {
1062
+ showError(I18N.errors.badFile, err);
1063
+ }
1064
+ }
1065
+ /**
1066
+ * Read data file
1067
+ * @param file
1068
+ * @public
1069
+ */
1070
+ async _fileReader(file) {
1071
+ return new Promise((resolve, reject) => {
1072
+ const reader = new FileReader();
1073
+ reader.addEventListener('load', async (e) => {
1074
+ const fileData = e.target.result;
1075
+ resolve(fileData);
1076
+ });
1077
+ reader.addEventListener('error', (err) => {
1078
+ console.error('Error' + err);
1079
+ reject();
1080
+ });
1081
+ reader.readAsText(file);
1082
+ });
1083
+ }
1084
+ /**
1085
+ * Attemp to change the geometry feature to the layer
1086
+ * @param feature
1087
+ * @private
1088
+ */
1089
+ _fixGeometry(feature) {
1090
+ // Geometry of the layer
1091
+ const geomTypeLayer = getActiveLayerToInsertEls().getDescribeFeatureType()._parsed
1092
+ .geomType;
1093
+ const geomTypeFeature = feature.getGeometry().getType();
1094
+ let geom;
1095
+ switch (geomTypeFeature) {
1096
+ case GeometryType.Point: {
1097
+ if (geomTypeLayer === GeometryType.MultiPoint) {
1098
+ const coords = feature.getGeometry().getCoordinates();
1099
+ geom = new MultiPoint([coords]);
1100
+ }
1101
+ break;
1102
+ }
1103
+ case GeometryType.LineString:
1104
+ if (geomTypeLayer === GeometryType.MultiLineString) {
1105
+ const coords = feature.getGeometry().getCoordinates();
1106
+ geom = new MultiLineString([coords]);
1107
+ }
1108
+ break;
1109
+ case GeometryType.Polygon:
1110
+ if (geomTypeLayer === GeometryType.MultiPolygon) {
1111
+ const coords = feature.getGeometry().getCoordinates();
1112
+ geom = new MultiPolygon([coords]);
1113
+ }
1114
+ break;
1115
+ default:
1116
+ geom = null;
1117
+ }
1118
+ if (!geom) {
1119
+ return null;
1120
+ }
1121
+ feature.setGeometry(geom);
1122
+ return feature;
1123
+ }
1124
+ /**
1125
+ * Check if the feature has the same geometry as the target layer
1126
+ * @param feature
1127
+ * @private
1128
+ */
1129
+ _checkGeometry(feature) {
1130
+ // Geometry of the layer
1131
+ const geomTypeLayer = getActiveLayerToInsertEls().getDescribeFeatureType()._parsed
1132
+ .geomType;
1133
+ const geomTypeFeature = feature.getGeometry().getType();
1134
+ // This geom accepts every type of geometry
1135
+ if (geomTypeLayer === GeometryType.GeometryCollection) {
1136
+ return true;
1137
+ }
1138
+ return geomTypeFeature === geomTypeLayer;
1139
+ }
1140
+ /**
1141
+ * Confirm modal before transact to the GeoServer the features in the file
1142
+ *
1143
+ * @param content
1144
+ * @param featuresToInsert
1145
+ * @private
1146
+ */
1147
+ _initModal(content, featuresToInsert) {
1148
+ const footer = `
1149
+ <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
1150
+ ${I18N.labels.cancel}
1151
+ </button>
1152
+ <button type="button" class="btn btn-sm btn-primary" data-action="save" data-dismiss="modal">
1153
+ ${I18N.labels.upload}
1154
+ </button>
1155
+ `;
1156
+ const modal = new Modal({
1157
+ ...this._options.modal,
1158
+ header: true,
1159
+ headerClose: false,
1160
+ title: I18N.labels.uploadFeatures +
1161
+ ' ' +
1162
+ getActiveLayerToInsertEls().get(BaseLayerProperty.NAME),
1163
+ content: content,
1164
+ backdrop: 'static',
1165
+ footer: footer
1166
+ }).show();
1167
+ modal.on('dismiss', (modal, event) => {
1168
+ // On saving changes
1169
+ if (event.target.dataset.action === 'save') {
1170
+ this.dispatchEvent(new VectorSourceEvent('addedFeatures', null, featuresToInsert));
1171
+ }
1172
+ else {
1173
+ // On cancel button
1174
+ getEditLayer().getSource().clear();
1175
+ }
1176
+ });
1177
+ }
1178
+ }
1179
+
1180
+ /**
1181
+ * @param target
1182
+ * @param sources
1183
+ * @returns
1184
+ */
1185
+ const deepObjectAssign = (target, ...sources) => {
1186
+ sources.forEach((source) => {
1187
+ Object.keys(source).forEach((key) => {
1188
+ const s_val = source[key];
1189
+ const t_val = target[key];
1190
+ target[key] =
1191
+ t_val &&
1192
+ s_val &&
1193
+ typeof t_val === 'object' &&
1194
+ typeof s_val === 'object' &&
1195
+ !Array.isArray(t_val) // Don't merge arrays
1196
+ ? deepObjectAssign(t_val, s_val)
1197
+ : s_val;
1198
+ });
1199
+ });
1200
+ return target;
1201
+ };
1202
+
1203
+ const DEFAULT_LANGUAGE = 'en';
1204
+ const getDefaultOptions = () => {
1205
+ return {
1206
+ layers: null,
1207
+ evtType: 'singleclick',
1208
+ active: true,
1209
+ showControl: true,
1210
+ language: DEFAULT_LANGUAGE,
1211
+ uploadFormats: '.geojson,.json,.kml',
1212
+ processUpload: null,
1213
+ modal: {
1214
+ animateClass: 'fade',
1215
+ animateInClass: 'show',
1216
+ transition: 300,
1217
+ backdropTransition: 150,
1218
+ templates: {
1219
+ dialog: '<div class="modal-dialog modal-dialog-centered"></div>',
1220
+ headerClose: `<button type="button" class="btn-close" data-dismiss="modal" aria-label="${I18N.labels.close}"><span aria-hidden="true">×</span></button>`
1221
+ }
1222
+ }
1223
+ };
1224
+ };
1225
+
1226
+ class EditControlChangesEl extends Control {
1227
+ constructor(feature) {
1228
+ super({
1229
+ element: (createElement("div", { className: "ol-wfst--changes-control" },
1230
+ createElement("div", { className: "ol-wfst--changes-control-el" },
1231
+ createElement("div", { className: "ol-wfst--changes-control-id" },
1232
+ createElement("b", null, I18N.labels.editMode),
1233
+ " -",
1234
+ ' ',
1235
+ createElement("i", null, String(feature.getId()))),
1236
+ createElement("button", { type: "button", className: "btn btn-sm btn-secondary", onclick: () => {
1237
+ this.dispatchEvent(new VectorSourceEvent('cancel', feature));
1238
+ } }, I18N.labels.cancel),
1239
+ createElement("button", { type: "button", className: "btn btn-sm btn-primary", onclick: () => {
1240
+ this.dispatchEvent(new VectorSourceEvent('apply', feature));
1241
+ } }, I18N.labels.apply),
1242
+ createElement("button", { type: "button", className: "btn btn-sm btn-danger-outline", onclick: () => {
1243
+ this.dispatchEvent(new VectorSourceEvent('delete', feature));
1244
+ } }, I18N.labels.delete))))
1245
+ });
1246
+ }
1247
+ }
1248
+
1249
+ // Ol
1250
+ /**
1251
+ * Master style that handles two modes on the Edit Layer:
1252
+ * - one is the basic, showing only the vertices
1253
+ * - and the other when modify is active, showing bigger vertices
1254
+ *
1255
+ * @param feature
1256
+ * @private
1257
+ */
1258
+ function styleFunction(feature) {
1259
+ const getVertexs = (feature) => {
1260
+ let geometry = feature.getGeometry();
1261
+ if (geometry instanceof GeometryCollection) {
1262
+ geometry = geometry.getGeometries()[0];
1263
+ }
1264
+ const coordinates = geometry.getCoordinates();
1265
+ let flatCoordinates = null;
1266
+ if (geometry instanceof Polygon ||
1267
+ geometry instanceof MultiLineString) {
1268
+ flatCoordinates = coordinates.flat(1);
1269
+ }
1270
+ else if (geometry instanceof MultiPolygon) {
1271
+ flatCoordinates = coordinates.flat(2);
1272
+ }
1273
+ else {
1274
+ flatCoordinates = coordinates;
1275
+ }
1276
+ if (!flatCoordinates || !flatCoordinates.length) {
1277
+ return;
1278
+ }
1279
+ return new MultiPoint(flatCoordinates);
1280
+ };
1281
+ let geometry = feature.getGeometry();
1282
+ let type = geometry.getType();
1283
+ if (geometry instanceof GeometryCollection) {
1284
+ geometry = geometry.getGeometries()[0];
1285
+ type = geometry.getType();
1286
+ }
1287
+ switch (type) {
1288
+ case GeometryType.Point:
1289
+ case GeometryType.MultiPoint:
1290
+ if (getMode() === Modes.Edit) {
1291
+ return [
1292
+ new Style({
1293
+ image: new Circle({
1294
+ radius: 6,
1295
+ fill: new Fill({
1296
+ color: '#000000'
1297
+ })
1298
+ })
1299
+ }),
1300
+ new Style({
1301
+ image: new Circle({
1302
+ radius: 4,
1303
+ fill: new Fill({
1304
+ color: '#ff0000'
1305
+ })
1306
+ })
1307
+ })
1308
+ ];
1309
+ }
1310
+ else {
1311
+ return [
1312
+ new Style({
1313
+ image: new Circle({
1314
+ radius: 5,
1315
+ fill: new Fill({
1316
+ color: '#ff0000'
1317
+ })
1318
+ })
1319
+ }),
1320
+ new Style({
1321
+ image: new Circle({
1322
+ radius: 2,
1323
+ fill: new Fill({
1324
+ color: '#000000'
1325
+ })
1326
+ })
1327
+ })
1328
+ ];
1329
+ }
1330
+ default:
1331
+ // If editing mode is active, show bigger vertex
1332
+ if (getMode() == Modes.Draw || getMode() == Modes.Edit) {
1333
+ return [
1334
+ new Style({
1335
+ stroke: new Stroke({
1336
+ color: 'rgba( 255, 0, 0, 1)',
1337
+ width: 4
1338
+ }),
1339
+ fill: new Fill({
1340
+ color: 'rgba(255, 0, 0, 0.7)'
1341
+ })
1342
+ }),
1343
+ new Style({
1344
+ image: new Circle({
1345
+ radius: 4,
1346
+ fill: new Fill({
1347
+ color: '#ff0000'
1348
+ }),
1349
+ stroke: new Stroke({
1350
+ width: 2,
1351
+ color: 'rgba(5, 5, 5, 0.9)'
1352
+ })
1353
+ }),
1354
+ geometry: (feature) => getVertexs(feature)
1355
+ }),
1356
+ new Style({
1357
+ stroke: new Stroke({
1358
+ color: 'rgba(255, 255, 255, 0.7)',
1359
+ width: 2
1360
+ })
1361
+ })
1362
+ ];
1363
+ }
1364
+ else {
1365
+ return [
1366
+ new Style({
1367
+ image: new Circle({
1368
+ radius: 2,
1369
+ fill: new Fill({
1370
+ color: '#000000'
1371
+ })
1372
+ }),
1373
+ geometry: (feature) => getVertexs(feature)
1374
+ }),
1375
+ new Style({
1376
+ stroke: new Stroke({
1377
+ color: '#ff0000',
1378
+ width: 4
1379
+ }),
1380
+ fill: new Fill({
1381
+ color: 'rgba(255, 0, 0, 0.7)'
1382
+ })
1383
+ })
1384
+ ];
1385
+ }
1386
+ }
1387
+ }
1388
+
1389
+ // Ol
1390
+ /**
1391
+ * Shows a fields form in a modal window to allow changes in the properties of the feature.
1392
+ *
1393
+ * @param feature
1394
+ * @private
1395
+ */
1396
+ class EditFieldsModal extends Observable {
1397
+ _options;
1398
+ _modal;
1399
+ _feature;
1400
+ constructor(options) {
1401
+ super();
1402
+ this._options = options;
1403
+ const footer = `
1404
+ <button type="button" class="btn btn-sm btn-link btn-third" data-action="delete" data-dismiss="modal">
1405
+ ${I18N.labels.delete}
1406
+ </button>
1407
+ <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
1408
+ ${I18N.labels.cancel}
1409
+ </button>
1410
+ <button type="button" class="btn btn-sm btn-primary" data-action="save" data-dismiss="modal">
1411
+ ${I18N.labels.save}
1412
+ </button>
1413
+ `;
1414
+ this._modal = new Modal({
1415
+ ...this._options,
1416
+ header: true,
1417
+ headerClose: true,
1418
+ title: '',
1419
+ content: '<div></div>',
1420
+ footer: footer
1421
+ });
1422
+ this._modal.on('dismiss', (modal, event) => {
1423
+ // On saving changes
1424
+ if (event.target.dataset.action === 'save') {
1425
+ const inputs = modal.el.querySelectorAll('input');
1426
+ inputs.forEach((el) => {
1427
+ const value = el.value;
1428
+ const field = el.name;
1429
+ this._feature.set(field, value, /*isSilent = */ true);
1430
+ });
1431
+ this._feature.changed();
1432
+ addFeatureToEditedList(this._feature);
1433
+ this.dispatchEvent(new VectorSourceEvent('save', this._feature));
1434
+ }
1435
+ else if (event.target.dataset.action === 'delete') {
1436
+ this.dispatchEvent(new VectorSourceEvent('delete', this._feature));
1437
+ }
1438
+ });
1439
+ }
1440
+ show(feature) {
1441
+ this._feature = feature;
1442
+ const title = `${I18N.labels.editElement} ${feature.getId()} `;
1443
+ const properties = feature.getProperties();
1444
+ const layerName = feature.get('_layerName_');
1445
+ // Data schema from the geoserver
1446
+ const layer = getStoredLayer(layerName);
1447
+ const dataSchema = layer.getDescribeFeatureType()._parsed.properties;
1448
+ let content = '<form autocomplete="false">';
1449
+ Object.keys(properties).forEach((key) => {
1450
+ // If the feature field exists in the geoserver and is not added by openlayers
1451
+ const field = dataSchema.find((data) => data.name === key);
1452
+ if (field) {
1453
+ const typeXsd = field.type;
1454
+ let type;
1455
+ switch (typeXsd) {
1456
+ case 'xsd:string':
1457
+ type = 'text';
1458
+ break;
1459
+ case 'xsd:number':
1460
+ case 'xsd:int':
1461
+ type = 'number';
1462
+ break;
1463
+ case 'xsd:date-time':
1464
+ type = 'datetime';
1465
+ break;
1466
+ default:
1467
+ type = 'text';
1468
+ }
1469
+ if (type) {
1470
+ content += `
1471
+ <div class="ol-wfst--input-field-container">
1472
+ <label class="ol-wfst--input-field-label" for="${key}">${key}</label>
1473
+ <input placeholder="NULL" class="ol-wfst--input-field-input" type="${type}" name="${key}" value="${properties[key] || ''}">
1474
+ </div>`;
1475
+ }
1476
+ }
1477
+ });
1478
+ content += '</form>';
1479
+ this._modal._html.body.innerHTML = content;
1480
+ this._modal._html.header.innerHTML = title;
1481
+ this._modal.show();
1482
+ }
1483
+ }
1484
+
1485
+ // Ol
1486
+ // https://docs.geoserver.org/latest/en/user/services/wfs/axis_order.html
1487
+ // Axis ordering: latitude/longitude
1488
+ const DEFAULT_GEOSERVER_SRS = 'EPSG:3857';
1489
+ /**
1490
+ * @fires change:capabilities
1491
+ * @extends {ol/Object~BaseObject}
1492
+ * @param options
1493
+ */
1494
+ class Geoserver extends BaseObject {
1495
+ _options;
1496
+ _countRequests;
1497
+ _insertFeatures;
1498
+ _updateFeatures;
1499
+ _deleteFeatures;
1500
+ // Formats
1501
+ _formatWFS;
1502
+ _formatGeoJSON;
1503
+ _formatKml;
1504
+ _xs;
1505
+ state_;
1506
+ constructor(options) {
1507
+ super();
1508
+ const defaults = {
1509
+ url: null,
1510
+ advanced: {
1511
+ getCapabilitiesVersion: '1.3.0',
1512
+ getFeatureVersion: '1.0.0',
1513
+ describeFeatureTypeVersion: '1.1.0',
1514
+ lockFeatureVersion: '1.1.0',
1515
+ wfsTransactionVersion: '1.1.0',
1516
+ projection: DEFAULT_GEOSERVER_SRS,
1517
+ lockFeatureParams: {
1518
+ expiry: 5,
1519
+ lockId: 'GeoServer',
1520
+ releaseAction: 'SOME'
1521
+ }
1522
+ },
1523
+ headers: {},
1524
+ credentials: 'same-origin',
1525
+ useLockFeature: true
1526
+ };
1527
+ this._options = deepObjectAssign(defaults, options);
1528
+ this.setAdvanced(this._options.advanced);
1529
+ this.setHeaders(this._options.headers);
1530
+ this.setCredentials(this._options.credentials);
1531
+ this.setUrl(this._options.url);
1532
+ this.setUseLockFeature(this._options.useLockFeature);
1533
+ this._countRequests = 0;
1534
+ this._insertFeatures = [];
1535
+ this._updateFeatures = [];
1536
+ this._deleteFeatures = [];
1537
+ // Formats
1538
+ this._formatWFS = new WFS();
1539
+ this._formatGeoJSON = new GeoJSON$1();
1540
+ this._formatKml = new KML({
1541
+ extractStyles: false,
1542
+ showPointNames: false
1543
+ });
1544
+ this._xs = new XMLSerializer();
1545
+ this.getAndUpdateCapabilities();
1546
+ this.on('change:capabilities', () => {
1547
+ this._checkGeoserverCapabilities();
1548
+ });
1549
+ }
1550
+ /**
1551
+ *
1552
+ * @returns
1553
+ * @public
1554
+ */
1555
+ getCapabilities() {
1556
+ return this.get(GeoserverProperty.CAPABILITIES);
1557
+ }
1558
+ /**
1559
+ *
1560
+ * @param url
1561
+ * @param opt_silent
1562
+ * @public
1563
+ */
1564
+ setUrl(url, opt_silent = false) {
1565
+ this.set(GeoserverProperty.URL, url, opt_silent);
1566
+ }
1567
+ /**
1568
+ *
1569
+ * @returns
1570
+ */
1571
+ getUrl() {
1572
+ return this.get(GeoserverProperty.URL);
1573
+ }
1574
+ /**
1575
+ *
1576
+ * @param headers
1577
+ * @param opt_silent
1578
+ * @returns
1579
+ * @public
1580
+ */
1581
+ setHeaders(headers = {}, opt_silent = false) {
1582
+ return this.set(GeoserverProperty.HEADERS, headers, opt_silent);
1583
+ }
1584
+ /**
1585
+ *
1586
+ * @returns
1587
+ * @public
1588
+ */
1589
+ getHeaders() {
1590
+ return this.get(GeoserverProperty.HEADERS);
1591
+ }
1592
+ /**
1593
+ *
1594
+ * @param credentials
1595
+ * @param opt_silent
1596
+ * @public
1597
+ */
1598
+ setCredentials(credentials = null, opt_silent = false) {
1599
+ this.set(GeoserverProperty.CREDENTIALS, credentials, opt_silent);
1600
+ }
1601
+ /**
1602
+ *
1603
+ * @returns
1604
+ * @public
1605
+ */
1606
+ getCredentials() {
1607
+ return this.get(GeoserverProperty.CREDENTIALS);
1608
+ }
1609
+ /**
1610
+ *
1611
+ * @returns
1612
+ * @public
1613
+ */
1614
+ setAdvanced(advanced = {}, opt_silent = false) {
1615
+ this.set(GeoserverProperty.ADVANCED, advanced, opt_silent);
1616
+ }
1617
+ /**
1618
+ *
1619
+ * @returns
1620
+ * @public
1621
+ */
1622
+ getAdvanced() {
1623
+ return this.get(GeoserverProperty.ADVANCED);
1624
+ }
1625
+ /**
1626
+ *
1627
+ * @returns
1628
+ * @public
1629
+ */
1630
+ hasTransaction() {
1631
+ return this.get(GeoserverProperty.HASTRASNACTION);
1632
+ }
1633
+ /**
1634
+ *
1635
+ * @returns
1636
+ * @public
1637
+ */
1638
+ hasLockFeature() {
1639
+ return this.get(GeoserverProperty.HASLOCKFEATURE);
1640
+ }
1641
+ /**
1642
+ *
1643
+ * @returns
1644
+ * @public
1645
+ */
1646
+ getUseLockFeature() {
1647
+ return this.get(GeoserverProperty.USELOCKFEATURE);
1648
+ }
1649
+ /**
1650
+ *
1651
+ * @returns
1652
+ * @public
1653
+ */
1654
+ setUseLockFeature(useLockFeature, opt_silent = false) {
1655
+ this.set(GeoserverProperty.USELOCKFEATURE, useLockFeature, opt_silent);
1656
+ }
1657
+ /**
1658
+ *
1659
+ * @returns
1660
+ * @public
1661
+ */
1662
+ isLoaded() {
1663
+ return this.get(GeoserverProperty.ISLOADED);
1664
+ }
1665
+ /**
1666
+ *
1667
+ * @returns
1668
+ */
1669
+ getState() {
1670
+ return this.state_;
1671
+ }
1672
+ /**
1673
+ * Get the capabilities from the GeoServer and check
1674
+ * all the available operations.
1675
+ *
1676
+ * @fires getcapabilities
1677
+ * @public
1678
+ */
1679
+ async getAndUpdateCapabilities() {
1680
+ try {
1681
+ const params = new URLSearchParams({
1682
+ service: 'wfs',
1683
+ version: this.getAdvanced().getCapabilitiesVersion,
1684
+ request: 'GetCapabilities',
1685
+ exceptions: 'application/json'
1686
+ });
1687
+ const url_fetch = this.getUrl() + '?' + params.toString();
1688
+ const response = await fetch(url_fetch, {
1689
+ headers: this.getHeaders(),
1690
+ credentials: this.getCredentials()
1691
+ });
1692
+ if (!response.ok) {
1693
+ throw new Error('');
1694
+ }
1695
+ const data = await response.text();
1696
+ const capabilities = new window.DOMParser().parseFromString(data, 'text/xml');
1697
+ this.set(GeoserverProperty.CAPABILITIES, capabilities);
1698
+ this.state_ = capabilities ? 'ready' : 'error';
1699
+ return capabilities;
1700
+ }
1701
+ catch (err) {
1702
+ console.error(err);
1703
+ const msg = typeof err === 'string' ? err : I18N.errors.capabilities;
1704
+ showError(msg, err);
1705
+ }
1706
+ }
1707
+ /**
1708
+ *
1709
+ * @private
1710
+ */
1711
+ _checkGeoserverCapabilities() {
1712
+ // Available operations in the geoserver
1713
+ const operations = this.getCapabilities().getElementsByTagName('ows:Operation');
1714
+ Array.from(operations).forEach((operation) => {
1715
+ if (operation.getAttribute('name') === 'Transaction') {
1716
+ this.set(GeoserverProperty.HASTRASNACTION, true);
1717
+ }
1718
+ else if (operation.getAttribute('name') === 'LockFeature') {
1719
+ this.set(GeoserverProperty.HASLOCKFEATURE, true);
1720
+ }
1721
+ else if (operation.getAttribute('name') === 'DescribeFeatureType') {
1722
+ this.set(GeoserverProperty.HASDESCRIBEFEATURETYPE, true);
1723
+ }
1724
+ });
1725
+ if (!this.hasTransaction()) {
1726
+ throw I18N.errors.wfst;
1727
+ }
1728
+ }
1729
+ /**
1730
+ * Make the WFS Transactions
1731
+ *
1732
+ * @param transactionType
1733
+ * @param features
1734
+ * @param layerName
1735
+ * @private
1736
+ */
1737
+ async transact(transactionType, features, layerName) {
1738
+ features = Array.isArray(features) ? features : [features];
1739
+ const clonedFeatures = [];
1740
+ const geoLayer = getStoredLayer(layerName);
1741
+ for (const feature of features) {
1742
+ let clone = this._cloneFeature(feature);
1743
+ const cloneGeom = clone.getGeometry();
1744
+ // Ugly fix to support GeometryCollection on GML
1745
+ // See https://github.com/openlayers/openlayers/issues/4220
1746
+ if (cloneGeom instanceof GeometryCollection) {
1747
+ this._transformGeoemtryCollectionToGeometries(clone, cloneGeom);
1748
+ }
1749
+ else if (cloneGeom instanceof Circle$1) {
1750
+ // Geoserver has no Support to Circles
1751
+ this._transformCircleToPolygon(clone, cloneGeom);
1752
+ }
1753
+ // Filters
1754
+ if ('beforeTransactFeature' in geoLayer &&
1755
+ typeof geoLayer.beforeTransactFeature === 'function') {
1756
+ clone = geoLayer.beforeTransactFeature(clone, transactionType);
1757
+ }
1758
+ if (clone) {
1759
+ clonedFeatures.push(clone);
1760
+ }
1761
+ }
1762
+ if (!clonedFeatures.length) {
1763
+ showError(I18N.errors.noValidGeometry);
1764
+ return false;
1765
+ }
1766
+ switch (transactionType) {
1767
+ case TransactionType.Insert:
1768
+ this._insertFeatures = [
1769
+ ...this._insertFeatures,
1770
+ ...clonedFeatures
1771
+ ];
1772
+ break;
1773
+ case TransactionType.Update:
1774
+ this._updateFeatures = [
1775
+ ...this._updateFeatures,
1776
+ ...clonedFeatures
1777
+ ];
1778
+ break;
1779
+ case TransactionType.Delete:
1780
+ this._deleteFeatures = [
1781
+ ...this._deleteFeatures,
1782
+ ...clonedFeatures
1783
+ ];
1784
+ break;
1785
+ }
1786
+ this._countRequests++;
1787
+ const numberRequest = this._countRequests;
1788
+ return new Promise((resolve, reject) => {
1789
+ setTimeout(async () => {
1790
+ try {
1791
+ // Prevent fire multiples times
1792
+ if (numberRequest !== this._countRequests) {
1793
+ return;
1794
+ }
1795
+ let srs = getMap().getView().getProjection().getCode();
1796
+ // Force latitude/longitude order on transactions
1797
+ // EPSG:4326 is longitude/latitude (assumption) and is not managed correctly by GML3
1798
+ srs =
1799
+ srs === 'EPSG:4326'
1800
+ ? 'urn:x-ogc:def:crs:EPSG:4326'
1801
+ : srs;
1802
+ const describeFeatureType = geoLayer.getDescribeFeatureType()._parsed;
1803
+ if (!geoLayer) {
1804
+ throw new Error(I18N.errors.layerNotFound);
1805
+ }
1806
+ const options = {
1807
+ featureNS: describeFeatureType.namespace,
1808
+ featureType: layerName,
1809
+ srsName: srs,
1810
+ featurePrefix: null,
1811
+ nativeElements: null,
1812
+ version: this.getAdvanced().wfsTransactionVersion
1813
+ };
1814
+ const transaction = this._formatWFS.writeTransaction(this._insertFeatures, this._updateFeatures, this._deleteFeatures, options);
1815
+ let payload = this._xs.serializeToString(transaction);
1816
+ const geomType = describeFeatureType.geomType;
1817
+ const geomField = describeFeatureType.geomField;
1818
+ // Ugly fix to support GeometryCollection on GML
1819
+ // See https://github.com/openlayers/openlayers/issues/4220
1820
+ if (geomType === GeometryType.GeometryCollection) {
1821
+ if (transactionType === TransactionType.Insert) {
1822
+ payload = payload.replace(/<geometry>/g, `<geometry><MultiGeometry xmlns="http://www.opengis.net/gml" srsName="${srs}"><geometryMember>`);
1823
+ payload = payload.replace(/<\/geometry>/g, `</geometryMember></MultiGeometry></geometry>`);
1824
+ }
1825
+ else if (transactionType === TransactionType.Update) {
1826
+ const gmemberIn = `<MultiGeometry xmlns="http://www.opengis.net/gml" srsName="${srs}"><geometryMember>`;
1827
+ const gmemberOut = `</geometryMember></MultiGeometry>`;
1828
+ payload = payload.replace(/(.*)(<Name>geometry<\/Name><Value>)(.*?)(<\/Value>)(.*)/g, `$1$2${gmemberIn}$3${gmemberOut}$4$5`);
1829
+ }
1830
+ }
1831
+ // Fixes geometry name, weird bug with GML:
1832
+ // The property for the geometry column is always named "geometry"
1833
+ if (transactionType === TransactionType.Insert) {
1834
+ payload = payload.replace(/<(\/?)\bgeometry\b>/g, `<$1${geomField}>`);
1835
+ }
1836
+ else {
1837
+ payload = payload.replace(/<Name>geometry<\/Name>/g, `<Name>${geomField}</Name>`);
1838
+ }
1839
+ // This has to be te same used before
1840
+ if (this.hasLockFeature &&
1841
+ this.getUseLockFeature() &&
1842
+ transactionType !== TransactionType.Insert) {
1843
+ payload = payload.replace(`</Transaction>`, `<LockId>${this._options.advanced.lockFeatureParams.lockId}</LockId></Transaction>`);
1844
+ }
1845
+ const headers = {
1846
+ 'Content-Type': 'text/xml',
1847
+ ...this.getHeaders()
1848
+ };
1849
+ const response = await fetch(this.getUrl(), {
1850
+ method: 'POST',
1851
+ body: payload,
1852
+ headers: headers,
1853
+ credentials: this._options.credentials
1854
+ });
1855
+ if (!response.ok) {
1856
+ throw new Error(I18N.errors.transaction + ' ' + response.status);
1857
+ }
1858
+ const parseResponse = this._formatWFS.readTransactionResponse(await response.text());
1859
+ if (!Object.keys(parseResponse).length) {
1860
+ const responseStr = await response.text();
1861
+ const findError = String(responseStr).match(/<ows:ExceptionText>([\s\S]*?)<\/ows:ExceptionText>/);
1862
+ if (findError) {
1863
+ throw new Error(findError[1]);
1864
+ }
1865
+ }
1866
+ if (transactionType !== TransactionType.Delete) {
1867
+ for (const feature of features) {
1868
+ getEditLayer().getSource().removeFeature(feature);
1869
+ }
1870
+ }
1871
+ const wlayer = getStoredLayer(layerName);
1872
+ wlayer.refresh();
1873
+ showLoading(false);
1874
+ this._insertFeatures = [];
1875
+ this._updateFeatures = [];
1876
+ this._deleteFeatures = [];
1877
+ this._countRequests = 0;
1878
+ resolve(parseResponse);
1879
+ }
1880
+ catch (err) {
1881
+ showError(err.message, err);
1882
+ showLoading(false);
1883
+ this._countRequests = 0;
1884
+ reject();
1885
+ }
1886
+ }, 0);
1887
+ });
1888
+ }
1889
+ /**
1890
+ *
1891
+ * @param feature
1892
+ * @param geom
1893
+ * @private
1894
+ */
1895
+ _transformCircleToPolygon(feature, geom) {
1896
+ const geomConverted = fromCircle(geom);
1897
+ feature.setGeometry(geomConverted);
1898
+ }
1899
+ /**
1900
+ *
1901
+ * @param feature
1902
+ * @private
1903
+ * @param geom
1904
+ */
1905
+ _transformGeoemtryCollectionToGeometries(feature, geom) {
1906
+ let geomConverted = geom.getGeometries()[0];
1907
+ if (geomConverted instanceof Circle$1) {
1908
+ geomConverted = fromCircle(geomConverted);
1909
+ }
1910
+ feature.setGeometry(geomConverted);
1911
+ }
1912
+ /**
1913
+ *
1914
+ * @param feature
1915
+ * @returns
1916
+ * @private
1917
+ */
1918
+ _cloneFeature(feature) {
1919
+ removeFeatureFromEditList(feature);
1920
+ const featureProperties = feature.getProperties();
1921
+ delete featureProperties.boundedBy;
1922
+ delete featureProperties._layerName_;
1923
+ const clone = new Feature(featureProperties);
1924
+ clone.setId(feature.getId());
1925
+ return clone;
1926
+ }
1927
+ /**
1928
+ * Lock a feature in the geoserver. Useful before editing a geometry,
1929
+ * to avoid changes from multiples suers
1930
+ *
1931
+ * @param featureId
1932
+ * @param layerName
1933
+ * @param retry
1934
+ * @public
1935
+ */
1936
+ async lockFeature(featureId, layerName, retry = 0) {
1937
+ const params = new URLSearchParams({
1938
+ service: 'wfs',
1939
+ version: this.getAdvanced().lockFeatureVersion,
1940
+ request: 'LockFeature',
1941
+ typeName: layerName,
1942
+ expiry: String(this._options.advanced.lockFeatureParams.expiry),
1943
+ LockId: this._options.advanced.lockFeatureParams.lockId,
1944
+ releaseAction: this._options.advanced.lockFeatureParams.releaseAction,
1945
+ exceptions: 'application/json',
1946
+ featureid: `${featureId}`
1947
+ });
1948
+ const url_fetch = this.getUrl() + '?' + params.toString();
1949
+ try {
1950
+ const response = await fetch(url_fetch, {
1951
+ headers: this._options.headers,
1952
+ credentials: this._options.credentials
1953
+ });
1954
+ if (!response.ok) {
1955
+ throw new Error(I18N.errors.lockFeature);
1956
+ }
1957
+ const data = await response.text();
1958
+ try {
1959
+ // First, check if is a JSON (with errors)
1960
+ const dataParsed = JSON.parse(data);
1961
+ if ('exceptions' in dataParsed) {
1962
+ const exceptions = dataParsed.exceptions;
1963
+ if (exceptions[0].code === 'CannotLockAllFeatures') {
1964
+ // Maybe the Feature is already blocked, ant thats trigger error, so, we try one locking more time again
1965
+ if (!retry) {
1966
+ this.lockFeature(featureId, layerName, 1);
1967
+ }
1968
+ else {
1969
+ showError(I18N.errors.lockFeature, exceptions);
1970
+ }
1971
+ }
1972
+ else {
1973
+ showError(exceptions[0].text, exceptions);
1974
+ }
1975
+ }
1976
+ }
1977
+ catch (err) {
1978
+ /*
1979
+
1980
+ let dataDoc = (new window.DOMParser()).parseFromString(data, 'text/xml');
1981
+
1982
+ let lockId = dataDoc.getElementsByTagName('wfs:LockId');
1983
+
1984
+ let featuresLocked: HTMLCollectionOf<Element> = dataDoc.getElementsByTagName('ogc:FeatureId');
1985
+
1986
+ for (let featureLocked of featuresLocked as any) {
1987
+
1988
+ console.log(featureLocked.getAttribute('fid'));
1989
+
1990
+ }
1991
+
1992
+ */
1993
+ }
1994
+ return data;
1995
+ }
1996
+ catch (err) {
1997
+ showError(err.message, err);
1998
+ }
1999
+ }
2000
+ }
2001
+ var GeoserverProperty;
2002
+ (function (GeoserverProperty) {
2003
+ GeoserverProperty["CAPABILITIES"] = "capabilities";
2004
+ GeoserverProperty["URL"] = "url";
2005
+ GeoserverProperty["HEADERS"] = "headers";
2006
+ GeoserverProperty["CREDENTIALS"] = "credentials";
2007
+ GeoserverProperty["ADVANCED"] = "advanced";
2008
+ GeoserverProperty["HASTRASNACTION"] = "hasTransaction";
2009
+ GeoserverProperty["HASLOCKFEATURE"] = "hasLockFeature";
2010
+ GeoserverProperty["HASDESCRIBEFEATURETYPE"] = "hasDescribeFeatureType";
2011
+ GeoserverProperty["USELOCKFEATURE"] = "useLockFeature";
2012
+ GeoserverProperty["ISLOADED"] = "isLoaded";
2013
+ })(GeoserverProperty || (GeoserverProperty = {}));
2014
+
2015
+ 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";
2016
+
2017
+ var img = "data:image/svg+xml,%3csvg version='1.1' xmlns='http://www.w3.org/2000/svg' width='541' height='512' viewBox='0 0 541 512'%3e %3cpath fill='black' d='M103.306 228.483l129.493-125.249c-17.662-4.272-31.226-18.148-34.98-35.663l-0.055-0.307-129.852 125.248c17.812 4.15 31.53 18.061 35.339 35.662l0.056 0.308z'%3e%3c/path%3e %3cpath fill='black' d='M459.052 393.010c-13.486-8.329-22.346-23.018-22.373-39.779v-0.004c-0.053-0.817-0.082-1.772-0.082-2.733s0.030-1.916 0.089-2.863l-0.007 0.13-149.852 71.94c9.598 8.565 15.611 20.969 15.611 34.779 0 0.014 0 0.029 0 0.043v-0.002c-0.048 5.164-0.94 10.104-2.544 14.711l0.098-0.322z'%3e%3c/path%3e %3cpath fill='black' d='M290.207 57.553c-0.009 15.55-7.606 29.324-19.289 37.819l-0.135 0.093 118.054 46.69c-0.216-1.608-0.346-3.48-0.36-5.379v-0.017c0.033-16.948 9.077-31.778 22.596-39.953l0.209-0.118-122.298-48.056c0.659 2.633 1.098 5.693 1.221 8.834l0.002 0.087z'%3e%3c/path%3e %3cpath fill='black' d='M241.36 410.132l-138.629-160.067c-4.734 17.421-18.861 30.61-36.472 33.911l-0.29 0.045 143.881 166.255c1.668-18.735 14.197-34.162 31.183-40.044l0.327-0.099z'%3e%3c/path%3e %3cpath fill='black' d='M243.446 115.105c-31.785 0-57.553-25.767-57.553-57.553s25.767-57.553 57.553-57.553c31.785 0 57.552 25.767 57.552 57.553v0c0 31.786-25.767 57.553-57.553 57.553v0zM243.446 21.582c-19.866 0-35.97 16.105-35.97 35.97s16.105 35.97 35.97 35.97c19.866 0 35.97-16.105 35.97-35.97v0c0-19.866-16.104-35.97-35.97-35.97v0z'%3e%3c/path%3e %3cpath fill='black' d='M483.224 410.78c-31.786 0-57.553-25.767-57.553-57.553s25.767-57.553 57.553-57.553c31.786 0 57.552 25.767 57.552 57.553v0c0 31.786-25.767 57.553-57.553 57.553v0zM483.224 317.257c-19.866 0-35.97 16.104-35.97 35.97s16.105 35.97 35.97 35.97c19.866 0 35.97-16.105 35.97-35.97v0c0-19.866-16.105-35.97-35.97-35.97v0z'%3e%3c/path%3e %3cpath fill='black' d='M57.553 295.531c-31.785 0-57.553-25.767-57.553-57.553s25.767-57.553 57.553-57.553c31.785 0 57.553 25.767 57.553 57.553v0c0 31.786-25.767 57.553-57.553 57.553v0zM57.553 202.008c-19.866 0-35.97 16.105-35.97 35.97s16.105 35.97 35.97 35.97c19.866 0 35.97-16.105 35.97-35.97v0c-0.041-19.835-16.13-35.898-35.97-35.898 0 0 0 0 0 0v0z'%3e%3c/path%3e %3cpath fill='black' d='M256.036 512.072c-31.786 0-57.553-25.767-57.553-57.553s25.767-57.553 57.553-57.553c31.786 0 57.553 25.767 57.553 57.553v0c0 31.786-25.767 57.553-57.553 57.553v0zM256.036 418.55c-19.866 0-35.97 16.104-35.97 35.97s16.105 35.97 35.97 35.97c19.866 0 35.97-16.105 35.97-35.97v0c0-19.866-16.105-35.97-35.97-35.97v0z'%3e%3c/path%3e %3cpath fill='black' d='M435.24 194.239c-31.786 0-57.553-25.767-57.553-57.553s25.767-57.553 57.553-57.553c31.786 0 57.553 25.767 57.553 57.553v0c0 31.785-25.767 57.553-57.553 57.553v0zM435.24 100.716c-19.866 0-35.97 16.105-35.97 35.97s16.105 35.97 35.97 35.97c19.866 0 35.97-16.105 35.97-35.97v0c0-19.866-16.105-35.97-35.97-35.97v0z'%3e%3c/path%3e%3c/svg%3e";
2018
+
2019
+ class EditOverlay extends Overlay {
2020
+ constructor(feature, coordinate = null) {
2021
+ super({
2022
+ id: feature.getId(),
2023
+ position: coordinate || getCenter(feature.getGeometry().getExtent()),
2024
+ positioning: 'center-center',
2025
+ offset: [0, -40],
2026
+ stopEvent: true,
2027
+ element: (createElement("div", null,
2028
+ createElement("div", { className: "ol-wfst--edit-button-cnt", onclick: () => {
2029
+ this.dispatchEvent('editFields');
2030
+ } },
2031
+ createElement("button", { className: "ol-wfst--edit-button", type: "button", title: I18N.labels.editFields },
2032
+ createElement("img", { src: img$1, alt: I18N.labels.editFields }))),
2033
+ createElement("div", { className: "ol-wfst--edit-button-cnt", onclick: () => {
2034
+ this.dispatchEvent('editGeom');
2035
+ } },
2036
+ createElement("button", { class: "ol-wfst--edit-button", type: "button", title: I18N.labels.editGeom },
2037
+ createElement("img", { src: img, alt: I18N.labels.editGeom })))))
2038
+ });
2039
+ }
2040
+ }
2041
+
2042
+ const controlElement = document.createElement('div');
2043
+ /**
2044
+ * Tiny WFST-T client to insert (drawing/uploading), modify and delete
2045
+ * features on GeoServers using OpenLayers. Layers with these types
2046
+ * of geometries are supported: "GeometryCollection" (in this case, you can
2047
+ * choose the geometry type of each element to draw), "Point", "MultiPoint",
2048
+ * "LineString", "MultiLineString", "Polygon" and "MultiPolygon".
2049
+ *
2050
+ * @constructor
2051
+ * @fires modifystart
2052
+ * @fires modifyend
2053
+ * @fires drawstart
2054
+ * @fires drawend
2055
+ * @fires load
2056
+ * @fires describeFeatureType
2057
+ * @extends {ol/control/Control~Control}
2058
+ * @param options Wfst options, see [Wfst Options](#options) for more details.
2059
+ */
2060
+ class Wfst extends Control {
2061
+ _options;
2062
+ _i18n;
2063
+ // Ol
2064
+ _map;
2065
+ _view;
2066
+ _viewport;
2067
+ _initialized = false;
2068
+ _layersControl;
2069
+ _overlay;
2070
+ // Interactions
2071
+ _interactionWfsSelect;
2072
+ _interactionSelectModify;
2073
+ _collectionModify;
2074
+ _interactionModify;
2075
+ _interactionSnap;
2076
+ _interactionDraw;
2077
+ // Obserbable keys
2078
+ _keyClickWms;
2079
+ _keyRemove;
2080
+ _keySelect;
2081
+ // Controls
2082
+ _controlApplyDiscardChanges;
2083
+ _controlWidgetToolsDiv;
2084
+ _selectDraw;
2085
+ // State
2086
+ _currentZoom;
2087
+ _lastZoom;
2088
+ // Editing
2089
+ _editFeature;
2090
+ _editFeatureOriginal;
2091
+ _uploads;
2092
+ _editFields;
2093
+ constructor(options) {
2094
+ super({
2095
+ target: null,
2096
+ element: controlElement,
2097
+ render: () => {
2098
+ if (!this._map)
2099
+ this._init();
2100
+ }
2101
+ });
2102
+ setLang(options.language, options.i18n);
2103
+ const defaultOptions = getDefaultOptions();
2104
+ this._options = deepObjectAssign(defaultOptions, options);
2105
+ // By default, the first layer is ready to accept new draws
2106
+ setActiveLayerToInsertEls(this._options.layers[0]);
2107
+ this._controlWidgetToolsDiv = controlElement;
2108
+ this._controlWidgetToolsDiv.className = 'ol-wfst--tools-control';
2109
+ this._uploads = new Uploads(this._options);
2110
+ this._editFields = new EditFieldsModal(this._options.modal);
2111
+ }
2112
+ /**
2113
+ * Get all the layers in the ol-wfst instance
2114
+ * @public
2115
+ */
2116
+ getLayers() {
2117
+ return Object.values(getStoredMapLayers());
2118
+ }
2119
+ /**
2120
+ * Get a layer
2121
+ * @public
2122
+ */
2123
+ getLayerByName(layerName = '') {
2124
+ const layers = getStoredMapLayers();
2125
+ if (layerName && layerName in layers) {
2126
+ return layers[layerName];
2127
+ }
2128
+ return null;
2129
+ }
2130
+ /**
2131
+ * Connect to the GeoServer and retrieve metadata about the service (GetCapabilities).
2132
+ * Get each layer specs (DescribeFeatureType) and create the layers and map controls.
2133
+ * @fires describeFeatureType
2134
+ * @private
2135
+ */
2136
+ async _initMapAndLayers() {
2137
+ try {
2138
+ const layers = this._options.layers;
2139
+ if (layers.length) {
2140
+ let layerRendered = 0;
2141
+ let layersNumber = 0; // Only count visibles
2142
+ layers.forEach((layer) => {
2143
+ if (layer.getVisible())
2144
+ layersNumber++;
2145
+ layer.on('layerRendered', () => {
2146
+ layerRendered++;
2147
+ if (layerRendered >= layersNumber) {
2148
+ // run only once
2149
+ if (!this._initialized) {
2150
+ this.dispatchEvent('load');
2151
+ this._initialized = true;
2152
+ }
2153
+ showLoading(false);
2154
+ }
2155
+ });
2156
+ layer.on('change:describeFeatureType', () => {
2157
+ const domEl = this._layersControl.addLayerEl(layer);
2158
+ layer.on('change:isVisible', () => {
2159
+ const layerNotVisible = 'ol-wfst--layer-not-visible';
2160
+ const visible = layer.isVisibleByZoom();
2161
+ if (visible)
2162
+ domEl.classList.remove(layerNotVisible);
2163
+ else
2164
+ domEl.classList.add(layerNotVisible);
2165
+ });
2166
+ layer.set(BaseLayerProperty.ISVISIBLE, this._currentZoom > layer.getMinZoom());
2167
+ this.dispatchEvent(new WfstEvent({
2168
+ type: 'describeFeatureType',
2169
+ layer: layer,
2170
+ data: layer.getDescribeFeatureType()
2171
+ }));
2172
+ });
2173
+ layer._init();
2174
+ this._map.addLayer(layer);
2175
+ setMapLayers({
2176
+ [layer.get(BaseLayerProperty.NAME)]: layer
2177
+ });
2178
+ });
2179
+ this._createMapElements(this._options.showControl, this._options.active);
2180
+ }
2181
+ }
2182
+ catch (err) {
2183
+ showLoading(false);
2184
+ showError(err.message, err);
2185
+ }
2186
+ }
2187
+ /**
2188
+ * @private
2189
+ */
2190
+ _init() {
2191
+ this._map = super.getMap();
2192
+ this._view = this._map.getView();
2193
+ this._viewport = this._map.getViewport();
2194
+ setMap(this._map);
2195
+ //@ts-expect-error
2196
+ this._uploads.on('addedFeatures', ({ features }) => {
2197
+ const layer = getActiveLayerToInsertEls();
2198
+ layer.insertFeatures(features);
2199
+ });
2200
+ //@ts-expect-error
2201
+ this._uploads.on('loadedFeatures', ({ features }) => {
2202
+ this.activateEditMode();
2203
+ const editLayerSource = getEditLayer().getSource();
2204
+ editLayerSource.addFeatures(features);
2205
+ this._view.fit(editLayerSource.getExtent(), {
2206
+ size: this._map.getSize(),
2207
+ maxZoom: 21,
2208
+ padding: [100, 100, 100, 100]
2209
+ });
2210
+ });
2211
+ // @ts-expect-error
2212
+ this._editFields.on('save', ({ feature }) => {
2213
+ // Force deselect to trigger handler
2214
+ this._collectionModify.remove(feature);
2215
+ });
2216
+ // @ts-expect-error
2217
+ this._editFields.dispose('delete', ({ feature }) => {
2218
+ this._deleteFeature(feature, true);
2219
+ });
2220
+ this._addMapEvents();
2221
+ initModal(this._options['modal']);
2222
+ this._controlWidgetToolsDiv.append(initLoading());
2223
+ this._initMapAndLayers();
2224
+ }
2225
+ /**
2226
+ * Create the edit layer to allow modify elements, add interactions,
2227
+ * map controls and keyboard handlers.
2228
+ *
2229
+ * @param showControl
2230
+ * @param active
2231
+ * @private
2232
+ */
2233
+ async _createMapElements(showControl, active) {
2234
+ // VectorLayer to store features on editing and inserting
2235
+ this._prepareEditLayer();
2236
+ this._addInteractions();
2237
+ this._addInteractionHandlers();
2238
+ if (showControl) {
2239
+ this._addMapControl();
2240
+ }
2241
+ // By default, init in edit mode
2242
+ this.activateEditMode(active);
2243
+ }
2244
+ /**
2245
+ * @private
2246
+ */
2247
+ _addInteractions() {
2248
+ /**
2249
+ * Select the wfs feature already downloaded
2250
+ * @private
2251
+ */
2252
+ const prepareWfsInteraction = () => {
2253
+ this._collectionModify = new Collection();
2254
+ // Interaction to select wfs layer elements
2255
+ this._interactionWfsSelect = new Select({
2256
+ hitTolerance: 10,
2257
+ style: (feature) => styleFunction(feature),
2258
+ toggleCondition: never,
2259
+ filter: (feature, layer) => {
2260
+ return (getMode() !== Modes.Edit &&
2261
+ layer &&
2262
+ layer instanceof WfsLayer &&
2263
+ layer === getActiveLayerToInsertEls());
2264
+ }
2265
+ });
2266
+ this._map.addInteraction(this._interactionWfsSelect);
2267
+ this._interactionWfsSelect.on('select', ({ selected, deselected, mapBrowserEvent }) => {
2268
+ const coordinate = mapBrowserEvent.coordinate;
2269
+ if (selected.length) {
2270
+ selected.forEach((feature) => {
2271
+ if (!isFeatureEdited(feature)) {
2272
+ // Remove the feature from the original layer
2273
+ const layer = this._interactionWfsSelect.getLayer(feature);
2274
+ layer.getSource().removeFeature(feature);
2275
+ this._addFeatureToEditMode(feature, coordinate, layer.get(BaseLayerProperty.NAME));
2276
+ }
2277
+ });
2278
+ }
2279
+ if (deselected.length) {
2280
+ if (getMode() !== Modes.Edit) {
2281
+ deselected.forEach((feature) => {
2282
+ // Trigger deselect
2283
+ // This is necessary for those times where two features overlap.
2284
+ this._collectionModify.remove(feature);
2285
+ });
2286
+ }
2287
+ }
2288
+ });
2289
+ };
2290
+ /**
2291
+ * Call the geoserver to get the clicked feature
2292
+ * @private
2293
+ */
2294
+ const prepareWmsInteraction = () => {
2295
+ // Interaction to allow select features in the edit layer
2296
+ this._interactionSelectModify = new Select({
2297
+ style: (feature) => styleFunction(feature),
2298
+ layers: [getEditLayer()],
2299
+ toggleCondition: never,
2300
+ removeCondition: () => (getMode() === Modes.Edit ? true : false) // Prevent deselect on clicking outside the feature
2301
+ });
2302
+ this._map.addInteraction(this._interactionSelectModify);
2303
+ this._collectionModify =
2304
+ this._interactionSelectModify.getFeatures();
2305
+ this._keyClickWms = this._map.on(this._options.evtType, async (evt) => {
2306
+ if (this._map.hasFeatureAtPixel(evt.pixel)) {
2307
+ return;
2308
+ }
2309
+ // Only get other features if editmode is disabled
2310
+ if (getMode() !== Modes.Edit) {
2311
+ const layer = getActiveLayerToInsertEls();
2312
+ // If layer is hidden or is a wfs, skip
2313
+ if (!layer.getVisible() ||
2314
+ !layer.isVisibleByZoom() ||
2315
+ layer instanceof WfsLayer) {
2316
+ return;
2317
+ }
2318
+ const features = await layer._getFeaturesByClickEvent(evt);
2319
+ if (!features.length) {
2320
+ return;
2321
+ }
2322
+ // For now, support is only for one feature at time
2323
+ this._addFeatureToEditMode(features[0], evt.coordinate, layer.get(BaseLayerProperty.NAME));
2324
+ }
2325
+ });
2326
+ };
2327
+ if (this._options.layers.find((layer) => layer instanceof WfsLayer)) {
2328
+ prepareWfsInteraction();
2329
+ }
2330
+ if (this._options.layers.find((layer) => layer instanceof WmsLayer)) {
2331
+ prepareWmsInteraction();
2332
+ }
2333
+ this._interactionModify = new Modify({
2334
+ style: () => {
2335
+ if (getMode() === Modes.Edit) {
2336
+ return new Style({
2337
+ image: new Circle({
2338
+ radius: 6,
2339
+ fill: new Fill({
2340
+ color: '#ff0000'
2341
+ }),
2342
+ stroke: new Stroke({
2343
+ width: 2,
2344
+ color: 'rgba(5, 5, 5, 0.9)'
2345
+ })
2346
+ })
2347
+ });
2348
+ }
2349
+ else {
2350
+ return;
2351
+ }
2352
+ },
2353
+ features: this._collectionModify,
2354
+ condition: (evt) => {
2355
+ return primaryAction(evt) && getMode() === Modes.Edit;
2356
+ }
2357
+ });
2358
+ this._map.addInteraction(this._interactionModify);
2359
+ this._interactionSnap = new Snap({
2360
+ source: getEditLayer().getSource()
2361
+ });
2362
+ this._map.addInteraction(this._interactionSnap);
2363
+ }
2364
+ /**
2365
+ * Layer to store temporary the elements to be edited
2366
+ * @private
2367
+ */
2368
+ _prepareEditLayer() {
2369
+ this._map.addLayer(getEditLayer());
2370
+ }
2371
+ /**
2372
+ * @private
2373
+ */
2374
+ _addMapEvents() {
2375
+ /**
2376
+ * @private
2377
+ */
2378
+ const keyboardEvents = () => {
2379
+ document.addEventListener('keydown', ({ key }) => {
2380
+ const inputFocus = document.querySelector('input:focus');
2381
+ if (inputFocus) {
2382
+ return;
2383
+ }
2384
+ if (key === 'Delete') {
2385
+ const selectedFeatures = this._collectionModify;
2386
+ if (selectedFeatures) {
2387
+ selectedFeatures.forEach((feature) => {
2388
+ this._deleteFeature(feature, true);
2389
+ });
2390
+ }
2391
+ }
2392
+ });
2393
+ };
2394
+ keyboardEvents();
2395
+ this._map.on('moveend', () => {
2396
+ this._currentZoom = this._view.getZoom();
2397
+ if (this._currentZoom !== this._lastZoom) {
2398
+ const layers = getStoredMapLayers();
2399
+ Object.keys(layers).forEach((key) => {
2400
+ const layer = layers[key];
2401
+ if (this._currentZoom > layer.getMinZoom()) {
2402
+ // Show the layers
2403
+ if (!layer.get(BaseLayerProperty.ISVISIBLE)) {
2404
+ layer.set(BaseLayerProperty.ISVISIBLE, true);
2405
+ }
2406
+ }
2407
+ else {
2408
+ // Hide the layer
2409
+ if (layer.get(BaseLayerProperty.ISVISIBLE)) {
2410
+ layer.set(BaseLayerProperty.ISVISIBLE, false);
2411
+ }
2412
+ }
2413
+ });
2414
+ this._lastZoom = this._currentZoom;
2415
+ }
2416
+ });
2417
+ }
2418
+ /**
2419
+ * Add map handlers
2420
+ * @private
2421
+ */
2422
+ _addInteractionHandlers() {
2423
+ // When a feature is modified, add this to a list.
2424
+ // This prevent events fired on select and deselect features that has no changes and should
2425
+ // not be updated in the geoserver
2426
+ this._interactionModify.on('modifyend', (evt) => {
2427
+ const feature = evt.features.item(0);
2428
+ addFeatureToEditedList(feature);
2429
+ super.dispatchEvent(evt);
2430
+ });
2431
+ this._interactionModify.on('modifystart', (evt) => {
2432
+ super.dispatchEvent(evt);
2433
+ });
2434
+ this._onDeselectFeatureEvent();
2435
+ this._onRemoveFeatureEvent();
2436
+ }
2437
+ /**
2438
+ * Add the widget on the map to allow change the tools and select active layers
2439
+ * @private
2440
+ */
2441
+ _addMapControl() {
2442
+ this._layersControl = new LayersControl(this._options.showUpload ? this._uploads : null, this._options.uploadFormats);
2443
+ // @ts-expect-error
2444
+ this._layersControl.on('drawMode', () => {
2445
+ if (getMode() === Modes.Draw) {
2446
+ resetStateButtons();
2447
+ this.activateEditMode();
2448
+ }
2449
+ else {
2450
+ const activeLayer = getActiveLayerToInsertEls();
2451
+ if (!activeLayer.isVisibleByZoom()) {
2452
+ showError(I18N.errors.layerNotVisible);
2453
+ }
2454
+ else {
2455
+ this.activateDrawMode(getActiveLayerToInsertEls());
2456
+ }
2457
+ }
2458
+ });
2459
+ // @ts-expect-error
2460
+ this._layersControl.on('changeGeom', () => {
2461
+ if (getMode() === Modes.Draw) {
2462
+ this.activateDrawMode(getActiveLayerToInsertEls());
2463
+ }
2464
+ });
2465
+ const controlEl = this._layersControl.render();
2466
+ this._selectDraw = controlEl.querySelector('.wfst--tools-control--select-draw');
2467
+ this._controlWidgetToolsDiv.append(controlEl);
2468
+ }
2469
+ /**
2470
+ *
2471
+ * @param feature
2472
+ * @private
2473
+ */
2474
+ _deselectEditFeature(feature) {
2475
+ this._removeOverlayHelper(feature);
2476
+ }
2477
+ /**
2478
+ *
2479
+ * @param feature
2480
+ * @param layerName
2481
+ * @private
2482
+ */
2483
+ _restoreFeatureToLayer(feature, layerName) {
2484
+ layerName = layerName || feature.get('_layerName_');
2485
+ const layer = getStoredMapLayers()[layerName];
2486
+ layer.getSource().addFeature(feature);
2487
+ }
2488
+ /**
2489
+ * @param feature
2490
+ * @private
2491
+ */
2492
+ _removeFeatureFromTmpLayer(feature) {
2493
+ // Remove element from the Layer
2494
+ getEditLayer().getSource().removeFeature(feature);
2495
+ }
2496
+ /**
2497
+ * Trigger on deselecting a feature from in the Edit layer
2498
+ *
2499
+ * @private
2500
+ */
2501
+ _onDeselectFeatureEvent() {
2502
+ const checkIfFeatureIsChanged = (feature) => {
2503
+ const layerName = feature.get('_layerName_');
2504
+ const layer = this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName);
2505
+ if (layer instanceof WfsLayer) {
2506
+ this._interactionWfsSelect.getFeatures().remove(feature);
2507
+ }
2508
+ if (isFeatureEdited(feature)) {
2509
+ layer.transactFeatures(TransactionType.Update, feature);
2510
+ }
2511
+ else {
2512
+ // Si es wfs y el elemento no tuvo cambios, lo devolvemos a la layer original
2513
+ if (layer instanceof WfsLayer) {
2514
+ this._restoreFeatureToLayer(feature, layerName);
2515
+ }
2516
+ this._removeFeatureFromTmpLayer(feature);
2517
+ }
2518
+ };
2519
+ // This is fired when a feature is deselected and fires the transaction process
2520
+ this._keySelect = this._collectionModify.on('remove', (evt) => {
2521
+ const feature = evt.element;
2522
+ this._deselectEditFeature(feature);
2523
+ checkIfFeatureIsChanged(feature);
2524
+ this._editModeOff();
2525
+ });
2526
+ }
2527
+ /**
2528
+ * Trigger on removing a feature from the Edit layer
2529
+ *
2530
+ * @private
2531
+ */
2532
+ _onRemoveFeatureEvent() {
2533
+ // If a feature is removed from the edit layer
2534
+ this._keyRemove = getEditLayer()
2535
+ .getSource()
2536
+ .on('removefeature', (evt) => {
2537
+ const feature = evt.feature;
2538
+ if (!feature.get('_delete_')) {
2539
+ return;
2540
+ }
2541
+ if (this._keySelect) {
2542
+ unByKey(this._keySelect);
2543
+ }
2544
+ const layerName = feature.get('_layerName_');
2545
+ const ll = this.getLayerByName(layerName);
2546
+ ll.transactFeatures(TransactionType.Delete, feature);
2547
+ this._deselectEditFeature(feature);
2548
+ this._editModeOff();
2549
+ if (this._keySelect) {
2550
+ setTimeout(() => {
2551
+ this._onDeselectFeatureEvent();
2552
+ }, 150);
2553
+ }
2554
+ });
2555
+ }
2556
+ /**
2557
+ *
2558
+ * @param feature
2559
+ * @private
2560
+ */
2561
+ _editModeOn(feature) {
2562
+ this._editFeatureOriginal = feature.clone();
2563
+ activateMode(Modes.Edit);
2564
+ // To refresh the style
2565
+ getEditLayer().getSource().changed();
2566
+ this._removeOverlayHelper(feature);
2567
+ this._controlApplyDiscardChanges = new EditControlChangesEl(feature);
2568
+ this._controlApplyDiscardChanges.on('cancel', ({ feature }) => {
2569
+ feature.setGeometry(this._editFeatureOriginal.getGeometry());
2570
+ removeFeatureFromEditList(feature);
2571
+ this._collectionModify.remove(feature);
2572
+ });
2573
+ this._controlApplyDiscardChanges.on('apply', ({ feature }) => {
2574
+ showLoading();
2575
+ this._collectionModify.remove(feature);
2576
+ });
2577
+ this._controlApplyDiscardChanges.on('delete', ({ feature }) => {
2578
+ this._deleteFeature(feature, true);
2579
+ });
2580
+ this._map.addControl(this._controlApplyDiscardChanges);
2581
+ }
2582
+ /**
2583
+ * @private
2584
+ */
2585
+ _editModeOff() {
2586
+ activateMode(null);
2587
+ this._map.removeControl(this._controlApplyDiscardChanges);
2588
+ }
2589
+ /**
2590
+ * Remove a feature from the edit Layer and from the Geoserver
2591
+ *
2592
+ * @param feature
2593
+ * @private
2594
+ */
2595
+ _deleteFeature(feature, confirm) {
2596
+ const deleteEl = () => {
2597
+ const features = Array.isArray(feature) ? feature : [feature];
2598
+ features.forEach((feature) => {
2599
+ feature.set('_delete_', true, true);
2600
+ getEditLayer().getSource().removeFeature(feature);
2601
+ });
2602
+ this._collectionModify.clear();
2603
+ const layerName = feature.get('_layerName_');
2604
+ const layer = this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName);
2605
+ if (layer instanceof WfsLayer) {
2606
+ this._interactionWfsSelect.getFeatures().remove(feature);
2607
+ }
2608
+ };
2609
+ if (confirm) {
2610
+ const confirmModal = Modal.confirm(I18N.labels.confirmDelete, {
2611
+ ...this._options.modal
2612
+ });
2613
+ confirmModal.show().once('dismiss', function (modal, ev, button) {
2614
+ if (button && button.value) {
2615
+ deleteEl();
2616
+ }
2617
+ });
2618
+ }
2619
+ else {
2620
+ deleteEl();
2621
+ }
2622
+ }
2623
+ /**
2624
+ * Add a feature to the Edit Layer to allow editing, and creates an Overlay Helper to show options
2625
+ *
2626
+ * @param feature
2627
+ * @param coordinate
2628
+ * @param layerName
2629
+ * @private
2630
+ */
2631
+ _addFeatureToEditMode(feature, coordinate = null, layerName = null) {
2632
+ // For now, only allow one element at time
2633
+ // @TODO: allow edit multiples elements
2634
+ if (this._collectionModify.getLength())
2635
+ return;
2636
+ if (layerName) {
2637
+ // Store the layer information inside the feature
2638
+ feature.set('_layerName_', layerName);
2639
+ }
2640
+ const props = feature ? feature.getProperties() : '';
2641
+ if (props) {
2642
+ if (feature.getGeometry()) {
2643
+ getEditLayer().getSource().addFeature(feature);
2644
+ this._collectionModify.push(feature);
2645
+ const overlay = new EditOverlay(feature, coordinate);
2646
+ // @ts-expect-error
2647
+ overlay.on('editFields', () => {
2648
+ this._editFields.show(feature);
2649
+ });
2650
+ // @ts-expect-error
2651
+ overlay.on('editGeom', () => {
2652
+ this._editModeOn(feature);
2653
+ });
2654
+ this._map.addOverlay(overlay);
2655
+ const layer = getStoredLayer(layerName);
2656
+ if (layer) {
2657
+ layer.maybeLockFeature(feature.getId());
2658
+ }
2659
+ }
2660
+ }
2661
+ }
2662
+ /**
2663
+ * Activate/deactivate the draw mode
2664
+ *
2665
+ * @param layer
2666
+ * @public
2667
+ */
2668
+ activateDrawMode(layer) {
2669
+ /**
2670
+ *
2671
+ * @param layer
2672
+ * @private
2673
+ */
2674
+ const addDrawInteraction = (layer) => {
2675
+ this.activateEditMode(false);
2676
+ // If already exists, remove
2677
+ if (this._interactionDraw) {
2678
+ this._map.removeInteraction(this._interactionDraw);
2679
+ }
2680
+ const geomDrawType = this._selectDraw.value;
2681
+ this._interactionDraw = new Draw({
2682
+ source: getEditLayer().getSource(),
2683
+ type: geomDrawType,
2684
+ style: (feature) => styleFunction(feature),
2685
+ stopClick: true // To prevent firing a map/wms click
2686
+ });
2687
+ this._map.addInteraction(this._interactionDraw);
2688
+ this._interactionDraw.on('drawstart', (evt) => {
2689
+ super.dispatchEvent(evt);
2690
+ });
2691
+ this._interactionDraw.on('drawend', (evt) => {
2692
+ const feature = evt.feature;
2693
+ layer.transactFeatures(TransactionType.Insert, feature);
2694
+ super.dispatchEvent(evt);
2695
+ });
2696
+ };
2697
+ if (!this._interactionDraw && !layer) {
2698
+ return;
2699
+ }
2700
+ if (layer) {
2701
+ // If layer is set to invisible, show warning
2702
+ if (!layer.getVisible()) {
2703
+ return;
2704
+ }
2705
+ activateDrawButton();
2706
+ this._viewport.classList.add('draw-mode');
2707
+ addDrawInteraction(layer);
2708
+ }
2709
+ else {
2710
+ this._map.removeInteraction(this._interactionDraw);
2711
+ this._viewport.classList.remove('draw-mode');
2712
+ }
2713
+ activateMode(layer ? Modes.Draw : null);
2714
+ }
2715
+ /**
2716
+ * Activate/desactivate the edit mode
2717
+ *
2718
+ * @param bool
2719
+ * @public
2720
+ */
2721
+ activateEditMode(bool = true) {
2722
+ if (bool) {
2723
+ activateModeButtons();
2724
+ this.activateDrawMode(false);
2725
+ }
2726
+ else {
2727
+ // Deselct features
2728
+ this._collectionModify.clear();
2729
+ }
2730
+ if (this._interactionSelectModify) {
2731
+ this._interactionSelectModify.setActive(bool);
2732
+ }
2733
+ this._interactionModify.setActive(bool);
2734
+ if (this._interactionWfsSelect)
2735
+ this._interactionWfsSelect.setActive(bool);
2736
+ }
2737
+ /**
2738
+ * Remove the overlay helper atttached to a specify feature
2739
+ * @param feature
2740
+ * @private
2741
+ */
2742
+ _removeOverlayHelper(feature) {
2743
+ const featureId = feature.getId();
2744
+ if (!featureId) {
2745
+ return;
2746
+ }
2747
+ const overlay = this._map.getOverlayById(featureId);
2748
+ if (!overlay) {
2749
+ return;
2750
+ }
2751
+ this._map.removeOverlay(overlay);
2752
+ }
2753
+ }
2754
+ class WfstEvent extends BaseEvent {
2755
+ data;
2756
+ layer;
2757
+ constructor(options) {
2758
+ super(options.type);
2759
+ this.layer = options.layer;
2760
+ this.data = options.data;
2761
+ }
2762
+ }
2763
+
2764
+ export { Geoserver, WfsLayer, WmsLayer, Wfst as default };
2765
+ //# sourceMappingURL=index-es.js.map