ol-wfst 4.3.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +240 -173
- package/dist/@types.d.ts +25 -9
- package/dist/@types.d.ts.map +1 -1
- package/dist/Geoserver.d.ts +6 -4
- package/dist/Geoserver.d.ts.map +1 -1
- package/dist/WfsLayer.d.ts +10 -6
- package/dist/WfsLayer.d.ts.map +1 -1
- package/dist/WmsLayer.d.ts +34 -4
- package/dist/WmsLayer.d.ts.map +1 -1
- package/dist/css/ol-wfst.bootstrap5.css +102 -90
- package/dist/css/ol-wfst.bootstrap5.min.css +1 -1
- package/dist/css/ol-wfst.css +432 -54
- package/dist/css/ol-wfst.min.css +1 -1
- package/dist/defaults.d.ts.map +1 -1
- package/dist/jsx-runtime.d.ts +4 -0
- package/dist/jsx-runtime.d.ts.map +1 -0
- package/dist/modules/EditControlChanges.d.ts +2 -1
- package/dist/modules/EditControlChanges.d.ts.map +1 -1
- package/dist/modules/EditFieldsModal.d.ts +10 -6
- package/dist/modules/EditFieldsModal.d.ts.map +1 -1
- package/dist/modules/EditOverlay.d.ts +1 -1
- package/dist/modules/EditOverlay.d.ts.map +1 -1
- package/dist/modules/ExtendLineOverlay.d.ts +12 -0
- package/dist/modules/ExtendLineOverlay.d.ts.map +1 -0
- package/dist/modules/LayersControl.d.ts +5 -0
- package/dist/modules/LayersControl.d.ts.map +1 -1
- package/dist/modules/QueryOverlay.d.ts +21 -0
- package/dist/modules/QueryOverlay.d.ts.map +1 -0
- package/dist/modules/base/BaseLayer.d.ts +7 -3
- package/dist/modules/base/BaseLayer.d.ts.map +1 -1
- package/dist/modules/base/WfsSource.d.ts.map +1 -1
- package/dist/modules/editLayer.d.ts.map +1 -1
- package/dist/modules/errors.d.ts +1 -1
- package/dist/modules/errors.d.ts.map +1 -1
- package/dist/modules/helpers.d.ts.map +1 -1
- package/dist/modules/i18n/en.d.ts.map +1 -1
- package/dist/modules/i18n/es.d.ts.map +1 -1
- package/dist/modules/i18n/index.d.ts.map +1 -1
- package/dist/modules/i18n/zh.d.ts.map +1 -1
- package/dist/modules/loading.d.ts.map +1 -1
- package/dist/modules/state.d.ts +9 -1
- package/dist/modules/state.d.ts.map +1 -1
- package/dist/modules/styleFunction.d.ts +36 -3
- package/dist/modules/styleFunction.d.ts.map +1 -1
- package/dist/ol-wfst.d.ts +243 -6
- package/dist/ol-wfst.d.ts.map +1 -1
- package/dist/ol-wfst.js +1855 -386
- package/dist/ol-wfst.js.map +1 -1
- package/dist/ol-wfst.min.js +3 -3
- package/dist/ol-wfst.min.js.map +1 -1
- package/lib/@types.d.ts +25 -9
- package/lib/@types.d.ts.map +1 -1
- package/lib/Geoserver.d.ts +6 -4
- package/lib/Geoserver.d.ts.map +1 -1
- package/lib/WfsLayer.d.ts +10 -6
- package/lib/WfsLayer.d.ts.map +1 -1
- package/lib/WmsLayer.d.ts +34 -4
- package/lib/WmsLayer.d.ts.map +1 -1
- package/lib/defaults.d.ts.map +1 -1
- package/lib/jsx-runtime.d.ts +4 -0
- package/lib/jsx-runtime.d.ts.map +1 -0
- package/lib/modules/EditControlChanges.d.ts +2 -1
- package/lib/modules/EditControlChanges.d.ts.map +1 -1
- package/lib/modules/EditFieldsModal.d.ts +10 -6
- package/lib/modules/EditFieldsModal.d.ts.map +1 -1
- package/lib/modules/EditOverlay.d.ts +1 -1
- package/lib/modules/EditOverlay.d.ts.map +1 -1
- package/lib/modules/ExtendLineOverlay.d.ts +12 -0
- package/lib/modules/ExtendLineOverlay.d.ts.map +1 -0
- package/lib/modules/LayersControl.d.ts +5 -0
- package/lib/modules/LayersControl.d.ts.map +1 -1
- package/lib/modules/QueryOverlay.d.ts +21 -0
- package/lib/modules/QueryOverlay.d.ts.map +1 -0
- package/lib/modules/base/BaseLayer.d.ts +7 -3
- package/lib/modules/base/BaseLayer.d.ts.map +1 -1
- package/lib/modules/base/WfsSource.d.ts.map +1 -1
- package/lib/modules/editLayer.d.ts.map +1 -1
- package/lib/modules/errors.d.ts +1 -1
- package/lib/modules/errors.d.ts.map +1 -1
- package/lib/modules/helpers.d.ts.map +1 -1
- package/lib/modules/i18n/en.d.ts.map +1 -1
- package/lib/modules/i18n/es.d.ts.map +1 -1
- package/lib/modules/i18n/index.d.ts.map +1 -1
- package/lib/modules/i18n/zh.d.ts.map +1 -1
- package/lib/modules/loading.d.ts.map +1 -1
- package/lib/modules/state.d.ts +9 -1
- package/lib/modules/state.d.ts.map +1 -1
- package/lib/modules/styleFunction.d.ts +36 -3
- package/lib/modules/styleFunction.d.ts.map +1 -1
- package/lib/ol-wfst.d.ts +243 -6
- package/lib/ol-wfst.d.ts.map +1 -1
- package/lib/ol-wfst.js +1857 -384
- package/lib/ol-wfst.js.map +1 -1
- package/lib/style/css/ol-gisify.css +432 -54
- package/lib/style/css/ol-wfst.bootstrap5.css +102 -90
- package/lib/style/scss/_variables.scss +2 -1
- package/lib/style/scss/ol-wfst.scss +441 -42
- package/package.json +3 -3
- package/dist/myPragma.d.ts +0 -2
- package/dist/myPragma.d.ts.map +0 -1
- package/lib/myPragma.d.ts +0 -2
- package/lib/myPragma.d.ts.map +0 -1
package/dist/ol-wfst.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* ol-wfst - v4.
|
|
2
|
+
* ol-wfst - v4.4.0
|
|
3
3
|
* https://github.com/GastonZalba/ol-wfst#readme
|
|
4
|
-
* Built: Thu
|
|
4
|
+
* Built: Thu Sep 10 2026 23:06:10 GMT-0300 (Argentina Standard Time)
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ol/style/
|
|
8
|
-
typeof define === 'function' && define.amd ? define(['ol/style/
|
|
9
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Wfst = factory(global.ol.style.
|
|
10
|
-
})(this, (function (
|
|
7
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('ol/style/Fill.js'), require('ol/style/Stroke.js'), require('ol/style/Style.js'), require('ol/control/Control.js'), require('ol/control/FullScreen.js'), require('ol/interaction/Draw.js'), require('ol/interaction/DragBox.js'), require('ol/interaction/Modify.js'), require('ol/interaction/Select.js'), require('ol/interaction/Snap.js'), require('ol/geom/LineString.js'), require('ol/geom/MultiLineString.js'), require('ol/geom/GeometryCollection.js'), require('ol/Collection.js'), require('ol/layer/Vector.js'), require('ol/source/Vector.js'), require('ol/events/Event.js'), require('ol/events/condition.js'), require('ol/Observable.js'), require('ol/coordinate.js'), require('ol/extent.js'), require('ol/layer/Base.js'), require('ol/format/GeoJSON.js'), require('ol/proj.js'), require('ol/loadingstrategy.js'), require('ol/layer/Tile.js'), require('ol/source/TileWMS.js'), require('ol/geom.js'), require('ol/format/KML.js'), require('ol/format/WFS.js'), require('ol/style.js'), require('ol/Object.js'), require('ol/geom/Circle.js'), require('ol/Feature.js'), require('ol/geom/Polygon.js'), require('ol/format/XML.js'), require('ol/xml.js'), require('ol/format/xlink.js'), require('ol/format/xsd.js'), require('ol/Overlay.js')) :
|
|
8
|
+
typeof define === 'function' && define.amd ? define(['ol/style/Fill.js', 'ol/style/Stroke.js', 'ol/style/Style.js', 'ol/control/Control.js', 'ol/control/FullScreen.js', 'ol/interaction/Draw.js', 'ol/interaction/DragBox.js', 'ol/interaction/Modify.js', 'ol/interaction/Select.js', 'ol/interaction/Snap.js', 'ol/geom/LineString.js', 'ol/geom/MultiLineString.js', 'ol/geom/GeometryCollection.js', 'ol/Collection.js', 'ol/layer/Vector.js', 'ol/source/Vector.js', 'ol/events/Event.js', 'ol/events/condition.js', 'ol/Observable.js', 'ol/coordinate.js', 'ol/extent.js', 'ol/layer/Base.js', 'ol/format/GeoJSON.js', 'ol/proj.js', 'ol/loadingstrategy.js', 'ol/layer/Tile.js', 'ol/source/TileWMS.js', 'ol/geom.js', 'ol/format/KML.js', 'ol/format/WFS.js', 'ol/style.js', 'ol/Object.js', 'ol/geom/Circle.js', 'ol/Feature.js', 'ol/geom/Polygon.js', 'ol/format/XML.js', 'ol/xml.js', 'ol/format/xlink.js', 'ol/format/xsd.js', 'ol/Overlay.js'], factory) :
|
|
9
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Wfst = factory(global.ol.style.Fill, global.ol.style.Stroke, global.ol.style.Style, global.ol.control.Control, global.ol.control.FullScreen, global.ol.interaction.Draw, global.ol.interaction.DragBox, global.ol.interaction.Modify, global.ol.interaction.Select, global.ol.interaction.Snap, global.ol.geom.LineString, global.ol.geom.MultiLineString, global.ol.geom.GeometryCollection, global.ol.Collection, global.ol.layer.Vector, global.ol.source.Vector, global.ol.events.Event, global.ol.events.condition, global.ol.Observable, global.ol.coordinate, global.ol.extent, global.ol.layer.Base, global.ol.format.GeoJSON, global.ol.proj, global.ol.loadingstrategy, global.ol.layer.Tile, global.ol.source.TileWMS, global.ol.geom, global.ol.format.KML, global.ol.format.WFS, global.ol.style, global.ol.Object, global.ol.geom.Circle, global.ol.Feature, global.ol.geom.Polygon, global.ol.format.XML, global.ol.xml, global.ol.format.xlink, global.ol.format.xsd, global.ol.Overlay));
|
|
10
|
+
})(this, (function (Fill, Stroke, Style, Control, FullScreen, Draw, DragBox, Modify, Select, Snap, LineString, MultiLineString, GeometryCollection, Collection, VectorLayer, VectorSource, BaseEvent, condition_js, Observable, coordinate_js, extent_js, Layer, GeoJSON, proj_js, loadingstrategy_js, TileLayer, TileWMS, geom_js, KML, WFS, style_js, BaseObject, Circle, Feature, Polygon_js, XML, xml_js, xlink_js, xsd_js, Overlay) { 'use strict';
|
|
11
11
|
|
|
12
12
|
var domain;
|
|
13
13
|
|
|
@@ -1140,6 +1140,10 @@
|
|
|
1140
1140
|
const es = {
|
|
1141
1141
|
labels: {
|
|
1142
1142
|
select: 'Seleccionar',
|
|
1143
|
+
selectBox: 'Seleccionar elementos dibujando una caja',
|
|
1144
|
+
selectFreehand: 'Seleccionar elementos dibujando a mano alzada',
|
|
1145
|
+
query: 'Consultar información del elemento',
|
|
1146
|
+
featureInfo: 'Información del elemento',
|
|
1143
1147
|
addElement: 'Modo dibujo',
|
|
1144
1148
|
editElement: 'Editar elemento',
|
|
1145
1149
|
save: 'Guardar',
|
|
@@ -1149,9 +1153,11 @@
|
|
|
1149
1153
|
upload: 'Subir',
|
|
1150
1154
|
editMode: 'Modo Edición',
|
|
1151
1155
|
confirmDelete: '¿Estás seguro de borrar el elemento?',
|
|
1156
|
+
confirmDeleteElements: '¿Borrar los {} elementos?',
|
|
1152
1157
|
geomTypeNotSupported: 'Geometría no compatible con la capa',
|
|
1153
1158
|
editFields: 'Editar campos',
|
|
1154
1159
|
editGeom: 'Editar geometría',
|
|
1160
|
+
continueLine: 'Continuar dibujando desde aquí',
|
|
1155
1161
|
selectDrawType: 'Tipo de geometría para dibujar',
|
|
1156
1162
|
uploadToLayer: 'Subir archivo a la capa seleccionada',
|
|
1157
1163
|
uploadFeatures: 'Subida de elementos a la capa',
|
|
@@ -1159,7 +1165,12 @@
|
|
|
1159
1165
|
invalidFeatures: 'Invalidas',
|
|
1160
1166
|
loading: 'Cargando...',
|
|
1161
1167
|
toggleVisibility: 'Cambiar visibilidad de la capa',
|
|
1162
|
-
|
|
1168
|
+
fullscreen: 'Pantalla completa',
|
|
1169
|
+
close: 'Cerrar',
|
|
1170
|
+
multipleValues: 'Valores múltiples',
|
|
1171
|
+
editElements: 'Editar elementos ({})',
|
|
1172
|
+
multipleEditNotice: 'Edición múltiple activada. Los cambios se aplicarán a los {} elementos seleccionados.',
|
|
1173
|
+
geoserverLayers: 'Capas de GeoServer'
|
|
1163
1174
|
},
|
|
1164
1175
|
errors: {
|
|
1165
1176
|
capabilities: 'No se pudieron obtener las Capabilidades del GeoServer',
|
|
@@ -1180,6 +1191,10 @@
|
|
|
1180
1191
|
const en = {
|
|
1181
1192
|
labels: {
|
|
1182
1193
|
select: 'Select',
|
|
1194
|
+
selectBox: 'Select features by drawing a box',
|
|
1195
|
+
selectFreehand: 'Select features by freehand drawing',
|
|
1196
|
+
query: 'Query feature info',
|
|
1197
|
+
featureInfo: 'Feature info',
|
|
1183
1198
|
addElement: 'Toggle Draw mode',
|
|
1184
1199
|
editElement: 'Edit feature',
|
|
1185
1200
|
save: 'Save',
|
|
@@ -1189,9 +1204,11 @@
|
|
|
1189
1204
|
upload: 'Upload',
|
|
1190
1205
|
editMode: 'Edit Mode',
|
|
1191
1206
|
confirmDelete: 'Are you sure to delete the feature?',
|
|
1207
|
+
confirmDeleteElements: 'Delete the {} selected features?',
|
|
1192
1208
|
geomTypeNotSupported: 'Geometry not supported by layer',
|
|
1193
1209
|
editFields: 'Edit fields',
|
|
1194
1210
|
editGeom: 'Edit geometry',
|
|
1211
|
+
continueLine: 'Continue drawing from here',
|
|
1195
1212
|
selectDrawType: 'Geometry type to draw',
|
|
1196
1213
|
uploadToLayer: 'Upload file to selected layer',
|
|
1197
1214
|
uploadFeatures: 'Uploaded features to layer',
|
|
@@ -1199,7 +1216,12 @@
|
|
|
1199
1216
|
invalidFeatures: 'Invalid',
|
|
1200
1217
|
loading: 'Loading...',
|
|
1201
1218
|
toggleVisibility: 'Toggle layer visibility',
|
|
1202
|
-
|
|
1219
|
+
fullscreen: 'Fullscreen',
|
|
1220
|
+
close: 'Close',
|
|
1221
|
+
multipleValues: 'Multiple values',
|
|
1222
|
+
editElements: 'Edit features ({})',
|
|
1223
|
+
multipleEditNotice: 'Multi-edit active. Changes will be applied to the {} selected features.',
|
|
1224
|
+
geoserverLayers: 'Geoserver Layers'
|
|
1203
1225
|
},
|
|
1204
1226
|
errors: {
|
|
1205
1227
|
capabilities: 'GeoServer Capabilities could not be downloaded.',
|
|
@@ -1220,6 +1242,10 @@
|
|
|
1220
1242
|
const zh = {
|
|
1221
1243
|
labels: {
|
|
1222
1244
|
select: '选择',
|
|
1245
|
+
selectBox: '框选元素',
|
|
1246
|
+
selectFreehand: '手绘选择元素',
|
|
1247
|
+
query: '查询元素信息',
|
|
1248
|
+
featureInfo: '元素信息',
|
|
1223
1249
|
addElement: '切换绘图类型',
|
|
1224
1250
|
editElement: '编辑元素',
|
|
1225
1251
|
save: '保存',
|
|
@@ -1229,9 +1255,11 @@
|
|
|
1229
1255
|
upload: '上传',
|
|
1230
1256
|
editMode: '编辑模式',
|
|
1231
1257
|
confirmDelete: '确认删除元素?',
|
|
1258
|
+
confirmDeleteElements: '确认删除选中的 {} 个元素?',
|
|
1232
1259
|
geomTypeNotSupported: '图层不支持该几何',
|
|
1233
1260
|
editFields: '编辑区域',
|
|
1234
1261
|
editGeom: '编辑几何',
|
|
1262
|
+
continueLine: '从此处继续绘制',
|
|
1235
1263
|
selectDrawType: '几何类型',
|
|
1236
1264
|
uploadToLayer: '通过文件上传图层',
|
|
1237
1265
|
uploadFeatures: '上传元素到图层',
|
|
@@ -1239,7 +1267,12 @@
|
|
|
1239
1267
|
invalidFeatures: '不合法',
|
|
1240
1268
|
loading: '加载中...',
|
|
1241
1269
|
toggleVisibility: '切换图层透明度',
|
|
1242
|
-
|
|
1270
|
+
fullscreen: '全屏',
|
|
1271
|
+
close: '关闭',
|
|
1272
|
+
multipleValues: '多个值',
|
|
1273
|
+
editElements: '编辑元素({})',
|
|
1274
|
+
multipleEditNotice: '多选编辑已激活。更改将应用于所有 {} 个选定元素。',
|
|
1275
|
+
geoserverLayers: 'GeoServer 图层'
|
|
1243
1276
|
},
|
|
1244
1277
|
errors: {
|
|
1245
1278
|
capabilities: '无法加载GeoServer服务所支持的能力.',
|
|
@@ -1274,12 +1307,37 @@
|
|
|
1274
1307
|
I18N = Object.assign(Object.assign({}, I18N), customI18n);
|
|
1275
1308
|
}
|
|
1276
1309
|
};
|
|
1310
|
+
/**
|
|
1311
|
+
* /**
|
|
1312
|
+
* For translations thas has a variable "{}"" to be replaced inside
|
|
1313
|
+
* @param string
|
|
1314
|
+
* @param args
|
|
1315
|
+
* @returns
|
|
1316
|
+
*/
|
|
1317
|
+
const I18N_ = (string, ...args) => {
|
|
1318
|
+
var _a;
|
|
1319
|
+
let text = (_a = I18N.labels) === null || _a === void 0 ? void 0 : _a[string];
|
|
1320
|
+
if (!text) {
|
|
1321
|
+
text = I18N[string];
|
|
1322
|
+
}
|
|
1323
|
+
if (!text) {
|
|
1324
|
+
console.error('Translation not found', string);
|
|
1325
|
+
text = string;
|
|
1326
|
+
}
|
|
1327
|
+
if (args.length) {
|
|
1328
|
+
args.forEach((arg) => {
|
|
1329
|
+
text = text.replace(/{}/, arg);
|
|
1330
|
+
});
|
|
1331
|
+
}
|
|
1332
|
+
return text;
|
|
1333
|
+
};
|
|
1277
1334
|
|
|
1278
1335
|
let loadingDiv;
|
|
1279
1336
|
const initLoading = () => {
|
|
1280
1337
|
loadingDiv = document.createElement('div');
|
|
1281
1338
|
loadingDiv.className = 'ol-wfst--tools-control--loading';
|
|
1282
|
-
loadingDiv.
|
|
1339
|
+
loadingDiv.setAttribute('role', 'progressbar');
|
|
1340
|
+
loadingDiv.setAttribute('aria-label', I18N.labels.loading);
|
|
1283
1341
|
return loadingDiv;
|
|
1284
1342
|
};
|
|
1285
1343
|
const showLoading = (bool = true) => {
|
|
@@ -1364,7 +1422,7 @@
|
|
|
1364
1422
|
const unique = (arr) => arr.filter((e, i) => arr.indexOf(e) == i);
|
|
1365
1423
|
|
|
1366
1424
|
// Keeps track of constituent classes for every mixin class created by ts-mixer.
|
|
1367
|
-
const mixins = new
|
|
1425
|
+
const mixins = new WeakMap();
|
|
1368
1426
|
const getMixinsForClass = (clazz) => mixins.get(clazz);
|
|
1369
1427
|
const registerMixins = (mixedClass, constituents) => mixins.set(mixedClass, constituents);
|
|
1370
1428
|
|
|
@@ -1488,13 +1546,27 @@
|
|
|
1488
1546
|
(function (Modes) {
|
|
1489
1547
|
Modes["Edit"] = "EDIT";
|
|
1490
1548
|
Modes["Draw"] = "DRAW";
|
|
1549
|
+
Modes["Query"] = "QUERY";
|
|
1491
1550
|
})(Modes || (Modes = {}));
|
|
1551
|
+
var SelectionMode;
|
|
1552
|
+
(function (SelectionMode) {
|
|
1553
|
+
SelectionMode["Single"] = "SINGLE";
|
|
1554
|
+
SelectionMode["Box"] = "BOX";
|
|
1555
|
+
SelectionMode["Freehand"] = "FREEHAND";
|
|
1556
|
+
})(SelectionMode || (SelectionMode = {}));
|
|
1557
|
+
let selectionMode = SelectionMode.Single;
|
|
1492
1558
|
function activateMode(m = null) {
|
|
1493
1559
|
mode = m;
|
|
1494
1560
|
}
|
|
1495
1561
|
function getMode() {
|
|
1496
1562
|
return mode;
|
|
1497
1563
|
}
|
|
1564
|
+
function setSelectionMode(m) {
|
|
1565
|
+
selectionMode = m;
|
|
1566
|
+
}
|
|
1567
|
+
function getSelectionMode() {
|
|
1568
|
+
return selectionMode;
|
|
1569
|
+
}
|
|
1498
1570
|
const editedFeatures = new Set();
|
|
1499
1571
|
const mapLayers = {};
|
|
1500
1572
|
function setMap(m) {
|
|
@@ -1533,16 +1605,23 @@
|
|
|
1533
1605
|
*/
|
|
1534
1606
|
class BaseLayer extends Layer {
|
|
1535
1607
|
/**
|
|
1608
|
+
* Initialize the layer: request DescribeFeatureType and resolve when it
|
|
1609
|
+
* finishes (whether successful or not).
|
|
1610
|
+
*
|
|
1536
1611
|
* @private
|
|
1612
|
+
* @returns Promise that resolves when the layer is initialized
|
|
1537
1613
|
*/
|
|
1538
|
-
_init() {
|
|
1614
|
+
async _init() {
|
|
1539
1615
|
const geoserver = this.getGeoserver();
|
|
1540
1616
|
if (geoserver.isLoaded()) {
|
|
1541
|
-
this.getAndUpdateDescribeFeatureType();
|
|
1617
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
1542
1618
|
}
|
|
1543
1619
|
else {
|
|
1544
|
-
|
|
1545
|
-
|
|
1620
|
+
await new Promise((resolve) => {
|
|
1621
|
+
geoserver.once('change:capabilities', async () => {
|
|
1622
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
1623
|
+
resolve();
|
|
1624
|
+
});
|
|
1546
1625
|
});
|
|
1547
1626
|
}
|
|
1548
1627
|
}
|
|
@@ -1618,13 +1697,13 @@
|
|
|
1618
1697
|
}
|
|
1619
1698
|
/**
|
|
1620
1699
|
* @public
|
|
1621
|
-
* @param
|
|
1700
|
+
* @param featureIds
|
|
1622
1701
|
* @returns
|
|
1623
1702
|
*/
|
|
1624
|
-
async maybeLockFeature(
|
|
1703
|
+
async maybeLockFeature(featureIds) {
|
|
1625
1704
|
const geoserver = this.getGeoserver();
|
|
1626
1705
|
if (geoserver.getUseLockFeature() && geoserver.hasLockFeature()) {
|
|
1627
|
-
return await geoserver.lockFeature(
|
|
1706
|
+
return await geoserver.lockFeature(featureIds, this.get(BaseLayerProperty.NAME));
|
|
1628
1707
|
}
|
|
1629
1708
|
return null;
|
|
1630
1709
|
}
|
|
@@ -1663,44 +1742,48 @@
|
|
|
1663
1742
|
*/
|
|
1664
1743
|
class WfsSource extends VectorSource {
|
|
1665
1744
|
constructor(options) {
|
|
1666
|
-
super(Object.assign(Object.assign({}, options), { format: new GeoJSON(), loader:
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1745
|
+
super(Object.assign(Object.assign({}, options), { format: new GeoJSON(), loader: (extent, resolution, projection, success, failure) => {
|
|
1746
|
+
void (async () => {
|
|
1747
|
+
try {
|
|
1748
|
+
// If bbox, add extent to the request
|
|
1749
|
+
if (options.strategy == loadingstrategy_js.bbox) {
|
|
1750
|
+
const extentGeoServer = proj_js.transformExtent(extent, projection.getCode(), options.geoServerAdvanced.projection);
|
|
1751
|
+
// https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
|
|
1752
|
+
// request features using a bounding box with CRS maybe different from featureTypes native CRS
|
|
1753
|
+
this.urlParams.set('bbox', extentGeoServer.toString() +
|
|
1754
|
+
`,${options.geoServerAdvanced.projection}`);
|
|
1755
|
+
}
|
|
1756
|
+
const url_fetch = options.geoserverUrl +
|
|
1757
|
+
'?' +
|
|
1758
|
+
this.urlParams.toString();
|
|
1759
|
+
const response = await fetch(url_fetch, {
|
|
1760
|
+
headers: options.headers,
|
|
1761
|
+
credentials: options.credentials
|
|
1762
|
+
});
|
|
1763
|
+
if (!response.ok) {
|
|
1764
|
+
throw new Error('');
|
|
1765
|
+
}
|
|
1766
|
+
const data = await response.json();
|
|
1767
|
+
if (data.exceptions) {
|
|
1768
|
+
throw new Error(parseError(data));
|
|
1769
|
+
}
|
|
1770
|
+
const features = this.getFormat().readFeatures(data, {
|
|
1771
|
+
featureProjection: projection.getCode(),
|
|
1772
|
+
dataProjection: options.geoServerAdvanced.projection
|
|
1773
|
+
});
|
|
1774
|
+
features.forEach((feature) => {
|
|
1775
|
+
feature.set('_layerName_', options.name,
|
|
1776
|
+
/* silent = */ true);
|
|
1777
|
+
});
|
|
1778
|
+
this.addFeatures(features);
|
|
1779
|
+
success(features);
|
|
1683
1780
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1781
|
+
catch (err) {
|
|
1782
|
+
this.removeLoadedExtent(extent);
|
|
1783
|
+
showError(I18N.errors.geoserver, err, options.name);
|
|
1784
|
+
failure();
|
|
1687
1785
|
}
|
|
1688
|
-
|
|
1689
|
-
featureProjection: projection.getCode(),
|
|
1690
|
-
dataProjection: options.geoServerAdvanced.projection
|
|
1691
|
-
});
|
|
1692
|
-
features.forEach((feature) => {
|
|
1693
|
-
feature.set('_layerName_', options.name,
|
|
1694
|
-
/* silent = */ true);
|
|
1695
|
-
});
|
|
1696
|
-
this.addFeatures(features);
|
|
1697
|
-
success(features);
|
|
1698
|
-
}
|
|
1699
|
-
catch (err) {
|
|
1700
|
-
this.removeLoadedExtent(extent);
|
|
1701
|
-
showError(I18N.errors.geoserver, err, options.name);
|
|
1702
|
-
failure();
|
|
1703
|
-
}
|
|
1786
|
+
})();
|
|
1704
1787
|
} }));
|
|
1705
1788
|
this.urlParams = new URLSearchParams({
|
|
1706
1789
|
SERVICE: 'wfs',
|
|
@@ -1867,7 +1950,7 @@
|
|
|
1867
1950
|
this._loadingCount = 0;
|
|
1868
1951
|
this._loadedCount = 0;
|
|
1869
1952
|
/**
|
|
1870
|
-
* Return the full accuracy
|
|
1953
|
+
* Return the full accuracy geometries to replace the features from GetFeatureInfo
|
|
1871
1954
|
* @param featuresId
|
|
1872
1955
|
* @returns
|
|
1873
1956
|
*/
|
|
@@ -1878,10 +1961,10 @@
|
|
|
1878
1961
|
INFO_FORMAT: 'application/json',
|
|
1879
1962
|
REQUEST: 'GetFeature',
|
|
1880
1963
|
TYPENAME: this.get('name'),
|
|
1881
|
-
MAXFEATURES:
|
|
1964
|
+
MAXFEATURES: String(featuresId.length),
|
|
1882
1965
|
OUTPUTFORMAT: 'application/json',
|
|
1883
1966
|
SRSNAME: getMap().getView().getProjection().getCode(),
|
|
1884
|
-
FEATUREID:
|
|
1967
|
+
FEATUREID: featuresId.join(',')
|
|
1885
1968
|
});
|
|
1886
1969
|
const url = this.getSource().getUrls()[0] + '?' + queryParams.toString();
|
|
1887
1970
|
try {
|
|
@@ -1894,7 +1977,11 @@
|
|
|
1894
1977
|
throw new Error(`${I18N.errors.getFeatures} ${response.status}`);
|
|
1895
1978
|
}
|
|
1896
1979
|
const data = await response.json();
|
|
1897
|
-
|
|
1980
|
+
const fullById = {};
|
|
1981
|
+
this._parseFeaturesFromResponse(data).forEach((feature) => {
|
|
1982
|
+
fullById[String(feature.getId())] = feature;
|
|
1983
|
+
});
|
|
1984
|
+
return featuresId.map((id) => fullById[String(id)]).filter(Boolean);
|
|
1898
1985
|
}
|
|
1899
1986
|
catch (err) {
|
|
1900
1987
|
console.error(err);
|
|
@@ -1904,6 +1991,9 @@
|
|
|
1904
1991
|
if (options.beforeTransactFeature) {
|
|
1905
1992
|
this.beforeTransactFeature = options.beforeTransactFeature;
|
|
1906
1993
|
}
|
|
1994
|
+
if (options.beforeShowFieldsModal) {
|
|
1995
|
+
this.beforeShowFieldsModal = options.beforeShowFieldsModal;
|
|
1996
|
+
}
|
|
1907
1997
|
this._formatGeoJSON = new GeoJSON();
|
|
1908
1998
|
const geoserver = options.geoserver;
|
|
1909
1999
|
const source = new WmsSource({
|
|
@@ -1959,6 +2049,112 @@
|
|
|
1959
2049
|
FEATURE_COUNT: 1,
|
|
1960
2050
|
EXCEPTIONS: 'application/json'
|
|
1961
2051
|
});
|
|
2052
|
+
return this._requestFeatures(url);
|
|
2053
|
+
}
|
|
2054
|
+
/**
|
|
2055
|
+
* Request the WFS features that intersect the given geometry, selecting
|
|
2056
|
+
* them with a WFS GetFeature + CQL INTERSECTS filter. Used to select
|
|
2057
|
+
* features from a WMS layer with a box or a freehand lasso.
|
|
2058
|
+
*
|
|
2059
|
+
* @param geometry Geometry in the map view projection
|
|
2060
|
+
* @returns
|
|
2061
|
+
* @private
|
|
2062
|
+
*/
|
|
2063
|
+
async _getFeaturesInGeometry(geometry) {
|
|
2064
|
+
var _a, _b, _c;
|
|
2065
|
+
// Make sure the DescribeFeatureType is loaded to get the geometry
|
|
2066
|
+
// field, waiting for it if the layer is still initializing
|
|
2067
|
+
if (!this.getDescribeFeatureType()) {
|
|
2068
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
2069
|
+
}
|
|
2070
|
+
const geomField = (_b = (_a = this.getDescribeFeatureType()) === null || _a === void 0 ? void 0 : _a._parsed) === null || _b === void 0 ? void 0 : _b.geomField;
|
|
2071
|
+
if (!geomField) {
|
|
2072
|
+
showError(`${I18N.errors.layer} "${this.get('name')}"`);
|
|
2073
|
+
return [];
|
|
2074
|
+
}
|
|
2075
|
+
const viewProj = getMap().getView().getProjection().getCode();
|
|
2076
|
+
const nativeSrs = this._getNativeSrs();
|
|
2077
|
+
// Work on a copy transformed to the layer native projection so the
|
|
2078
|
+
// server evaluates the INTERSECTS filter against the proper SRID
|
|
2079
|
+
const polygon = geometry.clone();
|
|
2080
|
+
if (nativeSrs !== viewProj) {
|
|
2081
|
+
try {
|
|
2082
|
+
polygon.transform(viewProj, nativeSrs);
|
|
2083
|
+
}
|
|
2084
|
+
catch (err) {
|
|
2085
|
+
console.error(err);
|
|
2086
|
+
}
|
|
2087
|
+
}
|
|
2088
|
+
const ring = polygon.getCoordinates()[0] || [];
|
|
2089
|
+
const ringWkt = ring.map(([x, y]) => `${x} ${y}`).join(', ');
|
|
2090
|
+
// Force the filter geometry CRS with the EWKT SRID prefix so the
|
|
2091
|
+
// server evaluates the INTERSECTS against the layer projection
|
|
2092
|
+
// instead of its native storage SRS
|
|
2093
|
+
const sridMatch = (_c = nativeSrs.match(/(?:EPSG\s*::?\s*|EPSG\/0\/)(\d+)/i)) !== null && _c !== void 0 ? _c : nativeSrs.match(/(\d+)$/);
|
|
2094
|
+
const cql = sridMatch
|
|
2095
|
+
? `INTERSECTS(${geomField}, SRID=${sridMatch[1]};POLYGON((${ringWkt})))`
|
|
2096
|
+
: `INTERSECTS(${geomField}, POLYGON((${ringWkt})))`;
|
|
2097
|
+
const queryParams = new URLSearchParams({
|
|
2098
|
+
SERVICE: 'wfs',
|
|
2099
|
+
VERSION: '2.0.0',
|
|
2100
|
+
REQUEST: 'GetFeature',
|
|
2101
|
+
TYPENAME: this.get('name'),
|
|
2102
|
+
OUTPUTFORMAT: 'application/json',
|
|
2103
|
+
SRSNAME: viewProj,
|
|
2104
|
+
CQL_FILTER: cql
|
|
2105
|
+
});
|
|
2106
|
+
const url = this.getSource().getUrls()[0] + '?' + queryParams.toString();
|
|
2107
|
+
// The server returns the features in the map view projection (same as
|
|
2108
|
+
// the full resolution request), so no further transformation is needed
|
|
2109
|
+
return this._requestFeatures(url);
|
|
2110
|
+
}
|
|
2111
|
+
/**
|
|
2112
|
+
* Resolve the layer native SRS so spatial filters are evaluated in the
|
|
2113
|
+
* correct projection. It reads the DefaultCRS from the WFS capabilities,
|
|
2114
|
+
* falling back to the geoserver advanced projection option and finally to
|
|
2115
|
+
* the map view projection.
|
|
2116
|
+
*
|
|
2117
|
+
* @returns
|
|
2118
|
+
* @private
|
|
2119
|
+
*/
|
|
2120
|
+
_getNativeSrs() {
|
|
2121
|
+
var _a;
|
|
2122
|
+
const geoserver = this.getGeoserver();
|
|
2123
|
+
const viewProj = getMap().getView().getProjection().getCode();
|
|
2124
|
+
const layerName = String(this.get('name') || '');
|
|
2125
|
+
const layerLocalName = layerName.split(':').pop();
|
|
2126
|
+
const featureTypeList = (_a = geoserver.getParsedCapabilities()) === null || _a === void 0 ? void 0 : _a.FeatureTypeList;
|
|
2127
|
+
const featureType = Array.isArray(featureTypeList)
|
|
2128
|
+
? featureTypeList.find((ft) => {
|
|
2129
|
+
const featureTypeName = String((ft === null || ft === void 0 ? void 0 : ft.Name) || '');
|
|
2130
|
+
return (featureTypeName === layerName ||
|
|
2131
|
+
featureTypeName === layerLocalName ||
|
|
2132
|
+
featureTypeName.split(':').pop() === layerLocalName);
|
|
2133
|
+
})
|
|
2134
|
+
: undefined;
|
|
2135
|
+
const defaultCrs = (featureType === null || featureType === void 0 ? void 0 : featureType.DefaultCRS)
|
|
2136
|
+
? String(featureType.DefaultCRS)
|
|
2137
|
+
: '';
|
|
2138
|
+
const epsgMatch = defaultCrs.match(/(?:EPSG\s*::?\s*|EPSG\/0\/)(\d+)/i);
|
|
2139
|
+
if (epsgMatch) {
|
|
2140
|
+
return `EPSG:${epsgMatch[1]}`;
|
|
2141
|
+
}
|
|
2142
|
+
const advanced = geoserver.getAdvanced().projection;
|
|
2143
|
+
if (advanced) {
|
|
2144
|
+
return String(advanced);
|
|
2145
|
+
}
|
|
2146
|
+
return viewProj;
|
|
2147
|
+
}
|
|
2148
|
+
/**
|
|
2149
|
+
* Fetch and parse the features from a request url, replacing the
|
|
2150
|
+
* low resolution geometries (GetFeatureInfo) with the full resolution
|
|
2151
|
+
* ones requested by FEATUREID when possible.
|
|
2152
|
+
*
|
|
2153
|
+
* @param url
|
|
2154
|
+
* @returns
|
|
2155
|
+
* @private
|
|
2156
|
+
*/
|
|
2157
|
+
async _requestFeatures(url) {
|
|
1962
2158
|
const geoserver = this.getGeoserver();
|
|
1963
2159
|
try {
|
|
1964
2160
|
const response = await fetch(url, {
|
|
@@ -1971,6 +2167,9 @@
|
|
|
1971
2167
|
const data = await response.json();
|
|
1972
2168
|
let features = this._parseFeaturesFromResponse(data);
|
|
1973
2169
|
const featuresId = features.map((f) => f.getId());
|
|
2170
|
+
if (!featuresId.length) {
|
|
2171
|
+
return [];
|
|
2172
|
+
}
|
|
1974
2173
|
const fullResList = await this._getFullResGeometryById(featuresId);
|
|
1975
2174
|
if (fullResList) {
|
|
1976
2175
|
features = fullResList;
|
|
@@ -2020,50 +2219,95 @@
|
|
|
2020
2219
|
}
|
|
2021
2220
|
}
|
|
2022
2221
|
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
return tagName(attrs, children);
|
|
2034
|
-
const elem = tagName === null
|
|
2035
|
-
? new DocumentFragment()
|
|
2222
|
+
function createElement(tagName, props) {
|
|
2223
|
+
props = props || {};
|
|
2224
|
+
let children = props.children || [];
|
|
2225
|
+
children = Array.isArray(children) ? children : [children];
|
|
2226
|
+
delete props.children;
|
|
2227
|
+
if (typeof tagName === 'function') {
|
|
2228
|
+
return tagName(props, children);
|
|
2229
|
+
}
|
|
2230
|
+
const elem = tagName === 'fragment'
|
|
2231
|
+
? document.createDocumentFragment()
|
|
2036
2232
|
: document.createElement(tagName);
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
value
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2233
|
+
if (elem instanceof HTMLElement) {
|
|
2234
|
+
Object.entries(props).forEach(([name, value]) => {
|
|
2235
|
+
if (value != null) {
|
|
2236
|
+
// listener
|
|
2237
|
+
if (name.startsWith('on') && typeof value === 'function') {
|
|
2238
|
+
elem.addEventListener(name
|
|
2239
|
+
.slice(2)
|
|
2240
|
+
.toLowerCase(), value);
|
|
2241
|
+
}
|
|
2242
|
+
else if (name === 'className') {
|
|
2045
2243
|
elem.setAttribute('class', value.toString());
|
|
2046
|
-
|
|
2244
|
+
}
|
|
2245
|
+
else if (name === 'htmlFor') {
|
|
2047
2246
|
elem.setAttribute('for', value.toString());
|
|
2048
|
-
|
|
2247
|
+
}
|
|
2248
|
+
else {
|
|
2049
2249
|
elem.setAttribute(name, value.toString());
|
|
2250
|
+
}
|
|
2050
2251
|
}
|
|
2051
|
-
}
|
|
2052
|
-
}
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2053
2254
|
for (const child of children) {
|
|
2054
|
-
if (!child)
|
|
2255
|
+
if (!child) {
|
|
2055
2256
|
continue;
|
|
2056
|
-
|
|
2257
|
+
}
|
|
2258
|
+
if (Array.isArray(child)) {
|
|
2057
2259
|
elem.append(...child);
|
|
2260
|
+
}
|
|
2261
|
+
else if (child instanceof Node) {
|
|
2262
|
+
elem.appendChild(child);
|
|
2263
|
+
}
|
|
2058
2264
|
else {
|
|
2059
|
-
|
|
2060
|
-
elem.innerHTML += child;
|
|
2061
|
-
else
|
|
2062
|
-
elem.appendChild(child);
|
|
2265
|
+
elem.innerHTML += child;
|
|
2063
2266
|
}
|
|
2064
2267
|
}
|
|
2065
2268
|
return elem;
|
|
2066
2269
|
}
|
|
2270
|
+
function jsx(type, props) {
|
|
2271
|
+
return createElement(type, props);
|
|
2272
|
+
}
|
|
2273
|
+
function jsxs(type, props) {
|
|
2274
|
+
return createElement(type, props);
|
|
2275
|
+
}
|
|
2276
|
+
function Fragment(type, children) {
|
|
2277
|
+
return createElement('fragment', { children });
|
|
2278
|
+
}
|
|
2279
|
+
|
|
2280
|
+
function uploadSvg() {
|
|
2281
|
+
return (new DOMParser().parseFromString("\r\n<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"512\" height=\"512\" viewBox=\"0 0 512 512\">\r\n<path d=\"M240 352h-240v128h480v-128h-240zM448 416h-64v-32h64v32zM112 160l128-128 128 128h-80v160h-96v-160z\"></path>\r\n</svg>\r\n", 'image/svg+xml')).firstChild;
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
function drawSvg() {
|
|
2285
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"768\" height=\"768\" viewBox=\"0 0 768 768\">\r\n <path 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\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
2286
|
+
}
|
|
2287
|
+
|
|
2288
|
+
function selectSvg() {
|
|
2289
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"289\" height=\"448\" viewBox=\"0 0 289 448\">\r\n <path d=\"M283.25 260.75c4.75 4.5 6 11.5 3.5 17.25-2.5 6-8.25 10-14.75 10h-95.5l50.25 119c3.5 8.25-0.5 17.5-8.5 21l-44.25 18.75c-8.25 3.5-17.5-0.5-21-8.5l-47.75-113-78 78c-3 3-7 4.75-11.25 4.75-2 0-4.25-0.5-6-1.25-6-2.5-10-8.25-10-14.75v-376c0-6.5 4-12.25 10-14.75 1.75-0.75 4-1.25 6-1.25 4.25 0 8.25 1.5 11.25 4.75z\"></path>\r\n</svg>\r\n", 'image/svg+xml')).firstChild;
|
|
2290
|
+
}
|
|
2291
|
+
|
|
2292
|
+
function selectBoxSvg() {
|
|
2293
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <rect x=\"3.5\" y=\"3.5\" width=\"17\" height=\"17\" rx=\"1.5\" fill=\"none\" stroke=\"#000\" stroke-width=\"2\" stroke-dasharray=\"4 2\"></rect>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
2294
|
+
}
|
|
2295
|
+
|
|
2296
|
+
function selectFreehandSvg() {
|
|
2297
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M4.59 6.89c.7-.71 1.4-1.35 1.71-1.22.5.2 0 1.03-.3 1.52-.25.42-2.86 3.89-2.86 6.31 0 1.28.48 2.34 1.34 2.98.75.56 1.74.73 2.64.46 1.07-.31 1.95-1.4 3.06-2.77 1.21-1.49 2.83-3.44 4.08-3.44 1.63 0 1.65 1.01 1.76 1.79-3.78.64-5.38 3.67-5.38 5.37 0 1.7 1.44 3.09 3.21 3.09 1.63 0 4.29-1.33 4.69-6.1H21v-2.5h-2.47c-.15-1.65-1.09-4.2-4.03-4.2-2.25 0-4.18 1.91-4.94 2.84-.58.73-2.06 2.48-2.29 2.72-.25.3-.68.84-1.11.84-.45 0-.72-.83-.36-1.92.35-1.09 1.4-2.86 1.85-3.52.78-1.14 1.3-1.92 1.3-3.28C8.95 3.69 7.31 3 6.44 3 5.12 3 3.97 4 3.72 4.25c-.36.36-.66.66-.88.93l1.75 1.71zm9.29 11.66c-.31 0-.74-.26-.74-.72 0-.6.73-2.2 2.87-2.76-.3 2.69-1.43 3.48-2.13 3.48z\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
2298
|
+
}
|
|
2299
|
+
|
|
2300
|
+
function infoSvg() {
|
|
2301
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
function visibilityOnSvg() {
|
|
2305
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"768\" height=\"768\" viewBox=\"0 0 768 768\">\n<path 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\"></path>\n</svg>\n", 'image/svg+xml')).firstChild;
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
function visibilityOffSvg() {
|
|
2309
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"768\" height=\"768\" viewBox=\"0 0 768 768\">\n<path 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\"></path>\n</svg>\n", 'image/svg+xml')).firstChild;
|
|
2310
|
+
}
|
|
2067
2311
|
|
|
2068
2312
|
/**
|
|
2069
2313
|
* Removes in the DOM the class of the tools
|
|
@@ -2080,6 +2324,19 @@
|
|
|
2080
2324
|
if (btn) {
|
|
2081
2325
|
btn.classList.add('wfst--active');
|
|
2082
2326
|
}
|
|
2327
|
+
deactivateQueryButton();
|
|
2328
|
+
};
|
|
2329
|
+
const activateQueryButton = () => {
|
|
2330
|
+
const btn = document.querySelector('.ol-wfst--tools-control-btn-query');
|
|
2331
|
+
if (btn) {
|
|
2332
|
+
btn.classList.add('wfst--active');
|
|
2333
|
+
}
|
|
2334
|
+
};
|
|
2335
|
+
const deactivateQueryButton = () => {
|
|
2336
|
+
const btn = document.querySelector('.ol-wfst--tools-control-btn-query');
|
|
2337
|
+
if (btn) {
|
|
2338
|
+
btn.classList.remove('wfst--active');
|
|
2339
|
+
}
|
|
2083
2340
|
};
|
|
2084
2341
|
const activateDrawButton = () => {
|
|
2085
2342
|
const btn = document.querySelector('.ol-wfst--tools-control-btn-draw');
|
|
@@ -2087,6 +2344,29 @@
|
|
|
2087
2344
|
btn.classList.add('wfst--active');
|
|
2088
2345
|
}
|
|
2089
2346
|
};
|
|
2347
|
+
const activateSelectModeButton = (mode) => {
|
|
2348
|
+
const activeBtn = document.querySelector('.ol-wfst--tools-control-btn-select.wfst--active, .ol-wfst--tools-control-btn-select-box.wfst--active, .ol-wfst--tools-control-btn-select-freehand.wfst--active');
|
|
2349
|
+
if (activeBtn) {
|
|
2350
|
+
activeBtn.classList.remove('wfst--active');
|
|
2351
|
+
}
|
|
2352
|
+
deactivateQueryButton();
|
|
2353
|
+
const btnMap = {
|
|
2354
|
+
[SelectionMode.Single]: '.ol-wfst--tools-control-btn-select',
|
|
2355
|
+
[SelectionMode.Box]: '.ol-wfst--tools-control-btn-select-box',
|
|
2356
|
+
[SelectionMode.Freehand]: '.ol-wfst--tools-control-btn-select-freehand'
|
|
2357
|
+
};
|
|
2358
|
+
const btn = document.querySelector(btnMap[mode]);
|
|
2359
|
+
if (btn) {
|
|
2360
|
+
btn.classList.add('wfst--active');
|
|
2361
|
+
}
|
|
2362
|
+
};
|
|
2363
|
+
const deactivateSelectModeButton = () => {
|
|
2364
|
+
const activeBtn = document.querySelector('.ol-wfst--tools-control-btn-select.wfst--active, .ol-wfst--tools-control-btn-select-box.wfst--active, .ol-wfst--tools-control-btn-select-freehand.wfst--active');
|
|
2365
|
+
if (activeBtn) {
|
|
2366
|
+
activeBtn.classList.remove('wfst--active');
|
|
2367
|
+
}
|
|
2368
|
+
deactivateQueryButton();
|
|
2369
|
+
};
|
|
2090
2370
|
class LayersControl extends Observable {
|
|
2091
2371
|
constructor(uploads, uploadFormats) {
|
|
2092
2372
|
super();
|
|
@@ -2099,23 +2379,15 @@
|
|
|
2099
2379
|
* @public
|
|
2100
2380
|
*/
|
|
2101
2381
|
addLayerEl(layer) {
|
|
2102
|
-
const container = document.querySelector('.wfst--tools-control--select-layers');
|
|
2382
|
+
const container = document.querySelector('.wfst--tools-control--select-layers-list');
|
|
2103
2383
|
const layerName = layer.get(BaseLayerProperty.NAME);
|
|
2104
2384
|
const checked = layer === getActiveLayerToInsertEls() ? { checked: true } : {};
|
|
2105
|
-
const input = (
|
|
2106
|
-
const layerDom = (
|
|
2385
|
+
const input = (jsx("input", Object.assign({ value: layerName, id: `wfst--${layerName}`, type: "radio", className: "ol-wfst--tools-control-input", name: "wfst--select-layer" }, checked, { onChange: (evt) => this._layerChangeHandler(evt, layer) })));
|
|
2386
|
+
const layerDom = (jsx("div", { className: `wfst--layer-control
|
|
2107
2387
|
${layer.getVisible() ? 'ol-wfst--visible-on' : ''}
|
|
2108
2388
|
${layer === getActiveLayerToInsertEls()
|
|
2109
2389
|
? 'ol-wfst--selected-on'
|
|
2110
|
-
: ''}`, "data-layer": layerName },
|
|
2111
|
-
createElement("div", { className: "ol-wfst--tools-control-visible" },
|
|
2112
|
-
createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt) },
|
|
2113
|
-
createElement("img", { src: img$3 })),
|
|
2114
|
-
createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt) },
|
|
2115
|
-
createElement("img", { src: img$2 }))),
|
|
2116
|
-
createElement("label", { htmlFor: `wfst--${layerName}` },
|
|
2117
|
-
input,
|
|
2118
|
-
createElement("span", { title: layer.getDescribeFeatureType()._parsed.geomType }, layer.get(BaseLayerProperty.LABEL)))));
|
|
2390
|
+
: ''}`, "data-layer": layerName, children: jsxs("label", { htmlFor: `wfst--${layerName}`, children: [jsxs("div", { className: "ol-wfst--tools-control-visible", children: [jsx("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt), children: visibilityOnSvg() }), jsx("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt), children: visibilityOffSvg() })] }), input, jsx("span", { title: `${layer instanceof WfsLayer ? 'WFS' : 'WMS'} - ${layer.getDescribeFeatureType()._parsed.geomType}`, children: layer.get(BaseLayerProperty.LABEL) })] }) }));
|
|
2119
2391
|
container.appendChild(layerDom);
|
|
2120
2392
|
if (layer === getActiveLayerToInsertEls()) {
|
|
2121
2393
|
input.dispatchEvent(new Event('change'));
|
|
@@ -2211,34 +2483,32 @@
|
|
|
2211
2483
|
this.dispatchEvent('changeLayer');
|
|
2212
2484
|
}
|
|
2213
2485
|
render() {
|
|
2214
|
-
return (
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
})))),
|
|
2241
|
-
createElement("div", { className: "wfst--tools-control--select-layers" })));
|
|
2486
|
+
return (jsxs(Fragment, { children: [jsxs("div", { className: "wfst--tools-control--head", children: [jsxs("div", { className: "ol-wfst--tools-control-select-cnt", children: [jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-select wfst--active", type: "button", title: I18N.labels.select, onClick: () => {
|
|
2487
|
+
this.dispatchEvent('selectModeSingle');
|
|
2488
|
+
}, children: selectSvg() }), jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-select-box", type: "button", title: I18N.labels.selectBox, onClick: () => {
|
|
2489
|
+
this.dispatchEvent('selectModeBox');
|
|
2490
|
+
}, children: selectBoxSvg() }), jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-select-freehand", type: "button", title: I18N.labels.selectFreehand, onClick: () => {
|
|
2491
|
+
this.dispatchEvent('selectModeFreehand');
|
|
2492
|
+
}, children: selectFreehandSvg() })] }), jsx("div", { className: "ol-wfst--tools-control-query-cnt", children: jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-query", type: "button", title: I18N.labels.query, onClick: () => {
|
|
2493
|
+
this.dispatchEvent('queryMode');
|
|
2494
|
+
}, children: infoSvg() }) }), jsxs("div", { className: "ol-wfst--tools-control-draw-cnt", children: [jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-draw", type: "button", title: I18N.labels.addElement, onClick: () => {
|
|
2495
|
+
this.dispatchEvent('drawMode');
|
|
2496
|
+
}, children: drawSvg() }), jsx("select", { title: I18N.labels.selectDrawType, className: "wfst--tools-control--select-draw", onChange: (evt) => {
|
|
2497
|
+
const selectedValue = evt.target.value;
|
|
2498
|
+
this._changeStateSelect(getActiveLayerToInsertEls(), selectedValue);
|
|
2499
|
+
this.dispatchEvent('changeGeom');
|
|
2500
|
+
}, children: [
|
|
2501
|
+
GeometryType.Point,
|
|
2502
|
+
GeometryType.MultiPoint,
|
|
2503
|
+
GeometryType.LineString,
|
|
2504
|
+
GeometryType.MultiLineString,
|
|
2505
|
+
GeometryType.Polygon,
|
|
2506
|
+
GeometryType.MultiPolygon,
|
|
2507
|
+
GeometryType.Circle
|
|
2508
|
+
].map((type) => {
|
|
2509
|
+
// Show all options, but enable only the accepted ones
|
|
2510
|
+
return jsx("option", { value: type, children: type });
|
|
2511
|
+
}) })] }), this._uploads && (jsxs("div", { children: [jsx("input", { id: "ol-wfst--upload", type: "file", accept: this._uploadFormats, onChange: (evt) => this._uploads.process(evt) }), jsx("label", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-upload", htmlFor: "ol-wfst--upload", title: I18N.labels.uploadToLayer, children: uploadSvg() })] }))] }), jsxs("div", { className: "wfst--tools-control--select-layers", children: [jsx("div", { className: "wfst--tools-control--select-layers-title", children: I18N.labels.geoserverLayers }), jsx("div", { className: "wfst--tools-control--select-layers-list" })] })] }));
|
|
2242
2512
|
}
|
|
2243
2513
|
}
|
|
2244
2514
|
|
|
@@ -2477,6 +2747,7 @@
|
|
|
2477
2747
|
evtType: 'singleclick',
|
|
2478
2748
|
active: true,
|
|
2479
2749
|
showControl: true,
|
|
2750
|
+
fullscreen: true,
|
|
2480
2751
|
language: DEFAULT_LANGUAGE,
|
|
2481
2752
|
uploadFormats: '.geojson,.json,.kml',
|
|
2482
2753
|
processUpload: null,
|
|
@@ -2494,91 +2765,145 @@
|
|
|
2494
2765
|
};
|
|
2495
2766
|
|
|
2496
2767
|
class EditControlChangesEl extends Control {
|
|
2497
|
-
constructor(
|
|
2768
|
+
constructor(features) {
|
|
2498
2769
|
super({
|
|
2499
|
-
element: (
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
} }, I18N.labels.cancel),
|
|
2509
|
-
createElement("button", { type: "button", className: "btn btn-sm btn-primary", onClick: () => {
|
|
2510
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('apply', feature));
|
|
2511
|
-
} }, I18N.labels.apply),
|
|
2512
|
-
createElement("button", { type: "button", className: "btn btn-sm btn-danger-outline", onClick: () => {
|
|
2513
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('delete', feature));
|
|
2514
|
-
} }, I18N.labels.delete))))
|
|
2770
|
+
element: (jsx("div", { className: "ol-wfst--changes-control", children: jsxs("div", { className: "ol-wfst--changes-control-el", children: [jsxs("div", { className: "ol-wfst--changes-control-id", children: [jsx("b", { children: I18N.labels.editMode }), " -", ' ', jsx("i", { children: features.length > 1
|
|
2771
|
+
? I18N_('editElements', features.length)
|
|
2772
|
+
: String(features[0].getId()) })] }), jsx("button", { type: "button", className: "btn btn-sm btn-secondary", onClick: () => {
|
|
2773
|
+
this._dispatch('cancel', features);
|
|
2774
|
+
}, children: I18N.labels.cancel }), jsx("button", { type: "button", className: "btn btn-sm btn-primary", onClick: () => {
|
|
2775
|
+
this._dispatch('apply', features);
|
|
2776
|
+
}, children: I18N.labels.apply }), jsx("button", { type: "button", className: "btn btn-sm btn-danger-outline", onClick: () => {
|
|
2777
|
+
this._dispatch('delete', features);
|
|
2778
|
+
}, children: I18N.labels.delete })] }) }))
|
|
2515
2779
|
});
|
|
2516
2780
|
}
|
|
2781
|
+
_dispatch(type, features) {
|
|
2782
|
+
const evt = new VectorSource.VectorSourceEvent(type, features[0]);
|
|
2783
|
+
evt.features = features;
|
|
2784
|
+
this.dispatchEvent(evt);
|
|
2785
|
+
}
|
|
2517
2786
|
}
|
|
2518
2787
|
|
|
2519
2788
|
// Ol
|
|
2789
|
+
/**
|
|
2790
|
+
* Modern vertex handle mimicking the round "extend line" buttons shown at
|
|
2791
|
+
* line endpoints: a soft halo for depth and a white disc with a subtle
|
|
2792
|
+
* border. When `highlighted` is `true` (e.g. hovering a vertex with the
|
|
2793
|
+
* Modify interaction) the handle gets bigger with a stronger halo and a
|
|
2794
|
+
* darker border.
|
|
2795
|
+
*
|
|
2796
|
+
* @param highlighted Whether to render the bigger/highlighted state
|
|
2797
|
+
* @param geometry Style geometry function to position the handles
|
|
2798
|
+
* @private
|
|
2799
|
+
*/
|
|
2800
|
+
const editVertexStyles = (highlighted = false, geometry) => {
|
|
2801
|
+
const halo = highlighted ? 10 : 8;
|
|
2802
|
+
const haloColor = highlighted
|
|
2803
|
+
? 'rgba(0, 0, 0, 0.40)'
|
|
2804
|
+
: 'rgba(0, 0, 0, 0.28)';
|
|
2805
|
+
const radius = highlighted ? 7 : 6;
|
|
2806
|
+
const border = highlighted ? '#4b5563' : '#b3b3b3';
|
|
2807
|
+
const borderWidth = highlighted ? 2 : 1.5;
|
|
2808
|
+
return [
|
|
2809
|
+
new style_js.Style(Object.assign({ image: new style_js.Circle({
|
|
2810
|
+
radius: halo,
|
|
2811
|
+
fill: new style_js.Fill({
|
|
2812
|
+
color: haloColor
|
|
2813
|
+
})
|
|
2814
|
+
}) }, (geometry ? { geometry } : {}))),
|
|
2815
|
+
new style_js.Style(Object.assign({ image: new style_js.Circle({
|
|
2816
|
+
radius: radius,
|
|
2817
|
+
fill: new style_js.Fill({
|
|
2818
|
+
color: '#ffffff'
|
|
2819
|
+
}),
|
|
2820
|
+
stroke: new style_js.Stroke({
|
|
2821
|
+
width: borderWidth,
|
|
2822
|
+
color: border
|
|
2823
|
+
})
|
|
2824
|
+
}) }, (geometry ? { geometry } : {})))
|
|
2825
|
+
];
|
|
2826
|
+
};
|
|
2827
|
+
/**
|
|
2828
|
+
* Extract a `MultiPoint` with every vertex of the given feature geometry
|
|
2829
|
+
* (polygons and multi-line strings are flattened). Returns `undefined` when
|
|
2830
|
+
* the geometry has no coordinates.
|
|
2831
|
+
*
|
|
2832
|
+
* @param feature
|
|
2833
|
+
* @private
|
|
2834
|
+
*/
|
|
2835
|
+
const getFeatureVertices = (feature) => {
|
|
2836
|
+
let geometry = feature.getGeometry();
|
|
2837
|
+
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2838
|
+
geometry = geometry.getGeometries()[0];
|
|
2839
|
+
}
|
|
2840
|
+
const coordinates = geometry.getCoordinates();
|
|
2841
|
+
let flatCoordinates = null;
|
|
2842
|
+
if (geometry instanceof geom_js.Polygon || geometry instanceof geom_js.MultiLineString) {
|
|
2843
|
+
flatCoordinates = coordinates.flat(1);
|
|
2844
|
+
}
|
|
2845
|
+
else if (geometry instanceof geom_js.MultiPolygon) {
|
|
2846
|
+
flatCoordinates = coordinates.flat(2);
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
flatCoordinates = coordinates;
|
|
2850
|
+
}
|
|
2851
|
+
if (!flatCoordinates || !flatCoordinates.length) {
|
|
2852
|
+
return;
|
|
2853
|
+
}
|
|
2854
|
+
return new geom_js.MultiPoint(flatCoordinates);
|
|
2855
|
+
};
|
|
2520
2856
|
/**
|
|
2521
2857
|
* Master style that handles two modes on the Edit Layer:
|
|
2522
2858
|
* - one is the basic, showing only the vertices
|
|
2523
2859
|
* - and the other when modify is active, showing bigger vertices
|
|
2524
2860
|
*
|
|
2861
|
+
* When `hovered` is `true`, a white/red halo is prepended to highlight the
|
|
2862
|
+
* feature, indicating it can be selected on click.
|
|
2863
|
+
*
|
|
2525
2864
|
* @param feature
|
|
2865
|
+
* @param hovered
|
|
2526
2866
|
* @private
|
|
2527
2867
|
*/
|
|
2528
|
-
function styleFunction(feature) {
|
|
2529
|
-
const getVertexs = (feature) => {
|
|
2530
|
-
let geometry = feature.getGeometry();
|
|
2531
|
-
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2532
|
-
geometry = geometry.getGeometries()[0];
|
|
2533
|
-
}
|
|
2534
|
-
const coordinates = geometry.getCoordinates();
|
|
2535
|
-
let flatCoordinates = null;
|
|
2536
|
-
if (geometry instanceof geom_js.Polygon ||
|
|
2537
|
-
geometry instanceof geom_js.MultiLineString) {
|
|
2538
|
-
flatCoordinates = coordinates.flat(1);
|
|
2539
|
-
}
|
|
2540
|
-
else if (geometry instanceof geom_js.MultiPolygon) {
|
|
2541
|
-
flatCoordinates = coordinates.flat(2);
|
|
2542
|
-
}
|
|
2543
|
-
else {
|
|
2544
|
-
flatCoordinates = coordinates;
|
|
2545
|
-
}
|
|
2546
|
-
if (!flatCoordinates || !flatCoordinates.length) {
|
|
2547
|
-
return;
|
|
2548
|
-
}
|
|
2549
|
-
return new geom_js.MultiPoint(flatCoordinates);
|
|
2550
|
-
};
|
|
2868
|
+
function styleFunction(feature, hovered = false) {
|
|
2551
2869
|
let geometry = feature.getGeometry();
|
|
2552
2870
|
let type = geometry.getType();
|
|
2553
2871
|
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2554
2872
|
geometry = geometry.getGeometries()[0];
|
|
2555
2873
|
type = geometry.getType();
|
|
2556
2874
|
}
|
|
2875
|
+
const haloStyle = () => {
|
|
2876
|
+
return [
|
|
2877
|
+
new style_js.Style({
|
|
2878
|
+
image: new style_js.Circle({
|
|
2879
|
+
radius: 9,
|
|
2880
|
+
fill: new style_js.Fill({
|
|
2881
|
+
color: 'rgba(255, 255, 255, 0.9)'
|
|
2882
|
+
}),
|
|
2883
|
+
stroke: new style_js.Stroke({
|
|
2884
|
+
color: '#ff0000',
|
|
2885
|
+
width: 3
|
|
2886
|
+
})
|
|
2887
|
+
}),
|
|
2888
|
+
stroke: new style_js.Stroke({
|
|
2889
|
+
color: 'rgba(255, 255, 255, 0.9)',
|
|
2890
|
+
width: 8
|
|
2891
|
+
}),
|
|
2892
|
+
fill: new style_js.Fill({
|
|
2893
|
+
color: 'rgba(255, 255, 255, 0.9)'
|
|
2894
|
+
})
|
|
2895
|
+
})
|
|
2896
|
+
];
|
|
2897
|
+
};
|
|
2557
2898
|
switch (type) {
|
|
2558
2899
|
case GeometryType.Point:
|
|
2559
2900
|
case GeometryType.MultiPoint:
|
|
2560
2901
|
if (getMode() === Modes.Edit) {
|
|
2561
|
-
return
|
|
2562
|
-
new style_js.Style({
|
|
2563
|
-
image: new style_js.Circle({
|
|
2564
|
-
radius: 6,
|
|
2565
|
-
fill: new style_js.Fill({
|
|
2566
|
-
color: '#000000'
|
|
2567
|
-
})
|
|
2568
|
-
})
|
|
2569
|
-
}),
|
|
2570
|
-
new style_js.Style({
|
|
2571
|
-
image: new style_js.Circle({
|
|
2572
|
-
radius: 4,
|
|
2573
|
-
fill: new style_js.Fill({
|
|
2574
|
-
color: '#ff0000'
|
|
2575
|
-
})
|
|
2576
|
-
})
|
|
2577
|
-
})
|
|
2578
|
-
];
|
|
2902
|
+
return editVertexStyles();
|
|
2579
2903
|
}
|
|
2580
2904
|
else {
|
|
2581
2905
|
return [
|
|
2906
|
+
...(hovered ? haloStyle() : []),
|
|
2582
2907
|
new style_js.Style({
|
|
2583
2908
|
image: new style_js.Circle({
|
|
2584
2909
|
radius: 5,
|
|
@@ -2610,19 +2935,7 @@
|
|
|
2610
2935
|
color: 'rgba(255, 0, 0, 0.7)'
|
|
2611
2936
|
})
|
|
2612
2937
|
}),
|
|
2613
|
-
|
|
2614
|
-
image: new style_js.Circle({
|
|
2615
|
-
radius: 4,
|
|
2616
|
-
fill: new style_js.Fill({
|
|
2617
|
-
color: '#ff0000'
|
|
2618
|
-
}),
|
|
2619
|
-
stroke: new style_js.Stroke({
|
|
2620
|
-
width: 2,
|
|
2621
|
-
color: 'rgba(5, 5, 5, 0.9)'
|
|
2622
|
-
})
|
|
2623
|
-
}),
|
|
2624
|
-
geometry: (feature) => getVertexs(feature)
|
|
2625
|
-
}),
|
|
2938
|
+
...editVertexStyles(false, getFeatureVertices),
|
|
2626
2939
|
new style_js.Style({
|
|
2627
2940
|
stroke: new style_js.Stroke({
|
|
2628
2941
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
@@ -2633,6 +2946,7 @@
|
|
|
2633
2946
|
}
|
|
2634
2947
|
else {
|
|
2635
2948
|
return [
|
|
2949
|
+
...(hovered ? haloStyle() : []),
|
|
2636
2950
|
new style_js.Style({
|
|
2637
2951
|
image: new style_js.Circle({
|
|
2638
2952
|
radius: 2,
|
|
@@ -2640,7 +2954,7 @@
|
|
|
2640
2954
|
color: '#000000'
|
|
2641
2955
|
})
|
|
2642
2956
|
}),
|
|
2643
|
-
geometry: (feature) =>
|
|
2957
|
+
geometry: (feature) => getFeatureVertices(feature)
|
|
2644
2958
|
}),
|
|
2645
2959
|
new style_js.Style({
|
|
2646
2960
|
stroke: new style_js.Stroke({
|
|
@@ -2655,91 +2969,178 @@
|
|
|
2655
2969
|
}
|
|
2656
2970
|
}
|
|
2657
2971
|
}
|
|
2658
|
-
|
|
2659
|
-
// Ol
|
|
2660
2972
|
/**
|
|
2661
|
-
*
|
|
2973
|
+
* Style used to highlight a feature after a query, drawn on a dedicated
|
|
2974
|
+
* highlight layer so the source layers keep their own rendering.
|
|
2662
2975
|
*
|
|
2663
2976
|
* @param feature
|
|
2664
2977
|
* @private
|
|
2665
2978
|
*/
|
|
2979
|
+
function queryStyleFunction(_feature) {
|
|
2980
|
+
return [
|
|
2981
|
+
new style_js.Style({
|
|
2982
|
+
image: new style_js.Circle({
|
|
2983
|
+
radius: 6,
|
|
2984
|
+
fill: new style_js.Fill({
|
|
2985
|
+
color: 'rgba(0, 170, 255, 0.4)'
|
|
2986
|
+
}),
|
|
2987
|
+
stroke: new style_js.Stroke({
|
|
2988
|
+
color: '#0066cc',
|
|
2989
|
+
width: 2
|
|
2990
|
+
})
|
|
2991
|
+
}),
|
|
2992
|
+
stroke: new style_js.Stroke({
|
|
2993
|
+
color: '#00aaff',
|
|
2994
|
+
width: 4
|
|
2995
|
+
}),
|
|
2996
|
+
fill: new style_js.Fill({
|
|
2997
|
+
color: 'rgba(0, 170, 255, 0.3)'
|
|
2998
|
+
})
|
|
2999
|
+
})
|
|
3000
|
+
];
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
/**
|
|
3004
|
+
* Shows a fields form in a modal window to allow changes in the properties of
|
|
3005
|
+
* one or several features. When editing multiple features, fields whose value
|
|
3006
|
+
* differs across the selection show the "multiple values" placeholder and only
|
|
3007
|
+
* the fields actually changed are applied to the whole selection.
|
|
3008
|
+
*
|
|
3009
|
+
* @param features
|
|
3010
|
+
* @private
|
|
3011
|
+
*/
|
|
2666
3012
|
class EditFieldsModal extends Observable {
|
|
2667
3013
|
constructor(options) {
|
|
2668
3014
|
super();
|
|
2669
3015
|
this._options = options;
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
3016
|
+
this._modal = new Modal(Object.assign(Object.assign({}, this._options.modal), { header: true, headerClose: true, title: '', content: '<div></div>', footer: `
|
|
3017
|
+
<button
|
|
3018
|
+
type="button"
|
|
3019
|
+
class="btn btn-sm btn-third"
|
|
3020
|
+
data-action="delete"
|
|
3021
|
+
data-dismiss="modal"
|
|
3022
|
+
>
|
|
3023
|
+
${I18N.labels.delete}
|
|
3024
|
+
</button>
|
|
3025
|
+
<button
|
|
3026
|
+
type="button"
|
|
3027
|
+
class="btn btn-sm btn-secondary"
|
|
3028
|
+
data-dismiss="modal"
|
|
3029
|
+
>
|
|
3030
|
+
${I18N.labels.cancel}
|
|
3031
|
+
</button>
|
|
3032
|
+
<button
|
|
3033
|
+
type="button"
|
|
3034
|
+
class="btn btn-sm btn-primary"
|
|
3035
|
+
data-action="save"
|
|
3036
|
+
data-dismiss="modal"
|
|
3037
|
+
>
|
|
3038
|
+
${I18N.labels.save}
|
|
3039
|
+
</button>
|
|
3040
|
+
` }));
|
|
2682
3041
|
this._modal.on('dismiss', (modal, event) => {
|
|
2683
3042
|
// On saving changes
|
|
2684
3043
|
if (event.target.dataset.action === 'save') {
|
|
2685
|
-
const
|
|
2686
|
-
|
|
3044
|
+
const formElements = modal.el.querySelector('form')
|
|
3045
|
+
.elements;
|
|
3046
|
+
const multipleFields = new Set(Array.from(formElements)
|
|
3047
|
+
.filter((el) => el.dataset.multiple === 'true')
|
|
3048
|
+
.map((el) => el.name));
|
|
3049
|
+
Array.from(formElements).forEach((el) => {
|
|
2687
3050
|
const value = el.value;
|
|
2688
3051
|
const field = el.name;
|
|
2689
|
-
|
|
3052
|
+
// Do not overwrite untouched fields that had
|
|
3053
|
+
// multiple values in the selection
|
|
3054
|
+
if (multipleFields.has(field) && value === '') {
|
|
3055
|
+
return;
|
|
3056
|
+
}
|
|
3057
|
+
this._features.forEach((feature) => {
|
|
3058
|
+
if (feature.get(field) !== value) {
|
|
3059
|
+
feature.set(field, value, /* isSilent = */ true);
|
|
3060
|
+
}
|
|
3061
|
+
});
|
|
3062
|
+
});
|
|
3063
|
+
this._features.forEach((feature) => {
|
|
3064
|
+
feature.changed();
|
|
3065
|
+
addFeatureToEditedList(feature);
|
|
2690
3066
|
});
|
|
2691
|
-
this.
|
|
2692
|
-
addFeatureToEditedList(this._feature);
|
|
2693
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('save', this._feature));
|
|
3067
|
+
this._dispatch('save', this._features);
|
|
2694
3068
|
}
|
|
2695
3069
|
else if (event.target.dataset.action === 'delete') {
|
|
2696
|
-
this.
|
|
3070
|
+
this._dispatch('delete', this._features);
|
|
2697
3071
|
}
|
|
2698
3072
|
});
|
|
2699
3073
|
}
|
|
2700
|
-
show(
|
|
2701
|
-
this.
|
|
2702
|
-
const
|
|
2703
|
-
const
|
|
2704
|
-
|
|
3074
|
+
show(features) {
|
|
3075
|
+
this._features = Array.isArray(features) ? features : [features];
|
|
3076
|
+
const multiple = this._features.length > 1;
|
|
3077
|
+
const modalTitle = multiple
|
|
3078
|
+
? I18N_('editElements', this._features.length)
|
|
3079
|
+
: `${I18N.labels.editElement} ${this._features[0].getId()} `;
|
|
3080
|
+
const layerName = this._features[0].get('_layerName_');
|
|
2705
3081
|
// Data schema from the geoserver
|
|
2706
3082
|
const layer = getStoredLayer(layerName);
|
|
2707
|
-
const
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
3083
|
+
const describeFeatureType = layer.getDescribeFeatureType()._parsed;
|
|
3084
|
+
const fieldList = describeFeatureType.properties.filter((field) => field.name !== describeFeatureType.geomField);
|
|
3085
|
+
this._modal._html.body.innerHTML = '';
|
|
3086
|
+
this._modal._html.body.append(jsxs("div", { children: [multiple && (jsx("div", { className: "ol-wfst--edit-modal-notice", children: I18N_('multipleEditNotice', this._features.length) })), jsx("form", { autocomplete: "false", children: fieldList.flatMap((field) => {
|
|
3087
|
+
const key = field.name;
|
|
3088
|
+
const values = this._features.map((feature) => feature.get(key));
|
|
3089
|
+
const uniqueValues = Array.from(new Set(values.map((value) => String(value !== null && value !== void 0 ? value : ''))));
|
|
3090
|
+
const hasMultipleValues = multiple && uniqueValues.length > 1;
|
|
3091
|
+
const value = hasMultipleValues
|
|
3092
|
+
? null
|
|
3093
|
+
: uniqueValues[0] || null;
|
|
3094
|
+
const typeXsd = field.type;
|
|
3095
|
+
let type;
|
|
3096
|
+
switch (typeXsd) {
|
|
3097
|
+
case 'xsd:double':
|
|
3098
|
+
case 'xsd:number':
|
|
3099
|
+
case 'xsd:int':
|
|
3100
|
+
type = 'number';
|
|
3101
|
+
break;
|
|
3102
|
+
case 'xsd:date':
|
|
3103
|
+
type = 'date';
|
|
3104
|
+
break;
|
|
3105
|
+
case 'xsd:date-time':
|
|
3106
|
+
type = 'datetime';
|
|
3107
|
+
break;
|
|
3108
|
+
case 'xsd:string':
|
|
3109
|
+
default:
|
|
3110
|
+
type = 'text';
|
|
3111
|
+
}
|
|
3112
|
+
let input = (jsx("input", { placeholder: hasMultipleValues
|
|
3113
|
+
? I18N.labels.multipleValues
|
|
3114
|
+
: 'NULL', className: 'ol-wfst--input-field-input' +
|
|
3115
|
+
(hasMultipleValues
|
|
3116
|
+
? ' ol-wfst--input-multiple'
|
|
3117
|
+
: ''), type: type, name: key, value: value, "data-multiple": hasMultipleValues ? 'true' : null }));
|
|
3118
|
+
if (layer.beforeShowFieldsModal) {
|
|
3119
|
+
const hookInput = layer.beforeShowFieldsModal(field, value, input);
|
|
3120
|
+
if (!hookInput) {
|
|
3121
|
+
return [];
|
|
3122
|
+
}
|
|
3123
|
+
if (typeof hookInput === 'string') {
|
|
3124
|
+
input = new DOMParser().parseFromString(hookInput, 'text/html').body.childNodes[0];
|
|
3125
|
+
}
|
|
3126
|
+
else {
|
|
3127
|
+
input = hookInput;
|
|
3128
|
+
}
|
|
3129
|
+
}
|
|
3130
|
+
if (hasMultipleValues) {
|
|
3131
|
+
input.dataset.multiple = 'true';
|
|
3132
|
+
input.classList.add('ol-wfst--input-multiple');
|
|
3133
|
+
}
|
|
3134
|
+
return (jsxs("div", { className: "ol-wfst--input-field-container", children: [jsx("label", { className: "ol-wfst--input-field-label", htmlFor: key, children: key }), input] }));
|
|
3135
|
+
}) })] }));
|
|
3136
|
+
this._modal._html.header.innerHTML = modalTitle;
|
|
2741
3137
|
this._modal.show();
|
|
2742
3138
|
}
|
|
3139
|
+
_dispatch(type, features) {
|
|
3140
|
+
const evt = new VectorSource.VectorSourceEvent(type, features[0]);
|
|
3141
|
+
evt.features = features;
|
|
3142
|
+
this.dispatchEvent(evt);
|
|
3143
|
+
}
|
|
2743
3144
|
}
|
|
2744
3145
|
|
|
2745
3146
|
/**
|
|
@@ -3558,8 +3959,8 @@
|
|
|
3558
3959
|
wfsTransactionVersion: '1.1.0',
|
|
3559
3960
|
projection: DEFAULT_GEOSERVER_SRS,
|
|
3560
3961
|
lockFeatureParams: {
|
|
3561
|
-
expiry: 5,
|
|
3562
|
-
lockId: '
|
|
3962
|
+
expiry: 5, // minutes
|
|
3963
|
+
lockId: 'WFST-editor',
|
|
3563
3964
|
releaseAction: 'SOME'
|
|
3564
3965
|
}
|
|
3565
3966
|
},
|
|
@@ -3577,6 +3978,7 @@
|
|
|
3577
3978
|
this._insertFeatures = [];
|
|
3578
3979
|
this._updateFeatures = [];
|
|
3579
3980
|
this._deleteFeatures = [];
|
|
3981
|
+
this._lockedId = this._options.advanced.lockFeatureParams.lockId;
|
|
3580
3982
|
// Formats
|
|
3581
3983
|
this._formatWFS = new WFS();
|
|
3582
3984
|
this._formatGeoJSON = new GeoJSON();
|
|
@@ -3915,7 +4317,7 @@
|
|
|
3915
4317
|
if (this.hasLockFeature &&
|
|
3916
4318
|
this.getUseLockFeature() &&
|
|
3917
4319
|
transactionType !== TransactionType.Insert) {
|
|
3918
|
-
payload = payload.replace(`</Transaction>`, `<LockId>${this.
|
|
4320
|
+
payload = payload.replace(`</Transaction>`, `<LockId>${this._lockedId}</LockId></Transaction>`);
|
|
3919
4321
|
}
|
|
3920
4322
|
const headers = Object.assign({ 'Content-Type': 'text/xml' }, this.getHeaders());
|
|
3921
4323
|
const response = await fetch(this.getUrl(), {
|
|
@@ -3943,6 +4345,9 @@
|
|
|
3943
4345
|
if (transactionType !== TransactionType.Delete) {
|
|
3944
4346
|
this._removeFeatures(features);
|
|
3945
4347
|
}
|
|
4348
|
+
features.forEach((feature) => {
|
|
4349
|
+
removeFeatureFromEditList(feature);
|
|
4350
|
+
});
|
|
3946
4351
|
wlayer.refresh();
|
|
3947
4352
|
showLoading(false);
|
|
3948
4353
|
this._insertFeatures = [];
|
|
@@ -4002,30 +4407,34 @@
|
|
|
4002
4407
|
const featureProperties = feature.getProperties();
|
|
4003
4408
|
delete featureProperties.boundedBy;
|
|
4004
4409
|
delete featureProperties._layerName_;
|
|
4410
|
+
delete featureProperties._editOverlayCoord_;
|
|
4005
4411
|
const clone = new Feature(featureProperties);
|
|
4006
4412
|
clone.setId(feature.getId());
|
|
4007
4413
|
return clone;
|
|
4008
4414
|
}
|
|
4009
4415
|
/**
|
|
4010
|
-
* Lock
|
|
4011
|
-
* to avoid changes from multiples
|
|
4416
|
+
* Lock one or several features in the geoserver. Useful before editing,
|
|
4417
|
+
* to avoid changes from multiples users. Locking several features at once
|
|
4418
|
+
* with a single request keeps the lock covered by one LockId
|
|
4012
4419
|
*
|
|
4013
|
-
* @param
|
|
4420
|
+
* @param featureIds
|
|
4014
4421
|
* @param layerName
|
|
4015
4422
|
* @param retry
|
|
4016
4423
|
* @public
|
|
4017
4424
|
*/
|
|
4018
|
-
async lockFeature(
|
|
4425
|
+
async lockFeature(featureIds, layerName, retry = 0) {
|
|
4019
4426
|
const params = new URLSearchParams({
|
|
4020
4427
|
service: 'wfs',
|
|
4021
4428
|
version: this.getAdvanced().lockFeatureVersion,
|
|
4022
4429
|
request: 'LockFeature',
|
|
4023
4430
|
typeName: layerName,
|
|
4024
4431
|
expiry: String(this._options.advanced.lockFeatureParams.expiry),
|
|
4025
|
-
|
|
4432
|
+
handle: this._options.advanced.lockFeatureParams.lockId,
|
|
4026
4433
|
releaseAction: this._options.advanced.lockFeatureParams.releaseAction,
|
|
4027
4434
|
exceptions: 'application/json',
|
|
4028
|
-
featureid:
|
|
4435
|
+
featureid: Array.isArray(featureIds)
|
|
4436
|
+
? featureIds.join(',')
|
|
4437
|
+
: String(featureIds)
|
|
4029
4438
|
});
|
|
4030
4439
|
const url_fetch = this.getUrl() + '?' + params.toString();
|
|
4031
4440
|
try {
|
|
@@ -4046,7 +4455,7 @@
|
|
|
4046
4455
|
if (exceptions[0].code === 'CannotLockAllFeatures') {
|
|
4047
4456
|
// Maybe the Feature is already blocked, ant thats trigger error, so, we try one locking more time again
|
|
4048
4457
|
if (!retry) {
|
|
4049
|
-
this.lockFeature(
|
|
4458
|
+
this.lockFeature(featureIds, layerName, 1);
|
|
4050
4459
|
}
|
|
4051
4460
|
else {
|
|
4052
4461
|
throw error;
|
|
@@ -4058,21 +4467,20 @@
|
|
|
4058
4467
|
}
|
|
4059
4468
|
}
|
|
4060
4469
|
catch (err) {
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4072
|
-
|
|
4470
|
+
// XML response (not JSON): parse the real lock id returned by
|
|
4471
|
+
// the server so the transaction references the lock that was
|
|
4472
|
+
// actually created instead of the configured one
|
|
4473
|
+
try {
|
|
4474
|
+
const dataDoc = new window.DOMParser().parseFromString(data, 'text/xml');
|
|
4475
|
+
const lockIdNode = dataDoc.getElementsByTagName('wfs:LockId')[0] ||
|
|
4476
|
+
dataDoc.getElementsByTagNameNS('http://www.opengis.net/wfs', 'LockId')[0];
|
|
4477
|
+
if (lockIdNode && lockIdNode.textContent) {
|
|
4478
|
+
this._lockedId = lockIdNode.textContent;
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
catch (parseErr) {
|
|
4482
|
+
// Keep the configured lockId
|
|
4073
4483
|
}
|
|
4074
|
-
|
|
4075
|
-
*/
|
|
4076
4484
|
}
|
|
4077
4485
|
return data;
|
|
4078
4486
|
}
|
|
@@ -4096,34 +4504,107 @@
|
|
|
4096
4504
|
GeoserverProperty["ISLOADED"] = "isLoaded";
|
|
4097
4505
|
})(GeoserverProperty || (GeoserverProperty = {}));
|
|
4098
4506
|
|
|
4099
|
-
|
|
4507
|
+
function editFieldsSvg() {
|
|
4508
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"448\" height=\"448\" viewBox=\"0 0 448 448\">\r\n <path 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\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
4509
|
+
}
|
|
4100
4510
|
|
|
4101
|
-
|
|
4511
|
+
function editGeomSvg() {
|
|
4512
|
+
return (new DOMParser().parseFromString(" <svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"541\" height=\"512\" viewBox=\"0 0 541 512\">\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n <path fill=\"#000\" 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\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
4513
|
+
}
|
|
4102
4514
|
|
|
4103
4515
|
class EditOverlay extends Overlay {
|
|
4104
|
-
constructor(feature, coordinate = null) {
|
|
4516
|
+
constructor(feature = null, coordinate = null, id) {
|
|
4105
4517
|
super({
|
|
4106
|
-
id: feature.getId(),
|
|
4107
|
-
position: coordinate ||
|
|
4518
|
+
id: id !== null && id !== void 0 ? id : (feature ? feature.getId() : undefined),
|
|
4519
|
+
position: coordinate ||
|
|
4520
|
+
(feature
|
|
4521
|
+
? extent_js.getCenter(feature.getGeometry().getExtent())
|
|
4522
|
+
: undefined),
|
|
4108
4523
|
positioning: 'center-center',
|
|
4109
4524
|
offset: [0, -40],
|
|
4110
4525
|
stopEvent: true,
|
|
4111
|
-
element: (
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4526
|
+
element: (jsxs("div", { children: [jsx("div", { className: "ol-wfst--edit-button-cnt", onClick: () => {
|
|
4527
|
+
this.dispatchEvent('editFields');
|
|
4528
|
+
}, children: jsx("button", { className: "ol-wfst--edit-button", type: "button", title: I18N.labels.editFields, children: editFieldsSvg() }) }), jsx("div", { className: "ol-wfst--edit-button-cnt", onClick: () => {
|
|
4529
|
+
this.dispatchEvent('editGeom');
|
|
4530
|
+
}, children: jsx("button", { class: "ol-wfst--edit-button", type: "button", title: I18N.labels.editGeom, children: editGeomSvg() }) })] }))
|
|
4531
|
+
});
|
|
4532
|
+
}
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
function addLineSvg() {
|
|
4536
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
4537
|
+
}
|
|
4538
|
+
|
|
4539
|
+
class ExtendLineOverlay extends Overlay {
|
|
4540
|
+
constructor(feature, coordinate, side, component = 0, onClick) {
|
|
4541
|
+
super({
|
|
4542
|
+
position: coordinate,
|
|
4543
|
+
positioning: 'center-center',
|
|
4544
|
+
stopEvent: true,
|
|
4545
|
+
element: (jsx("div", { className: "ol-wfst--extend-button-cnt", children: jsx("button", { className: "ol-wfst--extend-button", type: "button", title: I18N.labels.continueLine, onClick: () => {
|
|
4546
|
+
onClick(side);
|
|
4547
|
+
}, children: addLineSvg() }) }))
|
|
4548
|
+
});
|
|
4549
|
+
this.feature = feature;
|
|
4550
|
+
this.side = side;
|
|
4551
|
+
this.component = component;
|
|
4552
|
+
}
|
|
4553
|
+
}
|
|
4554
|
+
|
|
4555
|
+
const QUERY_OVERLAY_ID = 'ol-wfst--query-overlay';
|
|
4556
|
+
/**
|
|
4557
|
+
* Popup overlay that shows the attributes of a queried feature and its
|
|
4558
|
+
* layer label. The geometry is highlighted separately on a dedicated layer.
|
|
4559
|
+
*
|
|
4560
|
+
* @extends {ol/Overlay~Overlay}
|
|
4561
|
+
* @param feature
|
|
4562
|
+
* @param coordinate
|
|
4563
|
+
* @param layer
|
|
4564
|
+
* @private
|
|
4565
|
+
*/
|
|
4566
|
+
class QueryOverlay extends Overlay {
|
|
4567
|
+
constructor(feature, coordinate, layer) {
|
|
4568
|
+
const describeFeatureType = layer.getDescribeFeatureType();
|
|
4569
|
+
const parsed = describeFeatureType === null || describeFeatureType === void 0 ? void 0 : describeFeatureType._parsed;
|
|
4570
|
+
const fields = (parsed === null || parsed === void 0 ? void 0 : parsed.properties) || [];
|
|
4571
|
+
const geomField = parsed === null || parsed === void 0 ? void 0 : parsed.geomField;
|
|
4572
|
+
const rows = fields
|
|
4573
|
+
.filter((field) => field.name !== geomField)
|
|
4574
|
+
.map((field) => {
|
|
4575
|
+
let value = feature.get(field.name);
|
|
4576
|
+
if (value === undefined || value === null) {
|
|
4577
|
+
value = '';
|
|
4578
|
+
}
|
|
4579
|
+
else if (typeof value === 'object') {
|
|
4580
|
+
value = JSON.stringify(value);
|
|
4581
|
+
}
|
|
4582
|
+
return (jsxs("div", { className: "ol-wfst--query-popup-row", children: [jsx("span", { className: "ol-wfst--query-popup-key", children: field.name }), jsx("span", { className: "ol-wfst--query-popup-value", children: String(value) })] }));
|
|
4583
|
+
});
|
|
4584
|
+
super({
|
|
4585
|
+
id: QUERY_OVERLAY_ID,
|
|
4586
|
+
position: coordinate || extent_js.getCenter(feature.getGeometry().getExtent()),
|
|
4587
|
+
positioning: 'bottom-center',
|
|
4588
|
+
offset: [0, -14],
|
|
4589
|
+
stopEvent: true,
|
|
4590
|
+
autoPan: true,
|
|
4591
|
+
element: (jsxs("div", { className: "ol-wfst--query-popup", children: [jsxs("div", { className: "ol-wfst--query-popup-head", children: [jsx("div", { className: "ol-wfst--query-popup-title", children: I18N.labels.featureInfo }), jsx("button", { className: "ol-wfst--query-popup-close", type: "button", title: I18N.labels.close, onClick: () => {
|
|
4592
|
+
this.dispatchEvent('close');
|
|
4593
|
+
}, children: "\u00D7" })] }), jsxs("div", { className: "ol-wfst--query-popup-subtitle", children: [jsx("b", { children: layer.get(BaseLayerProperty.LABEL) }), jsx("i", { children: String(feature.getId()) })] }), jsx("div", { className: "ol-wfst--query-popup-body", children: rows })] }))
|
|
4122
4594
|
});
|
|
4123
4595
|
}
|
|
4124
4596
|
}
|
|
4125
4597
|
|
|
4598
|
+
function fullscreenSvg() {
|
|
4599
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M7 14H5v5h5v-2H7v-3zm-2-4h2V7h3V5H5v5zm12 7h-3v2h5v-5h-2v3zM14 5v2h3v3h2V5h-5z\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
4600
|
+
}
|
|
4601
|
+
|
|
4602
|
+
function fullscreenExitSvg() {
|
|
4603
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\r\n <path d=\"M5 16h3v3h2v-5H5v2zm3-8H5v2h5V5H8v3zm6 11h2v-3h3v-2h-5v5zm2-11V5h-2v5h5V8h-3z\"></path>\r\n</svg>", 'image/svg+xml')).firstChild;
|
|
4604
|
+
}
|
|
4605
|
+
|
|
4126
4606
|
const controlElement = document.createElement('div');
|
|
4607
|
+
const INIT_LOADING_TIMEOUT = 20000;
|
|
4127
4608
|
/**
|
|
4128
4609
|
* Tiny WFS-T client to insert (drawing/uploading), modify and delete
|
|
4129
4610
|
* features on GeoServers using OpenLayers. Layers with these types
|
|
@@ -4152,6 +4633,14 @@
|
|
|
4152
4633
|
}
|
|
4153
4634
|
});
|
|
4154
4635
|
this._initialized = false;
|
|
4636
|
+
this._queryOverlayId = 'ol-wfst--query-overlay';
|
|
4637
|
+
this._shiftPressed = false;
|
|
4638
|
+
this._hoveredFeature = null;
|
|
4639
|
+
this._keySelectBound = false;
|
|
4640
|
+
// Editing
|
|
4641
|
+
this._editFeaturesOriginal = {};
|
|
4642
|
+
this._multiOverlayId = 'ol-wfst--multi-edit-overlay';
|
|
4643
|
+
this._extendOverlays = [];
|
|
4155
4644
|
setLang(options.language, options.i18n);
|
|
4156
4645
|
const defaultOptions = getDefaultOptions();
|
|
4157
4646
|
this._options = deepObjectAssign(defaultOptions, options);
|
|
@@ -4160,7 +4649,7 @@
|
|
|
4160
4649
|
this._controlWidgetToolsDiv = controlElement;
|
|
4161
4650
|
this._controlWidgetToolsDiv.className = 'ol-wfst--tools-control';
|
|
4162
4651
|
this._uploads = new Uploads(this._options);
|
|
4163
|
-
this._editFields = new EditFieldsModal(this._options
|
|
4652
|
+
this._editFields = new EditFieldsModal(this._options);
|
|
4164
4653
|
}
|
|
4165
4654
|
/**
|
|
4166
4655
|
* Get all the layers in the ol-wfst instance
|
|
@@ -4192,8 +4681,10 @@
|
|
|
4192
4681
|
if (layers.length) {
|
|
4193
4682
|
let layerRendered = 0;
|
|
4194
4683
|
let layersNumber = 0; // Only count visibles
|
|
4684
|
+
showLoading();
|
|
4685
|
+
const layerInitPromises = [];
|
|
4195
4686
|
layers.forEach((layer) => {
|
|
4196
|
-
if (layer.
|
|
4687
|
+
if (layer.isVisible(this._view))
|
|
4197
4688
|
layersNumber++;
|
|
4198
4689
|
layer.on('layerRendered', () => {
|
|
4199
4690
|
layerRendered++;
|
|
@@ -4207,15 +4698,17 @@
|
|
|
4207
4698
|
}
|
|
4208
4699
|
});
|
|
4209
4700
|
layer.on('change:describeFeatureType', () => {
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
|
|
4214
|
-
|
|
4215
|
-
|
|
4216
|
-
|
|
4217
|
-
|
|
4218
|
-
|
|
4701
|
+
if (this._options.showControl && layers.length > 1) {
|
|
4702
|
+
const domEl = this._layersControl.addLayerEl(layer);
|
|
4703
|
+
layer.on('change:isVisible', () => {
|
|
4704
|
+
const layerNotVisible = 'ol-wfst--layer-not-visible';
|
|
4705
|
+
const visible = layer.isVisibleByZoom();
|
|
4706
|
+
if (visible)
|
|
4707
|
+
domEl.classList.remove(layerNotVisible);
|
|
4708
|
+
else
|
|
4709
|
+
domEl.classList.add(layerNotVisible);
|
|
4710
|
+
});
|
|
4711
|
+
}
|
|
4219
4712
|
layer.set(BaseLayerProperty.ISVISIBLE, this._currentZoom > layer.getMinZoom());
|
|
4220
4713
|
this.dispatchEvent(new WfstEvent({
|
|
4221
4714
|
type: 'describeFeatureType',
|
|
@@ -4223,13 +4716,21 @@
|
|
|
4223
4716
|
data: layer.getDescribeFeatureType()
|
|
4224
4717
|
}));
|
|
4225
4718
|
});
|
|
4226
|
-
layer._init();
|
|
4719
|
+
layerInitPromises.push(layer._init());
|
|
4227
4720
|
this._map.addLayer(layer);
|
|
4228
4721
|
setMapLayers({
|
|
4229
4722
|
[layer.get(BaseLayerProperty.NAME)]: layer
|
|
4230
4723
|
});
|
|
4231
4724
|
});
|
|
4232
4725
|
this._createMapElements(this._options.showControl, this._options.active);
|
|
4726
|
+
// Show the loading bar until every layer has resolved its
|
|
4727
|
+
// DescribeFeatureType (success or error), with a fail-safe
|
|
4728
|
+
// timeout so it never stays visible on a dead connection.
|
|
4729
|
+
await Promise.race([
|
|
4730
|
+
Promise.all(layerInitPromises),
|
|
4731
|
+
new Promise((resolve) => setTimeout(resolve, INIT_LOADING_TIMEOUT))
|
|
4732
|
+
]);
|
|
4733
|
+
showLoading(false);
|
|
4233
4734
|
}
|
|
4234
4735
|
}
|
|
4235
4736
|
catch (err) {
|
|
@@ -4262,17 +4763,26 @@
|
|
|
4262
4763
|
});
|
|
4263
4764
|
});
|
|
4264
4765
|
// @ts-expect-error
|
|
4265
|
-
this._editFields.on('save', ({ feature }) => {
|
|
4266
|
-
|
|
4267
|
-
this.
|
|
4766
|
+
this._editFields.on('save', async ({ feature, features }) => {
|
|
4767
|
+
const list = features || [feature];
|
|
4768
|
+
const ok = await this._transactEditList(list);
|
|
4769
|
+
if (ok) {
|
|
4770
|
+
Array.from(list).forEach((f) => this._collectionModify.remove(f));
|
|
4771
|
+
}
|
|
4268
4772
|
});
|
|
4269
4773
|
// @ts-expect-error
|
|
4270
|
-
this._editFields.on('delete', ({
|
|
4271
|
-
this._deleteFeature(
|
|
4774
|
+
this._editFields.on('delete', ({ features }) => {
|
|
4775
|
+
this._deleteFeature(features, true);
|
|
4272
4776
|
});
|
|
4273
4777
|
this._addMapEvents();
|
|
4778
|
+
this._addMiddleButtonPan();
|
|
4274
4779
|
initModal(this._options['modal']);
|
|
4275
|
-
this.
|
|
4780
|
+
this._layersWidgetDiv = document.createElement('div');
|
|
4781
|
+
this._layersWidgetDiv.className = 'ol-wfst--layers-control';
|
|
4782
|
+
this._layersWidgetDiv.append(initLoading());
|
|
4783
|
+
this._map.addControl(new Control({
|
|
4784
|
+
element: this._layersWidgetDiv
|
|
4785
|
+
}));
|
|
4276
4786
|
this._initMapAndLayers();
|
|
4277
4787
|
}
|
|
4278
4788
|
/**
|
|
@@ -4288,8 +4798,18 @@
|
|
|
4288
4798
|
this._prepareEditLayer();
|
|
4289
4799
|
this._addInteractions();
|
|
4290
4800
|
this._addInteractionHandlers();
|
|
4801
|
+
this._prepareHighlightLayer();
|
|
4802
|
+
this._addQueryInteraction();
|
|
4291
4803
|
if (showControl) {
|
|
4292
4804
|
this._addMapControl();
|
|
4805
|
+
if (this._options.fullscreen) {
|
|
4806
|
+
this._map.addControl(new FullScreen({
|
|
4807
|
+
className: 'ol-wfst--fullscreen',
|
|
4808
|
+
tipLabel: I18N.labels.fullscreen,
|
|
4809
|
+
label: fullscreenSvg(),
|
|
4810
|
+
labelActive: fullscreenExitSvg()
|
|
4811
|
+
}));
|
|
4812
|
+
}
|
|
4293
4813
|
}
|
|
4294
4814
|
// By default, init in edit mode
|
|
4295
4815
|
this.activateEditMode(active);
|
|
@@ -4308,9 +4828,10 @@
|
|
|
4308
4828
|
this._interactionWfsSelect = new Select({
|
|
4309
4829
|
hitTolerance: 10,
|
|
4310
4830
|
style: (feature) => styleFunction(feature),
|
|
4311
|
-
toggleCondition: condition_js.
|
|
4831
|
+
toggleCondition: condition_js.shiftKeyOnly, // Allow adding features to the selection with shift
|
|
4312
4832
|
filter: (feature, layer) => {
|
|
4313
4833
|
return (getMode() !== Modes.Edit &&
|
|
4834
|
+
getMode() !== Modes.Query &&
|
|
4314
4835
|
layer &&
|
|
4315
4836
|
layer instanceof WfsLayer &&
|
|
4316
4837
|
layer === getActiveLayerToInsertEls());
|
|
@@ -4319,6 +4840,11 @@
|
|
|
4319
4840
|
this._map.addInteraction(this._interactionWfsSelect);
|
|
4320
4841
|
this._interactionWfsSelect.on('select', ({ selected, deselected, mapBrowserEvent }) => {
|
|
4321
4842
|
const coordinate = mapBrowserEvent.coordinate;
|
|
4843
|
+
// Clear hover to avoid keeping the style on the selected feature
|
|
4844
|
+
this._clearHoverState();
|
|
4845
|
+
if (getMode() === Modes.Query) {
|
|
4846
|
+
return;
|
|
4847
|
+
}
|
|
4322
4848
|
if (selected.length) {
|
|
4323
4849
|
selected.forEach((feature) => {
|
|
4324
4850
|
if (!isFeatureEdited(feature)) {
|
|
@@ -4328,6 +4854,7 @@
|
|
|
4328
4854
|
this._addFeatureToEditMode(feature, coordinate, layer.get(BaseLayerProperty.NAME));
|
|
4329
4855
|
}
|
|
4330
4856
|
});
|
|
4857
|
+
this._lockSelectedFeatures();
|
|
4331
4858
|
}
|
|
4332
4859
|
if (deselected.length) {
|
|
4333
4860
|
if (getMode() !== Modes.Edit) {
|
|
@@ -4349,18 +4876,33 @@
|
|
|
4349
4876
|
this._interactionSelectModify = new Select({
|
|
4350
4877
|
style: (feature) => styleFunction(feature),
|
|
4351
4878
|
layers: [getEditLayer()],
|
|
4352
|
-
toggleCondition: condition_js.
|
|
4879
|
+
toggleCondition: condition_js.shiftKeyOnly, // Allow adding features with shift
|
|
4353
4880
|
removeCondition: () => (getMode() === Modes.Edit ? true : false) // Prevent deselect on clicking outside the feature
|
|
4354
4881
|
});
|
|
4355
4882
|
this._map.addInteraction(this._interactionSelectModify);
|
|
4356
|
-
|
|
4357
|
-
|
|
4358
|
-
this.
|
|
4359
|
-
if (
|
|
4883
|
+
// When clicking without shift, replace the current selection: remove
|
|
4884
|
+
// any previously selected and not re-selected feature from the edit list
|
|
4885
|
+
this._interactionSelectModify.on('select', ({ selected, mapBrowserEvent }) => {
|
|
4886
|
+
if (!selected.length ||
|
|
4887
|
+
!mapBrowserEvent ||
|
|
4888
|
+
condition_js.shiftKeyOnly(mapBrowserEvent)) {
|
|
4360
4889
|
return;
|
|
4361
4890
|
}
|
|
4362
|
-
|
|
4363
|
-
|
|
4891
|
+
const selectedSet = new Set(selected);
|
|
4892
|
+
Array.from(this._collectionModify.getArray()).forEach((feature) => {
|
|
4893
|
+
if (!selectedSet.has(feature)) {
|
|
4894
|
+
this._collectionModify.remove(feature);
|
|
4895
|
+
}
|
|
4896
|
+
});
|
|
4897
|
+
});
|
|
4898
|
+
this._collectionModify =
|
|
4899
|
+
this._interactionSelectModify.getFeatures();
|
|
4900
|
+
this._keyClickWms = this._map.on(this._options.evtType || 'singleclick', async (evt) => {
|
|
4901
|
+
if (this._map.hasFeatureAtPixel(evt.pixel)) {
|
|
4902
|
+
return;
|
|
4903
|
+
}
|
|
4904
|
+
// Only get other features if editmode is disabled
|
|
4905
|
+
if (getMode() !== Modes.Edit && getMode() !== Modes.Query) {
|
|
4364
4906
|
const layer = getActiveLayerToInsertEls();
|
|
4365
4907
|
// If layer is hidden or is a wfs, skip
|
|
4366
4908
|
if (!layer.getVisible() ||
|
|
@@ -4372,8 +4914,14 @@
|
|
|
4372
4914
|
if (!(features === null || features === void 0 ? void 0 : features.length)) {
|
|
4373
4915
|
return;
|
|
4374
4916
|
}
|
|
4375
|
-
//
|
|
4917
|
+
// Without shift, replace the current selection
|
|
4918
|
+
if (!condition_js.shiftKeyOnly(evt)) {
|
|
4919
|
+
Array.from(this._collectionModify.getArray()).forEach((feature) => {
|
|
4920
|
+
this._collectionModify.remove(feature);
|
|
4921
|
+
});
|
|
4922
|
+
}
|
|
4376
4923
|
this._addFeatureToEditMode(features[0], evt.coordinate, layer.get(BaseLayerProperty.NAME));
|
|
4924
|
+
this._lockSelectedFeatures();
|
|
4377
4925
|
}
|
|
4378
4926
|
});
|
|
4379
4927
|
};
|
|
@@ -4384,20 +4932,9 @@
|
|
|
4384
4932
|
prepareWmsInteraction();
|
|
4385
4933
|
}
|
|
4386
4934
|
this._interactionModify = new Modify({
|
|
4387
|
-
style: () => {
|
|
4935
|
+
style: (feature) => {
|
|
4388
4936
|
if (getMode() === Modes.Edit) {
|
|
4389
|
-
return
|
|
4390
|
-
image: new CircleStyle({
|
|
4391
|
-
radius: 6,
|
|
4392
|
-
fill: new Fill({
|
|
4393
|
-
color: '#ff0000'
|
|
4394
|
-
}),
|
|
4395
|
-
stroke: new Stroke({
|
|
4396
|
-
width: 2,
|
|
4397
|
-
color: 'rgba(5, 5, 5, 0.9)'
|
|
4398
|
-
})
|
|
4399
|
-
})
|
|
4400
|
-
});
|
|
4937
|
+
return editVertexStyles(feature.get('existing') === true);
|
|
4401
4938
|
}
|
|
4402
4939
|
else {
|
|
4403
4940
|
return;
|
|
@@ -4413,6 +4950,175 @@
|
|
|
4413
4950
|
source: getEditLayer().getSource()
|
|
4414
4951
|
});
|
|
4415
4952
|
this._map.addInteraction(this._interactionSnap);
|
|
4953
|
+
this._addSelectionInteractions();
|
|
4954
|
+
}
|
|
4955
|
+
/**
|
|
4956
|
+
* Interactions to select features by drawing a box (DragBox) or a freehand
|
|
4957
|
+
* polygon (freehand Draw lasso). The selected features are added to the
|
|
4958
|
+
* edit list, keeping the same behaviour as the single click selection.
|
|
4959
|
+
*
|
|
4960
|
+
* @private
|
|
4961
|
+
*/
|
|
4962
|
+
_addSelectionInteractions() {
|
|
4963
|
+
const selectionStyle = new Style({
|
|
4964
|
+
fill: new Fill({
|
|
4965
|
+
color: 'rgba(255, 200, 0, 0.1)'
|
|
4966
|
+
}),
|
|
4967
|
+
stroke: new Stroke({
|
|
4968
|
+
color: '#ffc800',
|
|
4969
|
+
width: 2,
|
|
4970
|
+
lineDash: [6, 4]
|
|
4971
|
+
})
|
|
4972
|
+
});
|
|
4973
|
+
// Box selection (dragging a rectangle)
|
|
4974
|
+
this._interactionDragBox = new DragBox({
|
|
4975
|
+
condition: (evt) => condition_js.primaryAction(evt) && this._isSelectMode(SelectionMode.Box),
|
|
4976
|
+
className: 'ol-wfst--dragbox'
|
|
4977
|
+
});
|
|
4978
|
+
this._interactionDragBox.on('boxend', (evt) => {
|
|
4979
|
+
if (!this._isSelectMode(SelectionMode.Box)) {
|
|
4980
|
+
return;
|
|
4981
|
+
}
|
|
4982
|
+
// Without shift, replace the current selection
|
|
4983
|
+
if (!evt.mapBrowserEvent.originalEvent.shiftKey) {
|
|
4984
|
+
this._collectionModify.clear();
|
|
4985
|
+
}
|
|
4986
|
+
this._selectByGeometry(this._interactionDragBox.getGeometry());
|
|
4987
|
+
});
|
|
4988
|
+
this._map.addInteraction(this._interactionDragBox);
|
|
4989
|
+
// Freehand selection (holding the mouse to draw a closed lasso)
|
|
4990
|
+
this._interactionFreehandSelect = new Draw({
|
|
4991
|
+
type: GeometryType.Polygon,
|
|
4992
|
+
freehand: true,
|
|
4993
|
+
stopClick: true,
|
|
4994
|
+
style: selectionStyle,
|
|
4995
|
+
condition: (evt) => condition_js.primaryAction(evt) && this._isSelectMode(SelectionMode.Freehand)
|
|
4996
|
+
});
|
|
4997
|
+
this._interactionFreehandSelect.on('drawend', (evt) => {
|
|
4998
|
+
if (!this._isSelectMode(SelectionMode.Freehand)) {
|
|
4999
|
+
return;
|
|
5000
|
+
}
|
|
5001
|
+
const geometry = evt.feature.getGeometry();
|
|
5002
|
+
const ring = geometry ? geometry.getCoordinates()[0] : [];
|
|
5003
|
+
// Ignore degenerate lassos (a simple click without dragging)
|
|
5004
|
+
if (!ring.length || ring.length < 4 || geometry.getArea() <= 0) {
|
|
5005
|
+
return;
|
|
5006
|
+
}
|
|
5007
|
+
// Without shift, replace the current selection
|
|
5008
|
+
if (!this._shiftPressed) {
|
|
5009
|
+
this._collectionModify.clear();
|
|
5010
|
+
}
|
|
5011
|
+
this._selectByGeometry(geometry);
|
|
5012
|
+
});
|
|
5013
|
+
this._map.addInteraction(this._interactionFreehandSelect);
|
|
5014
|
+
this._refreshSelectionInteractions();
|
|
5015
|
+
}
|
|
5016
|
+
/**
|
|
5017
|
+
* Whether a selection tool mode is currently usable: only outside the
|
|
5018
|
+
* edit/draw modes.
|
|
5019
|
+
*
|
|
5020
|
+
* @param mode
|
|
5021
|
+
* @private
|
|
5022
|
+
*/
|
|
5023
|
+
_isSelectMode(mode) {
|
|
5024
|
+
return getMode() === null && getSelectionMode() === mode;
|
|
5025
|
+
}
|
|
5026
|
+
/**
|
|
5027
|
+
* Enable/disable the box and freehand selection interactions according to
|
|
5028
|
+
* the current selection tool and map mode.
|
|
5029
|
+
*
|
|
5030
|
+
* @private
|
|
5031
|
+
*/
|
|
5032
|
+
_refreshSelectionInteractions() {
|
|
5033
|
+
const active = getMode() === null;
|
|
5034
|
+
if (this._interactionDragBox) {
|
|
5035
|
+
this._interactionDragBox.setActive(active && getSelectionMode() === SelectionMode.Box);
|
|
5036
|
+
}
|
|
5037
|
+
if (this._interactionFreehandSelect) {
|
|
5038
|
+
this._interactionFreehandSelect.setActive(active && getSelectionMode() === SelectionMode.Freehand);
|
|
5039
|
+
}
|
|
5040
|
+
}
|
|
5041
|
+
/**
|
|
5042
|
+
* Select the features that intersect the given polygon on the active layer
|
|
5043
|
+
* and add them to the edit list. WFS features are picked from the already
|
|
5044
|
+
* loaded source, while WMS features are requested to the GeoServer with a
|
|
5045
|
+
* WFS GetFeature INTERSECTS filter.
|
|
5046
|
+
*
|
|
5047
|
+
* @param geometry
|
|
5048
|
+
* @private
|
|
5049
|
+
*/
|
|
5050
|
+
async _selectByGeometry(geometry) {
|
|
5051
|
+
const layer = getActiveLayerToInsertEls();
|
|
5052
|
+
if (!layer || !layer.getVisible() || !layer.isVisibleByZoom()) {
|
|
5053
|
+
showError(I18N.errors.layerNotVisible);
|
|
5054
|
+
return;
|
|
5055
|
+
}
|
|
5056
|
+
let features;
|
|
5057
|
+
if (layer instanceof WfsLayer) {
|
|
5058
|
+
const candidates = layer
|
|
5059
|
+
.getSource()
|
|
5060
|
+
.getFeaturesInExtent(geometry.getExtent());
|
|
5061
|
+
// The extent is only a bounding box; for the freehand lasso be
|
|
5062
|
+
// more precise and keep the features whose vertices fall inside it
|
|
5063
|
+
if (getSelectionMode() === SelectionMode.Freehand) {
|
|
5064
|
+
features = candidates.filter((feature) => {
|
|
5065
|
+
const featureGeom = feature.getGeometry();
|
|
5066
|
+
if (!featureGeom) {
|
|
5067
|
+
return false;
|
|
5068
|
+
}
|
|
5069
|
+
const flatCoords = featureGeom.getFlatCoordinates();
|
|
5070
|
+
const stride = featureGeom.getStride();
|
|
5071
|
+
for (let i = 0; i < flatCoords.length; i += stride) {
|
|
5072
|
+
if (geometry.intersectsCoordinate([
|
|
5073
|
+
flatCoords[i],
|
|
5074
|
+
flatCoords[i + 1]
|
|
5075
|
+
])) {
|
|
5076
|
+
return true;
|
|
5077
|
+
}
|
|
5078
|
+
}
|
|
5079
|
+
return false;
|
|
5080
|
+
});
|
|
5081
|
+
}
|
|
5082
|
+
else {
|
|
5083
|
+
features = candidates;
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
else if (layer instanceof WmsLayer) {
|
|
5087
|
+
features = await layer._getFeaturesInGeometry(geometry);
|
|
5088
|
+
}
|
|
5089
|
+
if (features) {
|
|
5090
|
+
this._selectFeatures(features);
|
|
5091
|
+
}
|
|
5092
|
+
}
|
|
5093
|
+
/**
|
|
5094
|
+
* Add the given features to the edit mode:
|
|
5095
|
+
* remove them from their original layer and push them into the edit
|
|
5096
|
+
* collection so they can be modified/deleted like the ones selected
|
|
5097
|
+
* with a single click.
|
|
5098
|
+
*
|
|
5099
|
+
* @param features
|
|
5100
|
+
* @private
|
|
5101
|
+
*/
|
|
5102
|
+
_selectFeatures(features) {
|
|
5103
|
+
if (!features || !features.length) {
|
|
5104
|
+
return;
|
|
5105
|
+
}
|
|
5106
|
+
const layer = getActiveLayerToInsertEls();
|
|
5107
|
+
const layerName = layer.get(BaseLayerProperty.NAME);
|
|
5108
|
+
const selectedIds = new Set(this._collectionModify.getArray().map((feature) => feature.getId()));
|
|
5109
|
+
features.forEach((feature) => {
|
|
5110
|
+
if (isFeatureEdited(feature) || selectedIds.has(feature.getId())) {
|
|
5111
|
+
return;
|
|
5112
|
+
}
|
|
5113
|
+
// WFS features are stored in their layer source: remove them from
|
|
5114
|
+
// there while they are being edited
|
|
5115
|
+
if (layer instanceof WfsLayer &&
|
|
5116
|
+
layer.getSource().hasFeature(feature)) {
|
|
5117
|
+
layer.getSource().removeFeature(feature);
|
|
5118
|
+
}
|
|
5119
|
+
this._addFeatureToEditMode(feature, null, layerName);
|
|
5120
|
+
});
|
|
5121
|
+
this._lockSelectedFeatures();
|
|
4416
5122
|
}
|
|
4417
5123
|
/**
|
|
4418
5124
|
* Layer to store temporary the elements to be edited
|
|
@@ -4421,6 +5127,149 @@
|
|
|
4421
5127
|
_prepareEditLayer() {
|
|
4422
5128
|
this._map.addLayer(getEditLayer());
|
|
4423
5129
|
}
|
|
5130
|
+
/**
|
|
5131
|
+
* Create the dedicated layer used to highlight the queried feature, so
|
|
5132
|
+
* the source layers keep their own rendering untouched.
|
|
5133
|
+
* @private
|
|
5134
|
+
*/
|
|
5135
|
+
_prepareHighlightLayer() {
|
|
5136
|
+
this._highlightSource = new VectorSource();
|
|
5137
|
+
this._highlightLayer = new VectorLayer({
|
|
5138
|
+
source: this._highlightSource,
|
|
5139
|
+
style: (feature) => queryStyleFunction(),
|
|
5140
|
+
zIndex: 100
|
|
5141
|
+
});
|
|
5142
|
+
this._map.addLayer(this._highlightLayer);
|
|
5143
|
+
}
|
|
5144
|
+
/**
|
|
5145
|
+
* Listen for clicks while the query mode is active: get the clicked
|
|
5146
|
+
* feature of the active layer (from the already loaded source for WFS,
|
|
5147
|
+
* or through a GetFeatureInfo request for WMS), highlight it and show
|
|
5148
|
+
* its attributes in a popup.
|
|
5149
|
+
* @private
|
|
5150
|
+
*/
|
|
5151
|
+
_addQueryInteraction() {
|
|
5152
|
+
this._keyClickQuery = this._map.on(this._options.evtType || 'singleclick', async (evt) => {
|
|
5153
|
+
if (getMode() !== Modes.Query) {
|
|
5154
|
+
return;
|
|
5155
|
+
}
|
|
5156
|
+
const layer = getActiveLayerToInsertEls();
|
|
5157
|
+
// Skip hidden layers
|
|
5158
|
+
if (!layer.getVisible() || !layer.isVisibleByZoom()) {
|
|
5159
|
+
return;
|
|
5160
|
+
}
|
|
5161
|
+
let feature = null;
|
|
5162
|
+
if (layer instanceof WfsLayer) {
|
|
5163
|
+
const featuresAtPixel = this._map.getFeaturesAtPixel(evt.pixel, {
|
|
5164
|
+
hitTolerance: 10,
|
|
5165
|
+
layerFilter: (candidate) => candidate === layer
|
|
5166
|
+
});
|
|
5167
|
+
if (featuresAtPixel && featuresAtPixel.length) {
|
|
5168
|
+
feature = featuresAtPixel[0];
|
|
5169
|
+
}
|
|
5170
|
+
}
|
|
5171
|
+
else if (layer instanceof WmsLayer) {
|
|
5172
|
+
const features = await layer._getFeaturesByClickEvent(evt);
|
|
5173
|
+
if (features === null || features === void 0 ? void 0 : features.length) {
|
|
5174
|
+
feature = features[0];
|
|
5175
|
+
}
|
|
5176
|
+
}
|
|
5177
|
+
// Click on empty space: clear the previous result
|
|
5178
|
+
if (!feature) {
|
|
5179
|
+
this._clearQueryResult();
|
|
5180
|
+
return;
|
|
5181
|
+
}
|
|
5182
|
+
this._showQueryResult(feature, layer, evt.coordinate);
|
|
5183
|
+
});
|
|
5184
|
+
}
|
|
5185
|
+
/**
|
|
5186
|
+
* Highlight the given feature and attach a popup with its attributes.
|
|
5187
|
+
* @param feature
|
|
5188
|
+
* @param layer
|
|
5189
|
+
* @param coordinate
|
|
5190
|
+
* @private
|
|
5191
|
+
*/
|
|
5192
|
+
_showQueryResult(feature, layer, coordinate) {
|
|
5193
|
+
this._clearQueryResult();
|
|
5194
|
+
this._highlightSource.addFeature(feature);
|
|
5195
|
+
this._queryOverlay = new QueryOverlay(feature, coordinate, layer);
|
|
5196
|
+
// @ts-expect-error
|
|
5197
|
+
this._queryOverlay.on('close', () => {
|
|
5198
|
+
this._clearQueryResult();
|
|
5199
|
+
});
|
|
5200
|
+
this._map.addOverlay(this._queryOverlay);
|
|
5201
|
+
}
|
|
5202
|
+
/**
|
|
5203
|
+
* Remove the query popup overlay if any.
|
|
5204
|
+
* @private
|
|
5205
|
+
*/
|
|
5206
|
+
_removeQueryOverlay() {
|
|
5207
|
+
if (this._queryOverlay) {
|
|
5208
|
+
this._map.removeOverlay(this._queryOverlay);
|
|
5209
|
+
this._queryOverlay = null;
|
|
5210
|
+
return;
|
|
5211
|
+
}
|
|
5212
|
+
const overlay = this._map.getOverlayById(this._queryOverlayId);
|
|
5213
|
+
if (overlay) {
|
|
5214
|
+
this._map.removeOverlay(overlay);
|
|
5215
|
+
}
|
|
5216
|
+
}
|
|
5217
|
+
/**
|
|
5218
|
+
* Clear the highlight and remove the query popup.
|
|
5219
|
+
* @private
|
|
5220
|
+
*/
|
|
5221
|
+
_clearQueryResult() {
|
|
5222
|
+
var _a;
|
|
5223
|
+
(_a = this._highlightSource) === null || _a === void 0 ? void 0 : _a.clear();
|
|
5224
|
+
this._removeQueryOverlay();
|
|
5225
|
+
}
|
|
5226
|
+
/**
|
|
5227
|
+
* Activate/deactivate the query mode: while it is active, clicking a
|
|
5228
|
+
* feature of the active layer shows its info in a popup instead of
|
|
5229
|
+
* selecting it for editing.
|
|
5230
|
+
*
|
|
5231
|
+
* @param bool
|
|
5232
|
+
* @public
|
|
5233
|
+
*/
|
|
5234
|
+
activateQueryMode(bool = true) {
|
|
5235
|
+
var _a, _b;
|
|
5236
|
+
if (bool) {
|
|
5237
|
+
// Leave any draw interaction active
|
|
5238
|
+
if (this._interactionDraw) {
|
|
5239
|
+
this._map.removeInteraction(this._interactionDraw);
|
|
5240
|
+
this._viewport.classList.remove('draw-mode');
|
|
5241
|
+
}
|
|
5242
|
+
resetStateButtons();
|
|
5243
|
+
deactivateSelectModeButton();
|
|
5244
|
+
activateQueryButton();
|
|
5245
|
+
this._clearHoverState();
|
|
5246
|
+
activateMode(Modes.Query);
|
|
5247
|
+
// Disable the edit selection interactions while querying
|
|
5248
|
+
if (this._interactionSelectModify) {
|
|
5249
|
+
this._interactionSelectModify.setActive(false);
|
|
5250
|
+
}
|
|
5251
|
+
if (this._interactionWfsSelect) {
|
|
5252
|
+
this._interactionWfsSelect.setActive(false);
|
|
5253
|
+
}
|
|
5254
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(false);
|
|
5255
|
+
this._syncExtendLineOverlays();
|
|
5256
|
+
this._refreshSelectionInteractions();
|
|
5257
|
+
}
|
|
5258
|
+
else {
|
|
5259
|
+
activateMode(null);
|
|
5260
|
+
deactivateQueryButton();
|
|
5261
|
+
activateSelectModeButton(getSelectionMode());
|
|
5262
|
+
if (this._interactionSelectModify) {
|
|
5263
|
+
this._interactionSelectModify.setActive(true);
|
|
5264
|
+
}
|
|
5265
|
+
if (this._interactionWfsSelect) {
|
|
5266
|
+
this._interactionWfsSelect.setActive(true);
|
|
5267
|
+
}
|
|
5268
|
+
(_b = this._interactionModify) === null || _b === void 0 ? void 0 : _b.setActive(true);
|
|
5269
|
+
this._refreshSelectionInteractions();
|
|
5270
|
+
}
|
|
5271
|
+
this._clearQueryResult();
|
|
5272
|
+
}
|
|
4424
5273
|
/**
|
|
4425
5274
|
* @private
|
|
4426
5275
|
*/
|
|
@@ -4435,16 +5284,74 @@
|
|
|
4435
5284
|
return;
|
|
4436
5285
|
}
|
|
4437
5286
|
if (key === 'Delete') {
|
|
4438
|
-
const selectedFeatures = this._collectionModify;
|
|
4439
|
-
if (selectedFeatures) {
|
|
4440
|
-
|
|
4441
|
-
this._deleteFeature(feature, true);
|
|
4442
|
-
});
|
|
5287
|
+
const selectedFeatures = this._collectionModify.getArray();
|
|
5288
|
+
if (selectedFeatures.length) {
|
|
5289
|
+
this._deleteFeature(selectedFeatures, true);
|
|
4443
5290
|
}
|
|
4444
5291
|
}
|
|
4445
5292
|
});
|
|
4446
5293
|
};
|
|
4447
5294
|
keyboardEvents();
|
|
5295
|
+
/**
|
|
5296
|
+
* Change the cursor to a pointing hand and highlight the feature
|
|
5297
|
+
* when hovering over a WFS feature of the active layer.
|
|
5298
|
+
* @private
|
|
5299
|
+
*/
|
|
5300
|
+
const pointerMoveHandler = (evt) => {
|
|
5301
|
+
if (evt.dragging) {
|
|
5302
|
+
return;
|
|
5303
|
+
}
|
|
5304
|
+
// In edit mode, give pointer feedback on the editable
|
|
5305
|
+
// vertices/segments
|
|
5306
|
+
if (getMode() === Modes.Edit) {
|
|
5307
|
+
this._clearHoverState();
|
|
5308
|
+
this._updateEditCursor(evt);
|
|
5309
|
+
return;
|
|
5310
|
+
}
|
|
5311
|
+
// Only show the hover outside edit and draw modes
|
|
5312
|
+
if (getMode() !== null) {
|
|
5313
|
+
this._clearHoverState();
|
|
5314
|
+
return;
|
|
5315
|
+
}
|
|
5316
|
+
const layer = getActiveLayerToInsertEls();
|
|
5317
|
+
// Hover only works on visible WFS layers
|
|
5318
|
+
if (!layer ||
|
|
5319
|
+
!layer.getVisible() ||
|
|
5320
|
+
!layer.isVisibleByZoom() ||
|
|
5321
|
+
!(layer instanceof WfsLayer)) {
|
|
5322
|
+
this._clearHoverState();
|
|
5323
|
+
return;
|
|
5324
|
+
}
|
|
5325
|
+
const features = this._map.getFeaturesAtPixel(evt.pixel, {
|
|
5326
|
+
hitTolerance: 10,
|
|
5327
|
+
layerFilter: (candidate) => candidate === layer
|
|
5328
|
+
});
|
|
5329
|
+
if (features && features.length) {
|
|
5330
|
+
const feature = features[0];
|
|
5331
|
+
if (feature !== this._hoveredFeature) {
|
|
5332
|
+
this._clearHoverState();
|
|
5333
|
+
this._hoveredFeature = feature;
|
|
5334
|
+
this._hoveredFeature.setStyle((f) => styleFunction(f, true));
|
|
5335
|
+
}
|
|
5336
|
+
if (this._viewport.style.cursor !== 'pointer') {
|
|
5337
|
+
this._viewport.style.cursor = 'pointer';
|
|
5338
|
+
}
|
|
5339
|
+
}
|
|
5340
|
+
else {
|
|
5341
|
+
this._clearHoverState();
|
|
5342
|
+
}
|
|
5343
|
+
};
|
|
5344
|
+
this._map.on('pointermove', pointerMoveHandler);
|
|
5345
|
+
// Store the shift state when a gesture starts, so the freehand
|
|
5346
|
+
// selection can decide between adding or replacing the selection
|
|
5347
|
+
this._viewport.addEventListener('pointerdown', (evt) => {
|
|
5348
|
+
this._shiftPressed = evt.shiftKey;
|
|
5349
|
+
});
|
|
5350
|
+
// Clear the hover before firing a click, otherwise the Select interaction
|
|
5351
|
+
// would capture the hover style as the original style of the feature and
|
|
5352
|
+
// restore it on deselect, leaving the highlight permanently enabled.
|
|
5353
|
+
this._viewport.addEventListener('pointerdown', () => this._clearHoverState());
|
|
5354
|
+
this._viewport.addEventListener('pointerleave', () => this._clearHoverState(), { once: false });
|
|
4448
5355
|
this._map.on('moveend', () => {
|
|
4449
5356
|
this._currentZoom = this._view.getZoom();
|
|
4450
5357
|
if (this._currentZoom !== this._lastZoom) {
|
|
@@ -4468,6 +5375,62 @@
|
|
|
4468
5375
|
}
|
|
4469
5376
|
});
|
|
4470
5377
|
}
|
|
5378
|
+
/**
|
|
5379
|
+
* Allow panning with the middle mouse button at all times, even when the
|
|
5380
|
+
* draw/freehand/box interactions are consuming the left button. The
|
|
5381
|
+
* middle pointerdown is intercepted in the capture phase so OpenLayers
|
|
5382
|
+
* never processes it and the active tool is not affected.
|
|
5383
|
+
*
|
|
5384
|
+
* @private
|
|
5385
|
+
*/
|
|
5386
|
+
_addMiddleButtonPan() {
|
|
5387
|
+
const viewport = this._viewport;
|
|
5388
|
+
let panning = false;
|
|
5389
|
+
let lastX = 0;
|
|
5390
|
+
let lastY = 0;
|
|
5391
|
+
const isMiddleButton = (evt) => evt.pointerType === 'mouse' && evt.button === 1;
|
|
5392
|
+
const stopPanning = () => {
|
|
5393
|
+
panning = false;
|
|
5394
|
+
};
|
|
5395
|
+
viewport.addEventListener('pointerdown', (evt) => {
|
|
5396
|
+
if (!isMiddleButton(evt)) {
|
|
5397
|
+
return;
|
|
5398
|
+
}
|
|
5399
|
+
evt.preventDefault();
|
|
5400
|
+
evt.stopPropagation();
|
|
5401
|
+
viewport.setPointerCapture(evt.pointerId);
|
|
5402
|
+
panning = true;
|
|
5403
|
+
lastX = evt.clientX;
|
|
5404
|
+
lastY = evt.clientY;
|
|
5405
|
+
if (this._view.getAnimating()) {
|
|
5406
|
+
this._view.cancelAnimations();
|
|
5407
|
+
}
|
|
5408
|
+
}, true);
|
|
5409
|
+
viewport.addEventListener('pointermove', (evt) => {
|
|
5410
|
+
if (!panning) {
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
evt.preventDefault();
|
|
5414
|
+
const delta = [lastX - evt.clientX, evt.clientY - lastY];
|
|
5415
|
+
lastX = evt.clientX;
|
|
5416
|
+
lastY = evt.clientY;
|
|
5417
|
+
const resolution = this._view.getResolution();
|
|
5418
|
+
const rotation = this._view.getRotation();
|
|
5419
|
+
const cosAngle = Math.cos(rotation);
|
|
5420
|
+
const sinAngle = Math.sin(rotation);
|
|
5421
|
+
const x = (delta[0] * cosAngle - delta[1] * sinAngle) * resolution;
|
|
5422
|
+
const y = (delta[1] * cosAngle + delta[0] * sinAngle) * resolution;
|
|
5423
|
+
this._view.adjustCenterInternal([x, y]);
|
|
5424
|
+
});
|
|
5425
|
+
const endPan = () => {
|
|
5426
|
+
if (!panning) {
|
|
5427
|
+
return;
|
|
5428
|
+
}
|
|
5429
|
+
stopPanning();
|
|
5430
|
+
};
|
|
5431
|
+
viewport.addEventListener('pointerup', endPan);
|
|
5432
|
+
viewport.addEventListener('pointercancel', endPan);
|
|
5433
|
+
}
|
|
4471
5434
|
/**
|
|
4472
5435
|
* Add map handlers
|
|
4473
5436
|
* @private
|
|
@@ -4477,13 +5440,19 @@
|
|
|
4477
5440
|
// This prevent events fired on select and deselect features that has no changes and should
|
|
4478
5441
|
// not be updated in the geoserver
|
|
4479
5442
|
this._interactionModify.on('modifyend', (evt) => {
|
|
4480
|
-
|
|
4481
|
-
|
|
5443
|
+
evt.features.forEach((feature) => {
|
|
5444
|
+
addFeatureToEditedList(feature);
|
|
5445
|
+
});
|
|
4482
5446
|
super.dispatchEvent(evt);
|
|
4483
5447
|
});
|
|
4484
5448
|
this._interactionModify.on('modifystart', (evt) => {
|
|
4485
5449
|
super.dispatchEvent(evt);
|
|
4486
5450
|
});
|
|
5451
|
+
// Keep the "continue drawing" buttons glued to the line endpoints
|
|
5452
|
+
// @ts-expect-error - 'modify' is a valid Modify event not typed in the `on` signature
|
|
5453
|
+
this._interactionModify.on('modify', () => {
|
|
5454
|
+
this._updateExtendOverlayPositions();
|
|
5455
|
+
});
|
|
4487
5456
|
this._onDeselectFeatureEvent();
|
|
4488
5457
|
this._onRemoveFeatureEvent();
|
|
4489
5458
|
}
|
|
@@ -4495,6 +5464,9 @@
|
|
|
4495
5464
|
this._layersControl = new LayersControl(this._options.showUpload ? this._uploads : null, this._options.uploadFormats);
|
|
4496
5465
|
// @ts-expect-error
|
|
4497
5466
|
this._layersControl.on('drawMode', () => {
|
|
5467
|
+
if (getMode() === Modes.Query) {
|
|
5468
|
+
this.activateQueryMode(false);
|
|
5469
|
+
}
|
|
4498
5470
|
if (getMode() === Modes.Draw) {
|
|
4499
5471
|
resetStateButtons();
|
|
4500
5472
|
this.activateEditMode();
|
|
@@ -4521,9 +5493,37 @@
|
|
|
4521
5493
|
this.activateDrawMode(getActiveLayerToInsertEls());
|
|
4522
5494
|
}
|
|
4523
5495
|
});
|
|
5496
|
+
[
|
|
5497
|
+
['selectModeSingle', SelectionMode.Single],
|
|
5498
|
+
['selectModeBox', SelectionMode.Box],
|
|
5499
|
+
['selectModeFreehand', SelectionMode.Freehand]
|
|
5500
|
+
].forEach(([evtName, mode]) => {
|
|
5501
|
+
// @ts-expect-error
|
|
5502
|
+
this._layersControl.on(evtName, () => {
|
|
5503
|
+
// Leaving the draw mode when a select tool is chosen
|
|
5504
|
+
if (getMode() === Modes.Draw) {
|
|
5505
|
+
resetStateButtons();
|
|
5506
|
+
this.activateEditMode();
|
|
5507
|
+
}
|
|
5508
|
+
// Leaving the query mode when a select tool is chosen
|
|
5509
|
+
if (getMode() === Modes.Query) {
|
|
5510
|
+
this.activateQueryMode(false);
|
|
5511
|
+
}
|
|
5512
|
+
setSelectionMode(mode);
|
|
5513
|
+
activateSelectModeButton(mode);
|
|
5514
|
+
this._refreshSelectionInteractions();
|
|
5515
|
+
});
|
|
5516
|
+
});
|
|
5517
|
+
// @ts-expect-error
|
|
5518
|
+
this._layersControl.on('queryMode', () => {
|
|
5519
|
+
this.activateQueryMode(getMode() !== Modes.Query);
|
|
5520
|
+
});
|
|
4524
5521
|
const controlEl = this._layersControl.render();
|
|
4525
5522
|
this._selectDraw = controlEl.querySelector('.wfst--tools-control--select-draw');
|
|
4526
|
-
this._controlWidgetToolsDiv.append(controlEl);
|
|
5523
|
+
this._controlWidgetToolsDiv.append(controlEl.querySelector('.wfst--tools-control--head'));
|
|
5524
|
+
if (this._options.layers.length > 1) {
|
|
5525
|
+
this._layersWidgetDiv.append(controlEl.querySelector('.wfst--tools-control--select-layers'));
|
|
5526
|
+
}
|
|
4527
5527
|
}
|
|
4528
5528
|
/**
|
|
4529
5529
|
*
|
|
@@ -4542,6 +5542,9 @@
|
|
|
4542
5542
|
_restoreFeatureToLayer(feature, layerName) {
|
|
4543
5543
|
layerName = layerName || feature.get('_layerName_');
|
|
4544
5544
|
const layer = getStoredMapLayers()[layerName];
|
|
5545
|
+
// Reset any per-feature style (e.g. hover) before returning the feature
|
|
5546
|
+
// to its layer, so it renders with the layer's default style.
|
|
5547
|
+
feature.setStyle(undefined);
|
|
4545
5548
|
layer.getSource().addFeature(feature);
|
|
4546
5549
|
}
|
|
4547
5550
|
/**
|
|
@@ -4575,10 +5578,18 @@
|
|
|
4575
5578
|
this._removeFeatureFromTmpLayer(feature);
|
|
4576
5579
|
}
|
|
4577
5580
|
};
|
|
5581
|
+
if (this._keySelectBound) {
|
|
5582
|
+
return;
|
|
5583
|
+
}
|
|
5584
|
+
this._keySelectBound = true;
|
|
4578
5585
|
// This is fired when a feature is deselected and fires the transaction process
|
|
5586
|
+
if (this._keySelect) {
|
|
5587
|
+
Observable.unByKey(this._keySelect);
|
|
5588
|
+
}
|
|
4579
5589
|
this._keySelect = this._collectionModify.on('remove', (evt) => {
|
|
4580
5590
|
const feature = evt.element;
|
|
4581
5591
|
this._deselectEditFeature(feature);
|
|
5592
|
+
this._syncEditOverlays();
|
|
4582
5593
|
checkIfFeatureIsChanged(feature);
|
|
4583
5594
|
this._editModeOff();
|
|
4584
5595
|
});
|
|
@@ -4599,6 +5610,7 @@
|
|
|
4599
5610
|
}
|
|
4600
5611
|
if (this._keySelect) {
|
|
4601
5612
|
Observable.unByKey(this._keySelect);
|
|
5613
|
+
this._keySelectBound = false;
|
|
4602
5614
|
}
|
|
4603
5615
|
const layerName = feature.get('_layerName_');
|
|
4604
5616
|
const ll = this.getLayerByName(layerName);
|
|
@@ -4614,27 +5626,52 @@
|
|
|
4614
5626
|
}
|
|
4615
5627
|
/**
|
|
4616
5628
|
*
|
|
4617
|
-
* @param
|
|
5629
|
+
* @param features
|
|
4618
5630
|
* @private
|
|
4619
5631
|
*/
|
|
4620
|
-
_editModeOn(
|
|
4621
|
-
|
|
5632
|
+
_editModeOn(features) {
|
|
5633
|
+
features = Array.isArray(features) ? features : [features];
|
|
5634
|
+
features.forEach((feature) => {
|
|
5635
|
+
this._editFeaturesOriginal[String(feature.getId())] =
|
|
5636
|
+
feature.clone();
|
|
5637
|
+
});
|
|
4622
5638
|
activateMode(Modes.Edit);
|
|
5639
|
+
// Show the "continue drawing" buttons on the line endpoints
|
|
5640
|
+
this._syncExtendLineOverlays();
|
|
4623
5641
|
// To refresh the style
|
|
4624
5642
|
getEditLayer().getSource().changed();
|
|
4625
|
-
this.
|
|
4626
|
-
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4630
|
-
|
|
5643
|
+
this._refreshSelectionInteractions();
|
|
5644
|
+
const multiOverlay = this._map.getOverlayById(this._multiOverlayId);
|
|
5645
|
+
if (multiOverlay) {
|
|
5646
|
+
this._map.removeOverlay(multiOverlay);
|
|
5647
|
+
}
|
|
5648
|
+
features.forEach((feature) => {
|
|
5649
|
+
this._removeOverlayHelper(feature);
|
|
5650
|
+
});
|
|
5651
|
+
this._controlApplyDiscardChanges = new EditControlChangesEl(features);
|
|
5652
|
+
this._controlApplyDiscardChanges.on('cancel', (evt) => {
|
|
5653
|
+
const list = evt.features;
|
|
5654
|
+
Array.from(list).forEach((feature) => {
|
|
5655
|
+
const original = this._editFeaturesOriginal[String(feature.getId())];
|
|
5656
|
+
if (original) {
|
|
5657
|
+
feature.setGeometry(original.getGeometry());
|
|
5658
|
+
}
|
|
5659
|
+
removeFeatureFromEditList(feature);
|
|
5660
|
+
this._collectionModify.remove(feature);
|
|
5661
|
+
});
|
|
4631
5662
|
});
|
|
4632
|
-
this._controlApplyDiscardChanges.on('apply', (
|
|
5663
|
+
this._controlApplyDiscardChanges.on('apply', async (evt) => {
|
|
4633
5664
|
showLoading();
|
|
4634
|
-
|
|
5665
|
+
const list = evt.features;
|
|
5666
|
+
const ok = await this._transactEditList(list);
|
|
5667
|
+
if (ok) {
|
|
5668
|
+
Array.from(list).forEach((feature) => {
|
|
5669
|
+
this._collectionModify.remove(feature);
|
|
5670
|
+
});
|
|
5671
|
+
}
|
|
4635
5672
|
});
|
|
4636
|
-
this._controlApplyDiscardChanges.on('delete', (
|
|
4637
|
-
this._deleteFeature(
|
|
5673
|
+
this._controlApplyDiscardChanges.on('delete', (evt) => {
|
|
5674
|
+
this._deleteFeature(evt.features, true);
|
|
4638
5675
|
});
|
|
4639
5676
|
this._map.addControl(this._controlApplyDiscardChanges);
|
|
4640
5677
|
}
|
|
@@ -4643,30 +5680,331 @@
|
|
|
4643
5680
|
*/
|
|
4644
5681
|
_editModeOff() {
|
|
4645
5682
|
activateMode(null);
|
|
5683
|
+
this._destroyExtendLineOverlays();
|
|
4646
5684
|
this._map.removeControl(this._controlApplyDiscardChanges);
|
|
5685
|
+
this._refreshSelectionInteractions();
|
|
5686
|
+
}
|
|
5687
|
+
/**
|
|
5688
|
+
* Get the first and last coordinate of every line component of a
|
|
5689
|
+
* LineString/MultiLineString geometry, including the line components
|
|
5690
|
+
* nested inside a GeometryCollection. Anything else returns an empty
|
|
5691
|
+
* array.
|
|
5692
|
+
*
|
|
5693
|
+
* @param geometry
|
|
5694
|
+
* @private
|
|
5695
|
+
*/
|
|
5696
|
+
_getLineEndPoints(geometry) {
|
|
5697
|
+
const endPoints = [];
|
|
5698
|
+
const appendLine = (line, collection) => {
|
|
5699
|
+
if (line instanceof LineString) {
|
|
5700
|
+
if (line.getCoordinates().length) {
|
|
5701
|
+
endPoints.push({
|
|
5702
|
+
first: line.getFirstCoordinate(),
|
|
5703
|
+
last: line.getLastCoordinate(),
|
|
5704
|
+
line,
|
|
5705
|
+
lineIndex: 0,
|
|
5706
|
+
collection
|
|
5707
|
+
});
|
|
5708
|
+
}
|
|
5709
|
+
return;
|
|
5710
|
+
}
|
|
5711
|
+
line.getCoordinates().forEach((coordinates, lineIndex) => {
|
|
5712
|
+
if (coordinates.length) {
|
|
5713
|
+
endPoints.push({
|
|
5714
|
+
first: coordinates[0],
|
|
5715
|
+
last: coordinates[coordinates.length - 1],
|
|
5716
|
+
line,
|
|
5717
|
+
lineIndex,
|
|
5718
|
+
collection
|
|
5719
|
+
});
|
|
5720
|
+
}
|
|
5721
|
+
});
|
|
5722
|
+
};
|
|
5723
|
+
// "getGeometries()" returns deep clones (see ol GeometryCollection),
|
|
5724
|
+
// but the extend/merge must work on the actual members.
|
|
5725
|
+
const collect = (geometry, collection) => {
|
|
5726
|
+
if (geometry instanceof LineString ||
|
|
5727
|
+
geometry instanceof MultiLineString) {
|
|
5728
|
+
appendLine(geometry, collection);
|
|
5729
|
+
}
|
|
5730
|
+
else if (geometry instanceof GeometryCollection) {
|
|
5731
|
+
geometry.getGeometriesArray().forEach((member) => {
|
|
5732
|
+
collect(member, geometry);
|
|
5733
|
+
});
|
|
5734
|
+
}
|
|
5735
|
+
};
|
|
5736
|
+
collect(geometry);
|
|
5737
|
+
return endPoints;
|
|
5738
|
+
}
|
|
5739
|
+
/**
|
|
5740
|
+
* Remove every "continue drawing" overlay and abort any active line
|
|
5741
|
+
* extension draw.
|
|
5742
|
+
*
|
|
5743
|
+
* @private
|
|
5744
|
+
*/
|
|
5745
|
+
_destroyExtendLineOverlays() {
|
|
5746
|
+
if (this._extendDraw) {
|
|
5747
|
+
this._finishExtendDraw();
|
|
5748
|
+
}
|
|
5749
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5750
|
+
this._map.removeOverlay(overlay);
|
|
5751
|
+
});
|
|
5752
|
+
this._extendOverlays = [];
|
|
5753
|
+
}
|
|
5754
|
+
/**
|
|
5755
|
+
* Keep the "continue drawing" overlays in sync with the current edit
|
|
5756
|
+
* selection: they are only shown while editing LineString/MultiLineString
|
|
5757
|
+
* features, and they are repositioned on the endpoints of each line
|
|
5758
|
+
* component.
|
|
5759
|
+
*
|
|
5760
|
+
* @private
|
|
5761
|
+
*/
|
|
5762
|
+
_syncExtendLineOverlays() {
|
|
5763
|
+
if (getMode() !== Modes.Edit || !this._collectionModify) {
|
|
5764
|
+
this._destroyExtendLineOverlays();
|
|
5765
|
+
return;
|
|
5766
|
+
}
|
|
5767
|
+
const items = this._collectionModify.getArray();
|
|
5768
|
+
// Remove overlays whose feature was deselected or lost its line geometry
|
|
5769
|
+
const removed = [];
|
|
5770
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5771
|
+
const stale = !items.includes(overlay.feature) ||
|
|
5772
|
+
overlay.component >=
|
|
5773
|
+
this._getLineEndPoints(overlay.feature.getGeometry())
|
|
5774
|
+
.length;
|
|
5775
|
+
if (stale) {
|
|
5776
|
+
this._map.removeOverlay(overlay);
|
|
5777
|
+
removed.push(overlay);
|
|
5778
|
+
}
|
|
5779
|
+
});
|
|
5780
|
+
this._extendOverlays = this._extendOverlays.filter((overlay) => !removed.includes(overlay));
|
|
5781
|
+
// Add overlays for new LineString/MultiLineString features
|
|
5782
|
+
items.forEach((feature) => {
|
|
5783
|
+
this._getLineEndPoints(feature.getGeometry()).forEach(({ first, last }, component) => {
|
|
5784
|
+
['start', 'end'].forEach((side) => {
|
|
5785
|
+
const exists = this._extendOverlays.some((overlay) => overlay.feature === feature &&
|
|
5786
|
+
overlay.component === component &&
|
|
5787
|
+
overlay.side === side);
|
|
5788
|
+
if (exists) {
|
|
5789
|
+
return;
|
|
5790
|
+
}
|
|
5791
|
+
const overlay = new ExtendLineOverlay(feature, side === 'start' ? first : last, side, component, () => this._startExtendLineDraw(feature, side, component));
|
|
5792
|
+
this._extendOverlays.push(overlay);
|
|
5793
|
+
this._map.addOverlay(overlay);
|
|
5794
|
+
});
|
|
5795
|
+
});
|
|
5796
|
+
});
|
|
5797
|
+
this._updateExtendOverlayPositions();
|
|
4647
5798
|
}
|
|
4648
5799
|
/**
|
|
4649
|
-
*
|
|
5800
|
+
* Move the "continue drawing" overlays to the current position of their
|
|
5801
|
+
* line endpoints, e.g. after a vertex is dragged with the Modify
|
|
5802
|
+
* interaction.
|
|
5803
|
+
*
|
|
5804
|
+
* @private
|
|
5805
|
+
*/
|
|
5806
|
+
_updateExtendOverlayPositions() {
|
|
5807
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5808
|
+
const point = this._getLineEndPoints(overlay.feature.getGeometry())[overlay.component];
|
|
5809
|
+
if (!point) {
|
|
5810
|
+
return;
|
|
5811
|
+
}
|
|
5812
|
+
overlay.setPosition(overlay.side === 'start' ? point.first : point.last);
|
|
5813
|
+
});
|
|
5814
|
+
}
|
|
5815
|
+
/**
|
|
5816
|
+
* Start drawing a continuation of a line from one of its endpoints. The
|
|
5817
|
+
* sketch is anchored on the clicked vertex, so the next clicks append the
|
|
5818
|
+
* new points. A double click (or clicking back on the last point) ends
|
|
5819
|
+
* the drawing and merges the segment into the feature.
|
|
5820
|
+
*
|
|
5821
|
+
* @param feature
|
|
5822
|
+
* @param side 'start' prepends the new segment, 'end' appends it
|
|
5823
|
+
* @param component index of the clicked line component
|
|
5824
|
+
* @private
|
|
5825
|
+
*/
|
|
5826
|
+
_startExtendLineDraw(feature, side, component = 0) {
|
|
5827
|
+
var _a;
|
|
5828
|
+
if (this._extendDraw || getMode() !== Modes.Edit) {
|
|
5829
|
+
return;
|
|
5830
|
+
}
|
|
5831
|
+
const points = this._getLineEndPoints(feature.getGeometry());
|
|
5832
|
+
if (!points.length || component >= points.length) {
|
|
5833
|
+
return;
|
|
5834
|
+
}
|
|
5835
|
+
const anchor = side === 'start' ? points[component].first : points[component].last;
|
|
5836
|
+
// While extending, the Modify/Select interactions must not grab the clicks
|
|
5837
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(false);
|
|
5838
|
+
if (this._interactionSelectModify) {
|
|
5839
|
+
this._interactionSelectModify.setActive(false);
|
|
5840
|
+
}
|
|
5841
|
+
if (this._interactionWfsSelect) {
|
|
5842
|
+
this._interactionWfsSelect.setActive(false);
|
|
5843
|
+
}
|
|
5844
|
+
this._extendDraw = new Draw({
|
|
5845
|
+
type: GeometryType.LineString,
|
|
5846
|
+
style: (f) => styleFunction(f),
|
|
5847
|
+
stopClick: true // To prevent firing a map/wms click
|
|
5848
|
+
});
|
|
5849
|
+
this._extendDraw.on('drawend', (evt) => {
|
|
5850
|
+
this._mergeExtendLine(feature, side, evt.feature, component);
|
|
5851
|
+
});
|
|
5852
|
+
this._extendDraw.on('drawabort', () => {
|
|
5853
|
+
this._finishExtendDraw();
|
|
5854
|
+
});
|
|
5855
|
+
this._map.addInteraction(this._extendDraw);
|
|
5856
|
+
// Anchor the sketch on the clicked vertex: the user only has to digitize
|
|
5857
|
+
// the continuation points (this also dispatches the "drawstart" event)
|
|
5858
|
+
this._extendDraw.appendCoordinates([anchor]);
|
|
5859
|
+
}
|
|
5860
|
+
/**
|
|
5861
|
+
* Merge the just-drawn segment into the edited feature's line geometry and
|
|
5862
|
+
* restore the editing interactions.
|
|
4650
5863
|
*
|
|
4651
5864
|
* @param feature
|
|
5865
|
+
* @param side
|
|
5866
|
+
* @param sketch
|
|
5867
|
+
* @param component index of the extended line component
|
|
5868
|
+
* @private
|
|
5869
|
+
*/
|
|
5870
|
+
_mergeExtendLine(feature, side, sketch, component = 0) {
|
|
5871
|
+
const points = this._getLineEndPoints(feature.getGeometry());
|
|
5872
|
+
const point = points[component];
|
|
5873
|
+
if (!point) {
|
|
5874
|
+
this._finishExtendDraw();
|
|
5875
|
+
return;
|
|
5876
|
+
}
|
|
5877
|
+
const segment = sketch.getGeometry().getCoordinates();
|
|
5878
|
+
const { line, lineIndex } = point;
|
|
5879
|
+
if (line instanceof LineString) {
|
|
5880
|
+
const coords = line.getCoordinates();
|
|
5881
|
+
if (side === 'start') {
|
|
5882
|
+
line.setCoordinates([...segment.slice(1).reverse(), ...coords]);
|
|
5883
|
+
}
|
|
5884
|
+
else {
|
|
5885
|
+
line.setCoordinates([...coords, ...segment.slice(1)]);
|
|
5886
|
+
}
|
|
5887
|
+
}
|
|
5888
|
+
else if (line instanceof MultiLineString) {
|
|
5889
|
+
const coords = line.getCoordinates();
|
|
5890
|
+
if (side === 'start') {
|
|
5891
|
+
coords[lineIndex] = [
|
|
5892
|
+
...segment.slice(1).reverse(),
|
|
5893
|
+
...coords[lineIndex]
|
|
5894
|
+
];
|
|
5895
|
+
}
|
|
5896
|
+
else {
|
|
5897
|
+
coords[lineIndex] = [...coords[lineIndex], ...segment.slice(1)];
|
|
5898
|
+
}
|
|
5899
|
+
line.setCoordinates(coords);
|
|
5900
|
+
}
|
|
5901
|
+
// Only mark the feature as edited when the segment added new vertices
|
|
5902
|
+
if (segment.length > 1) {
|
|
5903
|
+
addFeatureToEditedList(feature);
|
|
5904
|
+
}
|
|
5905
|
+
this._finishExtendDraw();
|
|
5906
|
+
this._syncExtendLineOverlays();
|
|
5907
|
+
}
|
|
5908
|
+
/**
|
|
5909
|
+
* Remove the active line extension draw and restore the interactions that
|
|
5910
|
+
* were disabled while drawing.
|
|
5911
|
+
*
|
|
5912
|
+
* @private
|
|
5913
|
+
*/
|
|
5914
|
+
_finishExtendDraw() {
|
|
5915
|
+
var _a;
|
|
5916
|
+
if (this._extendDraw) {
|
|
5917
|
+
this._map.removeInteraction(this._extendDraw);
|
|
5918
|
+
this._extendDraw = null;
|
|
5919
|
+
}
|
|
5920
|
+
if (getMode() !== Modes.Edit) {
|
|
5921
|
+
return;
|
|
5922
|
+
}
|
|
5923
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(true);
|
|
5924
|
+
if (this._interactionSelectModify) {
|
|
5925
|
+
this._interactionSelectModify.setActive(true);
|
|
5926
|
+
}
|
|
5927
|
+
if (this._interactionWfsSelect) {
|
|
5928
|
+
this._interactionWfsSelect.setActive(true);
|
|
5929
|
+
}
|
|
5930
|
+
}
|
|
5931
|
+
/**
|
|
5932
|
+
* Send all the given features in a single WFS-T Update transaction per
|
|
5933
|
+
* layer, so a multi selection is saved with only one request. The "edited"
|
|
5934
|
+
* flag is cleared before transacting, so a deselect fired while the
|
|
5935
|
+
* request is in flight does not trigger a duplicate transaction.
|
|
5936
|
+
*
|
|
5937
|
+
* @param list
|
|
5938
|
+
* @returns false when there is nothing to transact or the transaction fails
|
|
5939
|
+
* @private
|
|
5940
|
+
*/
|
|
5941
|
+
async _transactEditList(list) {
|
|
5942
|
+
if (!list.length) {
|
|
5943
|
+
return true;
|
|
5944
|
+
}
|
|
5945
|
+
const byLayer = {};
|
|
5946
|
+
list.forEach((feature) => {
|
|
5947
|
+
const layerName = feature.get('_layerName_');
|
|
5948
|
+
if (!byLayer[layerName]) {
|
|
5949
|
+
byLayer[layerName] = [];
|
|
5950
|
+
}
|
|
5951
|
+
byLayer[layerName].push(feature);
|
|
5952
|
+
});
|
|
5953
|
+
for (const layerName of Object.keys(byLayer)) {
|
|
5954
|
+
const layer = this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName);
|
|
5955
|
+
if (!layer) {
|
|
5956
|
+
continue;
|
|
5957
|
+
}
|
|
5958
|
+
byLayer[layerName].forEach((feature) => {
|
|
5959
|
+
removeFeatureFromEditList(feature);
|
|
5960
|
+
});
|
|
5961
|
+
let ok;
|
|
5962
|
+
try {
|
|
5963
|
+
ok = await layer.transactFeatures(TransactionType.Update, byLayer[layerName]);
|
|
5964
|
+
}
|
|
5965
|
+
catch (err) {
|
|
5966
|
+
return false;
|
|
5967
|
+
}
|
|
5968
|
+
if (ok === false) {
|
|
5969
|
+
return false;
|
|
5970
|
+
}
|
|
5971
|
+
}
|
|
5972
|
+
return true;
|
|
5973
|
+
}
|
|
5974
|
+
/**
|
|
5975
|
+
* Remove features from the edit Layer and from the Geoserver
|
|
5976
|
+
*
|
|
5977
|
+
* @param feature
|
|
5978
|
+
* @param confirm
|
|
4652
5979
|
* @private
|
|
4653
5980
|
*/
|
|
4654
5981
|
_deleteFeature(feature, confirm) {
|
|
4655
5982
|
const deleteEl = () => {
|
|
5983
|
+
var _a;
|
|
4656
5984
|
const features = Array.isArray(feature) ? feature : [feature];
|
|
4657
5985
|
features.forEach((feature) => {
|
|
4658
5986
|
feature.set('_delete_', true, true);
|
|
4659
5987
|
getEditLayer().getSource().removeFeature(feature);
|
|
4660
5988
|
});
|
|
4661
5989
|
this._collectionModify.clear();
|
|
4662
|
-
|
|
4663
|
-
|
|
5990
|
+
this._editModeOff();
|
|
5991
|
+
this._syncEditOverlays();
|
|
5992
|
+
const layerName = (_a = features[0]) === null || _a === void 0 ? void 0 : _a.get('_layerName_');
|
|
5993
|
+
const layer = layerName
|
|
5994
|
+
? this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName)
|
|
5995
|
+
: null;
|
|
4664
5996
|
if (layer instanceof WfsLayer) {
|
|
4665
|
-
|
|
5997
|
+
features.forEach((feature) => {
|
|
5998
|
+
this._interactionWfsSelect.getFeatures().remove(feature);
|
|
5999
|
+
});
|
|
4666
6000
|
}
|
|
4667
6001
|
};
|
|
4668
6002
|
if (confirm) {
|
|
4669
|
-
const
|
|
6003
|
+
const features = Array.isArray(feature) ? feature : [feature];
|
|
6004
|
+
const message = features.length > 1
|
|
6005
|
+
? I18N_('confirmDeleteElements', features.length)
|
|
6006
|
+
: I18N.labels.confirmDelete;
|
|
6007
|
+
const confirmModal = Modal.confirm(message, Object.assign({}, this._options.modal));
|
|
4670
6008
|
confirmModal.show().once('dismiss', function (modal, ev, button) {
|
|
4671
6009
|
if (button && button.value) {
|
|
4672
6010
|
deleteEl();
|
|
@@ -4686,33 +6024,102 @@
|
|
|
4686
6024
|
* @private
|
|
4687
6025
|
*/
|
|
4688
6026
|
_addFeatureToEditMode(feature, coordinate = null, layerName = null) {
|
|
4689
|
-
// For now, only allow one element at time
|
|
4690
|
-
// @TODO: allow edit multiples elements
|
|
4691
|
-
if (this._collectionModify.getLength())
|
|
4692
|
-
return;
|
|
4693
6027
|
if (layerName) {
|
|
4694
6028
|
// Store the layer information inside the feature
|
|
4695
6029
|
feature.set('_layerName_', layerName);
|
|
4696
6030
|
}
|
|
6031
|
+
if (coordinate) {
|
|
6032
|
+
// Keep the click coordinate to position the overlay helper
|
|
6033
|
+
feature.set('_editOverlayCoord_', coordinate);
|
|
6034
|
+
}
|
|
4697
6035
|
const props = feature ? feature.getProperties() : '';
|
|
4698
6036
|
if (props) {
|
|
4699
6037
|
if (feature.getGeometry()) {
|
|
4700
6038
|
getEditLayer().getSource().addFeature(feature);
|
|
4701
6039
|
this._collectionModify.push(feature);
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
4709
|
-
|
|
4710
|
-
|
|
4711
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
6040
|
+
this._syncEditOverlays();
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
}
|
|
6044
|
+
/**
|
|
6045
|
+
* Lock the whole current selection with a single LockFeature request per
|
|
6046
|
+
* layer, so the transaction LockId covers all the selected features
|
|
6047
|
+
*
|
|
6048
|
+
* @private
|
|
6049
|
+
*/
|
|
6050
|
+
async _lockSelectedFeatures() {
|
|
6051
|
+
const byLayer = {};
|
|
6052
|
+
this._collectionModify.getArray().forEach((selected) => {
|
|
6053
|
+
const layerName = selected.get('_layerName_');
|
|
6054
|
+
if (!layerName) {
|
|
6055
|
+
return;
|
|
6056
|
+
}
|
|
6057
|
+
(byLayer[layerName] || (byLayer[layerName] = [])).push(selected.getId());
|
|
6058
|
+
});
|
|
6059
|
+
const locks = [];
|
|
6060
|
+
Object.keys(byLayer).forEach((layerName) => {
|
|
6061
|
+
const layer = getStoredLayer(layerName);
|
|
6062
|
+
if (layer) {
|
|
6063
|
+
locks.push(layer.maybeLockFeature(byLayer[layerName]));
|
|
6064
|
+
}
|
|
6065
|
+
});
|
|
6066
|
+
if (locks.length) {
|
|
6067
|
+
await Promise.all(locks);
|
|
6068
|
+
}
|
|
6069
|
+
}
|
|
6070
|
+
/**
|
|
6071
|
+
* Create an EditOverlay helper and attach the edit listeners
|
|
6072
|
+
*
|
|
6073
|
+
* @param feature
|
|
6074
|
+
* @param coordinate
|
|
6075
|
+
* @param id
|
|
6076
|
+
* @private
|
|
6077
|
+
*/
|
|
6078
|
+
_createEditOverlay(feature = null, coordinate = null, id = null) {
|
|
6079
|
+
coordinate =
|
|
6080
|
+
coordinate || (feature ? feature.get('_editOverlayCoord_') : null);
|
|
6081
|
+
const overlay = new EditOverlay(feature, coordinate, id);
|
|
6082
|
+
// @ts-expect-error
|
|
6083
|
+
overlay.on('editFields', () => {
|
|
6084
|
+
this._editFields.show(this._collectionModify.getArray());
|
|
6085
|
+
});
|
|
6086
|
+
// @ts-expect-error
|
|
6087
|
+
overlay.on('editGeom', () => {
|
|
6088
|
+
this._editModeOn(this._collectionModify.getArray());
|
|
6089
|
+
});
|
|
6090
|
+
this._map.addOverlay(overlay);
|
|
6091
|
+
}
|
|
6092
|
+
/**
|
|
6093
|
+
* Sync the edit overlay helpers with the current selection: a single
|
|
6094
|
+
* per-feature overlay while a feature is selected, or a unique overlay
|
|
6095
|
+
* centered between all the selected features when editing several at once
|
|
6096
|
+
*
|
|
6097
|
+
* @private
|
|
6098
|
+
*/
|
|
6099
|
+
_syncEditOverlays() {
|
|
6100
|
+
if (getMode() === Modes.Edit) {
|
|
6101
|
+
return;
|
|
6102
|
+
}
|
|
6103
|
+
const multiOverlay = this._map.getOverlayById(this._multiOverlayId);
|
|
6104
|
+
if (multiOverlay) {
|
|
6105
|
+
this._map.removeOverlay(multiOverlay);
|
|
6106
|
+
}
|
|
6107
|
+
const items = this._collectionModify.getArray();
|
|
6108
|
+
if (items.length > 1) {
|
|
6109
|
+
items.forEach((feature) => {
|
|
6110
|
+
this._removeOverlayHelper(feature);
|
|
6111
|
+
});
|
|
6112
|
+
const extent = extent_js.createEmpty();
|
|
6113
|
+
items.forEach((feature) => {
|
|
6114
|
+
extent_js.extend(extent, feature.getGeometry().getExtent());
|
|
6115
|
+
});
|
|
6116
|
+
this._createEditOverlay(null, extent_js.getCenter(extent), this._multiOverlayId);
|
|
6117
|
+
}
|
|
6118
|
+
else if (items.length === 1) {
|
|
6119
|
+
const feature = items[0];
|
|
6120
|
+
const featureId = feature.getId();
|
|
6121
|
+
if (featureId && !this._map.getOverlayById(featureId)) {
|
|
6122
|
+
this._createEditOverlay(feature);
|
|
4716
6123
|
}
|
|
4717
6124
|
}
|
|
4718
6125
|
}
|
|
@@ -4759,15 +6166,20 @@
|
|
|
4759
6166
|
if (!layer.getVisible()) {
|
|
4760
6167
|
return;
|
|
4761
6168
|
}
|
|
6169
|
+
// Clear hover style while drawing
|
|
6170
|
+
this._clearHoverState();
|
|
4762
6171
|
activateDrawButton();
|
|
6172
|
+
deactivateSelectModeButton();
|
|
4763
6173
|
this._viewport.classList.add('draw-mode');
|
|
4764
6174
|
addDrawInteraction(layer);
|
|
4765
6175
|
}
|
|
4766
6176
|
else {
|
|
4767
6177
|
this._map.removeInteraction(this._interactionDraw);
|
|
4768
6178
|
this._viewport.classList.remove('draw-mode');
|
|
6179
|
+
activateSelectModeButton(getSelectionMode());
|
|
4769
6180
|
}
|
|
4770
6181
|
activateMode(layer ? Modes.Draw : null);
|
|
6182
|
+
this._refreshSelectionInteractions();
|
|
4771
6183
|
}
|
|
4772
6184
|
/**
|
|
4773
6185
|
* Activate/desactivate the edit mode
|
|
@@ -4783,6 +6195,8 @@
|
|
|
4783
6195
|
else {
|
|
4784
6196
|
// Deselct features
|
|
4785
6197
|
this._collectionModify.clear();
|
|
6198
|
+
// Clear hover state when leaving the edit mode
|
|
6199
|
+
this._clearHoverState();
|
|
4786
6200
|
}
|
|
4787
6201
|
if (this._interactionSelectModify) {
|
|
4788
6202
|
this._interactionSelectModify.setActive(bool);
|
|
@@ -4790,6 +6204,61 @@
|
|
|
4790
6204
|
this._interactionModify.setActive(bool);
|
|
4791
6205
|
if (this._interactionWfsSelect)
|
|
4792
6206
|
this._interactionWfsSelect.setActive(bool);
|
|
6207
|
+
this._refreshSelectionInteractions();
|
|
6208
|
+
}
|
|
6209
|
+
/**
|
|
6210
|
+
* Reset the cursor and remove the hover style of the last hovered feature
|
|
6211
|
+
* @private
|
|
6212
|
+
*/
|
|
6213
|
+
_clearHoverState() {
|
|
6214
|
+
if (this._viewport) {
|
|
6215
|
+
this._viewport.style.cursor = '';
|
|
6216
|
+
}
|
|
6217
|
+
if (this._hoveredFeature) {
|
|
6218
|
+
this._hoveredFeature.setStyle(undefined);
|
|
6219
|
+
this._hoveredFeature = null;
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
/**
|
|
6223
|
+
* Change the map cursor while in edit mode: `move` when the pointer is
|
|
6224
|
+
* over an editable vertex and `copy` when it is over a segment (where a
|
|
6225
|
+
* new vertex can be inserted), mirroring the Modify interaction hit
|
|
6226
|
+
* tolerance.
|
|
6227
|
+
*
|
|
6228
|
+
* @param evt
|
|
6229
|
+
* @private
|
|
6230
|
+
*/
|
|
6231
|
+
_updateEditCursor(evt) {
|
|
6232
|
+
if (!this._collectionModify) {
|
|
6233
|
+
return;
|
|
6234
|
+
}
|
|
6235
|
+
const tolerance = 12;
|
|
6236
|
+
const toleranceSq = tolerance * tolerance;
|
|
6237
|
+
const pixel = evt.pixel;
|
|
6238
|
+
for (const feature of this._collectionModify.getArray()) {
|
|
6239
|
+
const vertices = getFeatureVertices(feature);
|
|
6240
|
+
const coordinates = vertices ? vertices.getCoordinates() : [];
|
|
6241
|
+
if (!coordinates.length) {
|
|
6242
|
+
continue;
|
|
6243
|
+
}
|
|
6244
|
+
// Hovering an existing vertex (draggable handle)
|
|
6245
|
+
for (const coordinate of coordinates) {
|
|
6246
|
+
const vertexPixel = this._map.getPixelFromCoordinate(coordinate);
|
|
6247
|
+
if (coordinate_js.squaredDistance(vertexPixel, pixel) <= toleranceSq) {
|
|
6248
|
+
this._viewport.style.cursor = 'move';
|
|
6249
|
+
return;
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
// Hovering a segment (allows inserting a new vertex)
|
|
6253
|
+
for (let i = 0; i < coordinates.length - 1; i++) {
|
|
6254
|
+
const start = this._map.getPixelFromCoordinate(coordinates[i]);
|
|
6255
|
+
const end = this._map.getPixelFromCoordinate(coordinates[i + 1]);
|
|
6256
|
+
if (coordinate_js.squaredDistanceToSegment(pixel, [start, end]) <= toleranceSq) {
|
|
6257
|
+
this._viewport.style.cursor = 'copy';
|
|
6258
|
+
return;
|
|
6259
|
+
}
|
|
6260
|
+
}
|
|
6261
|
+
}
|
|
4793
6262
|
}
|
|
4794
6263
|
/**
|
|
4795
6264
|
* Remove the overlay helper atttached to a specify feature
|