ol-wfst 4.4.0 → 5.1.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 +191 -175
- package/dist/@types.d.ts +15 -1
- 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 +8 -2
- package/dist/WfsLayer.d.ts.map +1 -1
- package/dist/WmsLayer.d.ts +32 -2
- 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 +563 -149
- 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 +23 -4
- 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 +8 -1
- 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 +251 -5
- package/dist/ol-wfst.d.ts.map +1 -1
- package/dist/ol-wfst.js +2008 -391
- 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 +15 -1
- 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 +8 -2
- package/lib/WfsLayer.d.ts.map +1 -1
- package/lib/WmsLayer.d.ts +32 -2
- 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 +23 -4
- 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 +8 -1
- 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 +251 -5
- package/lib/ol-wfst.d.ts.map +1 -1
- package/lib/ol-wfst.js +2013 -392
- package/lib/ol-wfst.js.map +1 -1
- package/lib/style/css/ol-gisify.css +563 -149
- 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 +728 -213
- 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 -
|
|
2
|
+
* ol-wfst - v5.0.1
|
|
3
3
|
* https://github.com/GastonZalba/ol-wfst#readme
|
|
4
|
-
* Built:
|
|
4
|
+
* Built: Thu Oct 08 2026 20:27:23 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,18 +1140,23 @@
|
|
|
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',
|
|
1146
1150
|
delete: 'Eliminar',
|
|
1147
1151
|
cancel: 'Cancelar',
|
|
1148
|
-
apply: 'Aplicar cambios',
|
|
1149
1152
|
upload: 'Subir',
|
|
1150
1153
|
editMode: 'Modo Edición',
|
|
1151
1154
|
confirmDelete: '¿Estás seguro de borrar el elemento?',
|
|
1155
|
+
confirmDeleteElements: '¿Borrar los {} elementos?',
|
|
1152
1156
|
geomTypeNotSupported: 'Geometría no compatible con la capa',
|
|
1153
1157
|
editFields: 'Editar campos',
|
|
1154
1158
|
editGeom: 'Editar geometría',
|
|
1159
|
+
continueLine: 'Continuar dibujando desde aquí',
|
|
1155
1160
|
selectDrawType: 'Tipo de geometría para dibujar',
|
|
1156
1161
|
uploadToLayer: 'Subir archivo a la capa seleccionada',
|
|
1157
1162
|
uploadFeatures: 'Subida de elementos a la capa',
|
|
@@ -1159,7 +1164,16 @@
|
|
|
1159
1164
|
invalidFeatures: 'Invalidas',
|
|
1160
1165
|
loading: 'Cargando...',
|
|
1161
1166
|
toggleVisibility: 'Cambiar visibilidad de la capa',
|
|
1162
|
-
|
|
1167
|
+
fullscreen: 'Pantalla completa',
|
|
1168
|
+
close: 'Cerrar',
|
|
1169
|
+
multipleValues: 'Valores múltiples',
|
|
1170
|
+
editElements: 'Editar elementos ({})',
|
|
1171
|
+
multipleEditNotice: 'Edición múltiple activada. Los cambios se aplicarán a los {} elementos seleccionados.',
|
|
1172
|
+
tableMode: 'Modo tabla',
|
|
1173
|
+
formMode: 'Modo formulario',
|
|
1174
|
+
feature: 'Elemento',
|
|
1175
|
+
geometry: 'Geometría',
|
|
1176
|
+
geoserverLayers: 'Capas de GeoServer'
|
|
1163
1177
|
},
|
|
1164
1178
|
errors: {
|
|
1165
1179
|
capabilities: 'No se pudieron obtener las Capabilidades del GeoServer',
|
|
@@ -1180,18 +1194,23 @@
|
|
|
1180
1194
|
const en = {
|
|
1181
1195
|
labels: {
|
|
1182
1196
|
select: 'Select',
|
|
1197
|
+
selectBox: 'Select features by drawing a box',
|
|
1198
|
+
selectFreehand: 'Select features by freehand drawing',
|
|
1199
|
+
query: 'Query feature info',
|
|
1200
|
+
featureInfo: 'Feature info',
|
|
1183
1201
|
addElement: 'Toggle Draw mode',
|
|
1184
1202
|
editElement: 'Edit feature',
|
|
1185
1203
|
save: 'Save',
|
|
1186
1204
|
delete: 'Delete',
|
|
1187
1205
|
cancel: 'Cancel',
|
|
1188
|
-
apply: 'Apply changes',
|
|
1189
1206
|
upload: 'Upload',
|
|
1190
1207
|
editMode: 'Edit Mode',
|
|
1191
1208
|
confirmDelete: 'Are you sure to delete the feature?',
|
|
1209
|
+
confirmDeleteElements: 'Delete the {} selected features?',
|
|
1192
1210
|
geomTypeNotSupported: 'Geometry not supported by layer',
|
|
1193
1211
|
editFields: 'Edit fields',
|
|
1194
1212
|
editGeom: 'Edit geometry',
|
|
1213
|
+
continueLine: 'Continue drawing from here',
|
|
1195
1214
|
selectDrawType: 'Geometry type to draw',
|
|
1196
1215
|
uploadToLayer: 'Upload file to selected layer',
|
|
1197
1216
|
uploadFeatures: 'Uploaded features to layer',
|
|
@@ -1199,7 +1218,16 @@
|
|
|
1199
1218
|
invalidFeatures: 'Invalid',
|
|
1200
1219
|
loading: 'Loading...',
|
|
1201
1220
|
toggleVisibility: 'Toggle layer visibility',
|
|
1202
|
-
|
|
1221
|
+
fullscreen: 'Fullscreen',
|
|
1222
|
+
close: 'Close',
|
|
1223
|
+
multipleValues: 'Multiple values',
|
|
1224
|
+
editElements: 'Edit features ({})',
|
|
1225
|
+
multipleEditNotice: 'Multi-edit active. Changes will be applied to the {} selected features.',
|
|
1226
|
+
tableMode: 'Table mode',
|
|
1227
|
+
formMode: 'Form mode',
|
|
1228
|
+
feature: 'Feature',
|
|
1229
|
+
geometry: 'Geometry',
|
|
1230
|
+
geoserverLayers: 'Geoserver Layers'
|
|
1203
1231
|
},
|
|
1204
1232
|
errors: {
|
|
1205
1233
|
capabilities: 'GeoServer Capabilities could not be downloaded.',
|
|
@@ -1220,18 +1248,23 @@
|
|
|
1220
1248
|
const zh = {
|
|
1221
1249
|
labels: {
|
|
1222
1250
|
select: '选择',
|
|
1251
|
+
selectBox: '框选元素',
|
|
1252
|
+
selectFreehand: '手绘选择元素',
|
|
1253
|
+
query: '查询元素信息',
|
|
1254
|
+
featureInfo: '元素信息',
|
|
1223
1255
|
addElement: '切换绘图类型',
|
|
1224
1256
|
editElement: '编辑元素',
|
|
1225
1257
|
save: '保存',
|
|
1226
1258
|
delete: '删除',
|
|
1227
1259
|
cancel: '取消',
|
|
1228
|
-
apply: '确认并应用改变',
|
|
1229
1260
|
upload: '上传',
|
|
1230
1261
|
editMode: '编辑模式',
|
|
1231
1262
|
confirmDelete: '确认删除元素?',
|
|
1263
|
+
confirmDeleteElements: '确认删除选中的 {} 个元素?',
|
|
1232
1264
|
geomTypeNotSupported: '图层不支持该几何',
|
|
1233
1265
|
editFields: '编辑区域',
|
|
1234
1266
|
editGeom: '编辑几何',
|
|
1267
|
+
continueLine: '从此处继续绘制',
|
|
1235
1268
|
selectDrawType: '几何类型',
|
|
1236
1269
|
uploadToLayer: '通过文件上传图层',
|
|
1237
1270
|
uploadFeatures: '上传元素到图层',
|
|
@@ -1239,7 +1272,16 @@
|
|
|
1239
1272
|
invalidFeatures: '不合法',
|
|
1240
1273
|
loading: '加载中...',
|
|
1241
1274
|
toggleVisibility: '切换图层透明度',
|
|
1242
|
-
|
|
1275
|
+
fullscreen: '全屏',
|
|
1276
|
+
close: '关闭',
|
|
1277
|
+
multipleValues: '多个值',
|
|
1278
|
+
editElements: '编辑元素({})',
|
|
1279
|
+
multipleEditNotice: '多选编辑已激活。更改将应用于所有 {} 个选定元素。',
|
|
1280
|
+
tableMode: '表格模式',
|
|
1281
|
+
formMode: '表单模式',
|
|
1282
|
+
feature: '要素',
|
|
1283
|
+
geometry: '几何',
|
|
1284
|
+
geoserverLayers: 'GeoServer 图层'
|
|
1243
1285
|
},
|
|
1244
1286
|
errors: {
|
|
1245
1287
|
capabilities: '无法加载GeoServer服务所支持的能力.',
|
|
@@ -1274,12 +1316,37 @@
|
|
|
1274
1316
|
I18N = Object.assign(Object.assign({}, I18N), customI18n);
|
|
1275
1317
|
}
|
|
1276
1318
|
};
|
|
1319
|
+
/**
|
|
1320
|
+
* /**
|
|
1321
|
+
* For translations thas has a variable "{}"" to be replaced inside
|
|
1322
|
+
* @param string
|
|
1323
|
+
* @param args
|
|
1324
|
+
* @returns
|
|
1325
|
+
*/
|
|
1326
|
+
const I18N_ = (string, ...args) => {
|
|
1327
|
+
var _a;
|
|
1328
|
+
let text = (_a = I18N.labels) === null || _a === void 0 ? void 0 : _a[string];
|
|
1329
|
+
if (!text) {
|
|
1330
|
+
text = I18N[string];
|
|
1331
|
+
}
|
|
1332
|
+
if (!text) {
|
|
1333
|
+
console.error('Translation not found', string);
|
|
1334
|
+
text = string;
|
|
1335
|
+
}
|
|
1336
|
+
if (args.length) {
|
|
1337
|
+
args.forEach((arg) => {
|
|
1338
|
+
text = text.replace(/{}/, arg);
|
|
1339
|
+
});
|
|
1340
|
+
}
|
|
1341
|
+
return text;
|
|
1342
|
+
};
|
|
1277
1343
|
|
|
1278
1344
|
let loadingDiv;
|
|
1279
1345
|
const initLoading = () => {
|
|
1280
1346
|
loadingDiv = document.createElement('div');
|
|
1281
1347
|
loadingDiv.className = 'ol-wfst--tools-control--loading';
|
|
1282
|
-
loadingDiv.
|
|
1348
|
+
loadingDiv.setAttribute('role', 'progressbar');
|
|
1349
|
+
loadingDiv.setAttribute('aria-label', I18N.labels.loading);
|
|
1283
1350
|
return loadingDiv;
|
|
1284
1351
|
};
|
|
1285
1352
|
const showLoading = (bool = true) => {
|
|
@@ -1364,7 +1431,7 @@
|
|
|
1364
1431
|
const unique = (arr) => arr.filter((e, i) => arr.indexOf(e) == i);
|
|
1365
1432
|
|
|
1366
1433
|
// Keeps track of constituent classes for every mixin class created by ts-mixer.
|
|
1367
|
-
const mixins = new
|
|
1434
|
+
const mixins = new WeakMap();
|
|
1368
1435
|
const getMixinsForClass = (clazz) => mixins.get(clazz);
|
|
1369
1436
|
const registerMixins = (mixedClass, constituents) => mixins.set(mixedClass, constituents);
|
|
1370
1437
|
|
|
@@ -1488,13 +1555,27 @@
|
|
|
1488
1555
|
(function (Modes) {
|
|
1489
1556
|
Modes["Edit"] = "EDIT";
|
|
1490
1557
|
Modes["Draw"] = "DRAW";
|
|
1558
|
+
Modes["Query"] = "QUERY";
|
|
1491
1559
|
})(Modes || (Modes = {}));
|
|
1560
|
+
var SelectionMode;
|
|
1561
|
+
(function (SelectionMode) {
|
|
1562
|
+
SelectionMode["Single"] = "SINGLE";
|
|
1563
|
+
SelectionMode["Box"] = "BOX";
|
|
1564
|
+
SelectionMode["Freehand"] = "FREEHAND";
|
|
1565
|
+
})(SelectionMode || (SelectionMode = {}));
|
|
1566
|
+
let selectionMode = SelectionMode.Single;
|
|
1492
1567
|
function activateMode(m = null) {
|
|
1493
1568
|
mode = m;
|
|
1494
1569
|
}
|
|
1495
1570
|
function getMode() {
|
|
1496
1571
|
return mode;
|
|
1497
1572
|
}
|
|
1573
|
+
function setSelectionMode(m) {
|
|
1574
|
+
selectionMode = m;
|
|
1575
|
+
}
|
|
1576
|
+
function getSelectionMode() {
|
|
1577
|
+
return selectionMode;
|
|
1578
|
+
}
|
|
1498
1579
|
const editedFeatures = new Set();
|
|
1499
1580
|
const mapLayers = {};
|
|
1500
1581
|
function setMap(m) {
|
|
@@ -1533,16 +1614,23 @@
|
|
|
1533
1614
|
*/
|
|
1534
1615
|
class BaseLayer extends Layer {
|
|
1535
1616
|
/**
|
|
1617
|
+
* Initialize the layer: request DescribeFeatureType and resolve when it
|
|
1618
|
+
* finishes (whether successful or not).
|
|
1619
|
+
*
|
|
1536
1620
|
* @private
|
|
1621
|
+
* @returns Promise that resolves when the layer is initialized
|
|
1537
1622
|
*/
|
|
1538
|
-
_init() {
|
|
1623
|
+
async _init() {
|
|
1539
1624
|
const geoserver = this.getGeoserver();
|
|
1540
1625
|
if (geoserver.isLoaded()) {
|
|
1541
|
-
this.getAndUpdateDescribeFeatureType();
|
|
1626
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
1542
1627
|
}
|
|
1543
1628
|
else {
|
|
1544
|
-
|
|
1545
|
-
|
|
1629
|
+
await new Promise((resolve) => {
|
|
1630
|
+
geoserver.once('change:capabilities', async () => {
|
|
1631
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
1632
|
+
resolve();
|
|
1633
|
+
});
|
|
1546
1634
|
});
|
|
1547
1635
|
}
|
|
1548
1636
|
}
|
|
@@ -1618,13 +1706,13 @@
|
|
|
1618
1706
|
}
|
|
1619
1707
|
/**
|
|
1620
1708
|
* @public
|
|
1621
|
-
* @param
|
|
1709
|
+
* @param featureIds
|
|
1622
1710
|
* @returns
|
|
1623
1711
|
*/
|
|
1624
|
-
async maybeLockFeature(
|
|
1712
|
+
async maybeLockFeature(featureIds) {
|
|
1625
1713
|
const geoserver = this.getGeoserver();
|
|
1626
1714
|
if (geoserver.getUseLockFeature() && geoserver.hasLockFeature()) {
|
|
1627
|
-
return await geoserver.lockFeature(
|
|
1715
|
+
return await geoserver.lockFeature(featureIds, this.get(BaseLayerProperty.NAME));
|
|
1628
1716
|
}
|
|
1629
1717
|
return null;
|
|
1630
1718
|
}
|
|
@@ -1663,44 +1751,48 @@
|
|
|
1663
1751
|
*/
|
|
1664
1752
|
class WfsSource extends VectorSource {
|
|
1665
1753
|
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
|
-
|
|
1754
|
+
super(Object.assign(Object.assign({}, options), { format: new GeoJSON(), loader: (extent, resolution, projection, success, failure) => {
|
|
1755
|
+
void (async () => {
|
|
1756
|
+
try {
|
|
1757
|
+
// If bbox, add extent to the request
|
|
1758
|
+
if (options.strategy == loadingstrategy_js.bbox) {
|
|
1759
|
+
const extentGeoServer = proj_js.transformExtent(extent, projection.getCode(), options.geoServerAdvanced.projection);
|
|
1760
|
+
// https://docs.geoserver.org/stable/en/user/services/wfs/reference.html
|
|
1761
|
+
// request features using a bounding box with CRS maybe different from featureTypes native CRS
|
|
1762
|
+
this.urlParams.set('bbox', extentGeoServer.toString() +
|
|
1763
|
+
`,${options.geoServerAdvanced.projection}`);
|
|
1764
|
+
}
|
|
1765
|
+
const url_fetch = options.geoserverUrl +
|
|
1766
|
+
'?' +
|
|
1767
|
+
this.urlParams.toString();
|
|
1768
|
+
const response = await fetch(url_fetch, {
|
|
1769
|
+
headers: options.headers,
|
|
1770
|
+
credentials: options.credentials
|
|
1771
|
+
});
|
|
1772
|
+
if (!response.ok) {
|
|
1773
|
+
throw new Error('');
|
|
1774
|
+
}
|
|
1775
|
+
const data = await response.json();
|
|
1776
|
+
if (data.exceptions) {
|
|
1777
|
+
throw new Error(parseError(data));
|
|
1778
|
+
}
|
|
1779
|
+
const features = this.getFormat().readFeatures(data, {
|
|
1780
|
+
featureProjection: projection.getCode(),
|
|
1781
|
+
dataProjection: options.geoServerAdvanced.projection
|
|
1782
|
+
});
|
|
1783
|
+
features.forEach((feature) => {
|
|
1784
|
+
feature.set('_layerName_', options.name,
|
|
1785
|
+
/* silent = */ true);
|
|
1786
|
+
});
|
|
1787
|
+
this.addFeatures(features);
|
|
1788
|
+
success(features);
|
|
1683
1789
|
}
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1790
|
+
catch (err) {
|
|
1791
|
+
this.removeLoadedExtent(extent);
|
|
1792
|
+
showError(I18N.errors.geoserver, err, options.name);
|
|
1793
|
+
failure();
|
|
1687
1794
|
}
|
|
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
|
-
}
|
|
1795
|
+
})();
|
|
1704
1796
|
} }));
|
|
1705
1797
|
this.urlParams = new URLSearchParams({
|
|
1706
1798
|
SERVICE: 'wfs',
|
|
@@ -1867,7 +1959,7 @@
|
|
|
1867
1959
|
this._loadingCount = 0;
|
|
1868
1960
|
this._loadedCount = 0;
|
|
1869
1961
|
/**
|
|
1870
|
-
* Return the full accuracy
|
|
1962
|
+
* Return the full accuracy geometries to replace the features from GetFeatureInfo
|
|
1871
1963
|
* @param featuresId
|
|
1872
1964
|
* @returns
|
|
1873
1965
|
*/
|
|
@@ -1878,10 +1970,10 @@
|
|
|
1878
1970
|
INFO_FORMAT: 'application/json',
|
|
1879
1971
|
REQUEST: 'GetFeature',
|
|
1880
1972
|
TYPENAME: this.get('name'),
|
|
1881
|
-
MAXFEATURES:
|
|
1973
|
+
MAXFEATURES: String(featuresId.length),
|
|
1882
1974
|
OUTPUTFORMAT: 'application/json',
|
|
1883
1975
|
SRSNAME: getMap().getView().getProjection().getCode(),
|
|
1884
|
-
FEATUREID:
|
|
1976
|
+
FEATUREID: featuresId.join(',')
|
|
1885
1977
|
});
|
|
1886
1978
|
const url = this.getSource().getUrls()[0] + '?' + queryParams.toString();
|
|
1887
1979
|
try {
|
|
@@ -1894,7 +1986,11 @@
|
|
|
1894
1986
|
throw new Error(`${I18N.errors.getFeatures} ${response.status}`);
|
|
1895
1987
|
}
|
|
1896
1988
|
const data = await response.json();
|
|
1897
|
-
|
|
1989
|
+
const fullById = {};
|
|
1990
|
+
this._parseFeaturesFromResponse(data).forEach((feature) => {
|
|
1991
|
+
fullById[String(feature.getId())] = feature;
|
|
1992
|
+
});
|
|
1993
|
+
return featuresId.map((id) => fullById[String(id)]).filter(Boolean);
|
|
1898
1994
|
}
|
|
1899
1995
|
catch (err) {
|
|
1900
1996
|
console.error(err);
|
|
@@ -1962,6 +2058,112 @@
|
|
|
1962
2058
|
FEATURE_COUNT: 1,
|
|
1963
2059
|
EXCEPTIONS: 'application/json'
|
|
1964
2060
|
});
|
|
2061
|
+
return this._requestFeatures(url);
|
|
2062
|
+
}
|
|
2063
|
+
/**
|
|
2064
|
+
* Request the WFS features that intersect the given geometry, selecting
|
|
2065
|
+
* them with a WFS GetFeature + CQL INTERSECTS filter. Used to select
|
|
2066
|
+
* features from a WMS layer with a box or a freehand lasso.
|
|
2067
|
+
*
|
|
2068
|
+
* @param geometry Geometry in the map view projection
|
|
2069
|
+
* @returns
|
|
2070
|
+
* @private
|
|
2071
|
+
*/
|
|
2072
|
+
async _getFeaturesInGeometry(geometry) {
|
|
2073
|
+
var _a, _b, _c;
|
|
2074
|
+
// Make sure the DescribeFeatureType is loaded to get the geometry
|
|
2075
|
+
// field, waiting for it if the layer is still initializing
|
|
2076
|
+
if (!this.getDescribeFeatureType()) {
|
|
2077
|
+
await this.getAndUpdateDescribeFeatureType();
|
|
2078
|
+
}
|
|
2079
|
+
const geomField = (_b = (_a = this.getDescribeFeatureType()) === null || _a === void 0 ? void 0 : _a._parsed) === null || _b === void 0 ? void 0 : _b.geomField;
|
|
2080
|
+
if (!geomField) {
|
|
2081
|
+
showError(`${I18N.errors.layer} "${this.get('name')}"`);
|
|
2082
|
+
return [];
|
|
2083
|
+
}
|
|
2084
|
+
const viewProj = getMap().getView().getProjection().getCode();
|
|
2085
|
+
const nativeSrs = this._getNativeSrs();
|
|
2086
|
+
// Work on a copy transformed to the layer native projection so the
|
|
2087
|
+
// server evaluates the INTERSECTS filter against the proper SRID
|
|
2088
|
+
const polygon = geometry.clone();
|
|
2089
|
+
if (nativeSrs !== viewProj) {
|
|
2090
|
+
try {
|
|
2091
|
+
polygon.transform(viewProj, nativeSrs);
|
|
2092
|
+
}
|
|
2093
|
+
catch (err) {
|
|
2094
|
+
console.error(err);
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
const ring = polygon.getCoordinates()[0] || [];
|
|
2098
|
+
const ringWkt = ring.map(([x, y]) => `${x} ${y}`).join(', ');
|
|
2099
|
+
// Force the filter geometry CRS with the EWKT SRID prefix so the
|
|
2100
|
+
// server evaluates the INTERSECTS against the layer projection
|
|
2101
|
+
// instead of its native storage SRS
|
|
2102
|
+
const sridMatch = (_c = nativeSrs.match(/(?:EPSG\s*::?\s*|EPSG\/0\/)(\d+)/i)) !== null && _c !== void 0 ? _c : nativeSrs.match(/(\d+)$/);
|
|
2103
|
+
const cql = sridMatch
|
|
2104
|
+
? `INTERSECTS(${geomField}, SRID=${sridMatch[1]};POLYGON((${ringWkt})))`
|
|
2105
|
+
: `INTERSECTS(${geomField}, POLYGON((${ringWkt})))`;
|
|
2106
|
+
const queryParams = new URLSearchParams({
|
|
2107
|
+
SERVICE: 'wfs',
|
|
2108
|
+
VERSION: '2.0.0',
|
|
2109
|
+
REQUEST: 'GetFeature',
|
|
2110
|
+
TYPENAME: this.get('name'),
|
|
2111
|
+
OUTPUTFORMAT: 'application/json',
|
|
2112
|
+
SRSNAME: viewProj,
|
|
2113
|
+
CQL_FILTER: cql
|
|
2114
|
+
});
|
|
2115
|
+
const url = this.getSource().getUrls()[0] + '?' + queryParams.toString();
|
|
2116
|
+
// The server returns the features in the map view projection (same as
|
|
2117
|
+
// the full resolution request), so no further transformation is needed
|
|
2118
|
+
return this._requestFeatures(url);
|
|
2119
|
+
}
|
|
2120
|
+
/**
|
|
2121
|
+
* Resolve the layer native SRS so spatial filters are evaluated in the
|
|
2122
|
+
* correct projection. It reads the DefaultCRS from the WFS capabilities,
|
|
2123
|
+
* falling back to the geoserver advanced projection option and finally to
|
|
2124
|
+
* the map view projection.
|
|
2125
|
+
*
|
|
2126
|
+
* @returns
|
|
2127
|
+
* @private
|
|
2128
|
+
*/
|
|
2129
|
+
_getNativeSrs() {
|
|
2130
|
+
var _a;
|
|
2131
|
+
const geoserver = this.getGeoserver();
|
|
2132
|
+
const viewProj = getMap().getView().getProjection().getCode();
|
|
2133
|
+
const layerName = String(this.get('name') || '');
|
|
2134
|
+
const layerLocalName = layerName.split(':').pop();
|
|
2135
|
+
const featureTypeList = (_a = geoserver.getParsedCapabilities()) === null || _a === void 0 ? void 0 : _a.FeatureTypeList;
|
|
2136
|
+
const featureType = Array.isArray(featureTypeList)
|
|
2137
|
+
? featureTypeList.find((ft) => {
|
|
2138
|
+
const featureTypeName = String((ft === null || ft === void 0 ? void 0 : ft.Name) || '');
|
|
2139
|
+
return (featureTypeName === layerName ||
|
|
2140
|
+
featureTypeName === layerLocalName ||
|
|
2141
|
+
featureTypeName.split(':').pop() === layerLocalName);
|
|
2142
|
+
})
|
|
2143
|
+
: undefined;
|
|
2144
|
+
const defaultCrs = (featureType === null || featureType === void 0 ? void 0 : featureType.DefaultCRS)
|
|
2145
|
+
? String(featureType.DefaultCRS)
|
|
2146
|
+
: '';
|
|
2147
|
+
const epsgMatch = defaultCrs.match(/(?:EPSG\s*::?\s*|EPSG\/0\/)(\d+)/i);
|
|
2148
|
+
if (epsgMatch) {
|
|
2149
|
+
return `EPSG:${epsgMatch[1]}`;
|
|
2150
|
+
}
|
|
2151
|
+
const advanced = geoserver.getAdvanced().projection;
|
|
2152
|
+
if (advanced) {
|
|
2153
|
+
return String(advanced);
|
|
2154
|
+
}
|
|
2155
|
+
return viewProj;
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Fetch and parse the features from a request url, replacing the
|
|
2159
|
+
* low resolution geometries (GetFeatureInfo) with the full resolution
|
|
2160
|
+
* ones requested by FEATUREID when possible.
|
|
2161
|
+
*
|
|
2162
|
+
* @param url
|
|
2163
|
+
* @returns
|
|
2164
|
+
* @private
|
|
2165
|
+
*/
|
|
2166
|
+
async _requestFeatures(url) {
|
|
1965
2167
|
const geoserver = this.getGeoserver();
|
|
1966
2168
|
try {
|
|
1967
2169
|
const response = await fetch(url, {
|
|
@@ -1975,7 +2177,7 @@
|
|
|
1975
2177
|
let features = this._parseFeaturesFromResponse(data);
|
|
1976
2178
|
const featuresId = features.map((f) => f.getId());
|
|
1977
2179
|
if (!featuresId.length) {
|
|
1978
|
-
return;
|
|
2180
|
+
return [];
|
|
1979
2181
|
}
|
|
1980
2182
|
const fullResList = await this._getFullResGeometryById(featuresId);
|
|
1981
2183
|
if (fullResList) {
|
|
@@ -2026,50 +2228,95 @@
|
|
|
2026
2228
|
}
|
|
2027
2229
|
}
|
|
2028
2230
|
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
return tagName(attrs, children);
|
|
2040
|
-
const elem = tagName === null
|
|
2041
|
-
? new DocumentFragment()
|
|
2231
|
+
function createElement(tagName, props) {
|
|
2232
|
+
props = props || {};
|
|
2233
|
+
let children = props.children || [];
|
|
2234
|
+
children = Array.isArray(children) ? children : [children];
|
|
2235
|
+
delete props.children;
|
|
2236
|
+
if (typeof tagName === 'function') {
|
|
2237
|
+
return tagName(props, children);
|
|
2238
|
+
}
|
|
2239
|
+
const elem = tagName === 'fragment'
|
|
2240
|
+
? document.createDocumentFragment()
|
|
2042
2241
|
: document.createElement(tagName);
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
value
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2242
|
+
if (elem instanceof HTMLElement) {
|
|
2243
|
+
Object.entries(props).forEach(([name, value]) => {
|
|
2244
|
+
if (value != null) {
|
|
2245
|
+
// listener
|
|
2246
|
+
if (name.startsWith('on') && typeof value === 'function') {
|
|
2247
|
+
elem.addEventListener(name
|
|
2248
|
+
.slice(2)
|
|
2249
|
+
.toLowerCase(), value);
|
|
2250
|
+
}
|
|
2251
|
+
else if (name === 'className') {
|
|
2051
2252
|
elem.setAttribute('class', value.toString());
|
|
2052
|
-
|
|
2253
|
+
}
|
|
2254
|
+
else if (name === 'htmlFor') {
|
|
2053
2255
|
elem.setAttribute('for', value.toString());
|
|
2054
|
-
|
|
2256
|
+
}
|
|
2257
|
+
else {
|
|
2055
2258
|
elem.setAttribute(name, value.toString());
|
|
2259
|
+
}
|
|
2056
2260
|
}
|
|
2057
|
-
}
|
|
2058
|
-
}
|
|
2261
|
+
});
|
|
2262
|
+
}
|
|
2059
2263
|
for (const child of children) {
|
|
2060
|
-
if (!child)
|
|
2264
|
+
if (!child) {
|
|
2061
2265
|
continue;
|
|
2062
|
-
|
|
2266
|
+
}
|
|
2267
|
+
if (Array.isArray(child)) {
|
|
2063
2268
|
elem.append(...child);
|
|
2269
|
+
}
|
|
2270
|
+
else if (child instanceof Node) {
|
|
2271
|
+
elem.appendChild(child);
|
|
2272
|
+
}
|
|
2064
2273
|
else {
|
|
2065
|
-
|
|
2066
|
-
elem.innerHTML += child;
|
|
2067
|
-
else
|
|
2068
|
-
elem.appendChild(child);
|
|
2274
|
+
elem.innerHTML += child;
|
|
2069
2275
|
}
|
|
2070
2276
|
}
|
|
2071
2277
|
return elem;
|
|
2072
2278
|
}
|
|
2279
|
+
function jsx(type, props) {
|
|
2280
|
+
return createElement(type, props);
|
|
2281
|
+
}
|
|
2282
|
+
function jsxs(type, props) {
|
|
2283
|
+
return createElement(type, props);
|
|
2284
|
+
}
|
|
2285
|
+
function Fragment(type, children) {
|
|
2286
|
+
return createElement('fragment', { children });
|
|
2287
|
+
}
|
|
2288
|
+
|
|
2289
|
+
function uploadSvg() {
|
|
2290
|
+
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;
|
|
2291
|
+
}
|
|
2292
|
+
|
|
2293
|
+
function drawSvg() {
|
|
2294
|
+
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;
|
|
2295
|
+
}
|
|
2296
|
+
|
|
2297
|
+
function selectSvg() {
|
|
2298
|
+
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;
|
|
2299
|
+
}
|
|
2300
|
+
|
|
2301
|
+
function selectBoxSvg() {
|
|
2302
|
+
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;
|
|
2303
|
+
}
|
|
2304
|
+
|
|
2305
|
+
function selectFreehandSvg() {
|
|
2306
|
+
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;
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
function infoSvg() {
|
|
2310
|
+
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;
|
|
2311
|
+
}
|
|
2312
|
+
|
|
2313
|
+
function visibilityOnSvg() {
|
|
2314
|
+
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;
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
function visibilityOffSvg() {
|
|
2318
|
+
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;
|
|
2319
|
+
}
|
|
2073
2320
|
|
|
2074
2321
|
/**
|
|
2075
2322
|
* Removes in the DOM the class of the tools
|
|
@@ -2086,6 +2333,19 @@
|
|
|
2086
2333
|
if (btn) {
|
|
2087
2334
|
btn.classList.add('wfst--active');
|
|
2088
2335
|
}
|
|
2336
|
+
deactivateQueryButton();
|
|
2337
|
+
};
|
|
2338
|
+
const activateQueryButton = () => {
|
|
2339
|
+
const btn = document.querySelector('.ol-wfst--tools-control-btn-query');
|
|
2340
|
+
if (btn) {
|
|
2341
|
+
btn.classList.add('wfst--active');
|
|
2342
|
+
}
|
|
2343
|
+
};
|
|
2344
|
+
const deactivateQueryButton = () => {
|
|
2345
|
+
const btn = document.querySelector('.ol-wfst--tools-control-btn-query');
|
|
2346
|
+
if (btn) {
|
|
2347
|
+
btn.classList.remove('wfst--active');
|
|
2348
|
+
}
|
|
2089
2349
|
};
|
|
2090
2350
|
const activateDrawButton = () => {
|
|
2091
2351
|
const btn = document.querySelector('.ol-wfst--tools-control-btn-draw');
|
|
@@ -2093,6 +2353,29 @@
|
|
|
2093
2353
|
btn.classList.add('wfst--active');
|
|
2094
2354
|
}
|
|
2095
2355
|
};
|
|
2356
|
+
const activateSelectModeButton = (mode) => {
|
|
2357
|
+
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');
|
|
2358
|
+
if (activeBtn) {
|
|
2359
|
+
activeBtn.classList.remove('wfst--active');
|
|
2360
|
+
}
|
|
2361
|
+
deactivateQueryButton();
|
|
2362
|
+
const btnMap = {
|
|
2363
|
+
[SelectionMode.Single]: '.ol-wfst--tools-control-btn-select',
|
|
2364
|
+
[SelectionMode.Box]: '.ol-wfst--tools-control-btn-select-box',
|
|
2365
|
+
[SelectionMode.Freehand]: '.ol-wfst--tools-control-btn-select-freehand'
|
|
2366
|
+
};
|
|
2367
|
+
const btn = document.querySelector(btnMap[mode]);
|
|
2368
|
+
if (btn) {
|
|
2369
|
+
btn.classList.add('wfst--active');
|
|
2370
|
+
}
|
|
2371
|
+
};
|
|
2372
|
+
const deactivateSelectModeButton = () => {
|
|
2373
|
+
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');
|
|
2374
|
+
if (activeBtn) {
|
|
2375
|
+
activeBtn.classList.remove('wfst--active');
|
|
2376
|
+
}
|
|
2377
|
+
deactivateQueryButton();
|
|
2378
|
+
};
|
|
2096
2379
|
class LayersControl extends Observable {
|
|
2097
2380
|
constructor(uploads, uploadFormats) {
|
|
2098
2381
|
super();
|
|
@@ -2105,23 +2388,15 @@
|
|
|
2105
2388
|
* @public
|
|
2106
2389
|
*/
|
|
2107
2390
|
addLayerEl(layer) {
|
|
2108
|
-
const container = document.querySelector('.wfst--tools-control--select-layers');
|
|
2391
|
+
const container = document.querySelector('.wfst--tools-control--select-layers-list');
|
|
2109
2392
|
const layerName = layer.get(BaseLayerProperty.NAME);
|
|
2110
2393
|
const checked = layer === getActiveLayerToInsertEls() ? { checked: true } : {};
|
|
2111
|
-
const input = (
|
|
2112
|
-
const layerDom = (
|
|
2394
|
+
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) })));
|
|
2395
|
+
const layerDom = (jsx("div", { className: `wfst--layer-control
|
|
2113
2396
|
${layer.getVisible() ? 'ol-wfst--visible-on' : ''}
|
|
2114
2397
|
${layer === getActiveLayerToInsertEls()
|
|
2115
2398
|
? 'ol-wfst--selected-on'
|
|
2116
|
-
: ''}`, "data-layer": layerName },
|
|
2117
|
-
createElement("div", { className: "ol-wfst--tools-control-visible" },
|
|
2118
|
-
createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-on", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt) },
|
|
2119
|
-
createElement("img", { src: img$3 })),
|
|
2120
|
-
createElement("span", { className: "ol-wfst--tools-control-visible-btn ol-wfst--visible-btn-off", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt) },
|
|
2121
|
-
createElement("img", { src: img$2 }))),
|
|
2122
|
-
createElement("label", { htmlFor: `wfst--${layerName}` },
|
|
2123
|
-
input,
|
|
2124
|
-
createElement("span", { title: layer.getDescribeFeatureType()._parsed.geomType }, layer.get(BaseLayerProperty.LABEL)))));
|
|
2399
|
+
: ''}`, "data-layer": layerName, children: jsxs("label", { htmlFor: `wfst--${layerName}`, children: [jsxs("div", { className: "ol-wfst--layer-control-visible ", children: [jsx("span", { className: "ol-wfst--layer-control-visible -btn ol-wfst--visible-btn-on", title: I18N.labels.toggleVisibility, onClick: (evt) => this._visibilityClickHandler(evt), children: visibilityOnSvg() }), jsx("span", { className: "ol-wfst--layer-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) })] }) }));
|
|
2125
2400
|
container.appendChild(layerDom);
|
|
2126
2401
|
if (layer === getActiveLayerToInsertEls()) {
|
|
2127
2402
|
input.dispatchEvent(new Event('change'));
|
|
@@ -2217,34 +2492,32 @@
|
|
|
2217
2492
|
this.dispatchEvent('changeLayer');
|
|
2218
2493
|
}
|
|
2219
2494
|
render() {
|
|
2220
|
-
return (
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
})))),
|
|
2247
|
-
createElement("div", { className: "wfst--tools-control--select-layers" })));
|
|
2495
|
+
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: () => {
|
|
2496
|
+
this.dispatchEvent('selectModeSingle');
|
|
2497
|
+
}, children: selectSvg() }), jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-select-box", type: "button", title: I18N.labels.selectBox, onClick: () => {
|
|
2498
|
+
this.dispatchEvent('selectModeBox');
|
|
2499
|
+
}, children: selectBoxSvg() }), jsx("button", { className: "ol-wfst--tools-control-btn ol-wfst--tools-control-btn-select-freehand", type: "button", title: I18N.labels.selectFreehand, onClick: () => {
|
|
2500
|
+
this.dispatchEvent('selectModeFreehand');
|
|
2501
|
+
}, 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: () => {
|
|
2502
|
+
this.dispatchEvent('queryMode');
|
|
2503
|
+
}, 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: () => {
|
|
2504
|
+
this.dispatchEvent('drawMode');
|
|
2505
|
+
}, children: drawSvg() }), jsx("select", { title: I18N.labels.selectDrawType, className: "wfst--tools-control--select-draw", onChange: (evt) => {
|
|
2506
|
+
const selectedValue = evt.target.value;
|
|
2507
|
+
this._changeStateSelect(getActiveLayerToInsertEls(), selectedValue);
|
|
2508
|
+
this.dispatchEvent('changeGeom');
|
|
2509
|
+
}, children: [
|
|
2510
|
+
GeometryType.Point,
|
|
2511
|
+
GeometryType.MultiPoint,
|
|
2512
|
+
GeometryType.LineString,
|
|
2513
|
+
GeometryType.MultiLineString,
|
|
2514
|
+
GeometryType.Polygon,
|
|
2515
|
+
GeometryType.MultiPolygon,
|
|
2516
|
+
GeometryType.Circle
|
|
2517
|
+
].map((type) => {
|
|
2518
|
+
// Show all options, but enable only the accepted ones
|
|
2519
|
+
return jsx("option", { value: type, children: type });
|
|
2520
|
+
}) })] }), 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" })] })] }));
|
|
2248
2521
|
}
|
|
2249
2522
|
}
|
|
2250
2523
|
|
|
@@ -2483,6 +2756,7 @@
|
|
|
2483
2756
|
evtType: 'singleclick',
|
|
2484
2757
|
active: true,
|
|
2485
2758
|
showControl: true,
|
|
2759
|
+
fullscreen: true,
|
|
2486
2760
|
language: DEFAULT_LANGUAGE,
|
|
2487
2761
|
uploadFormats: '.geojson,.json,.kml',
|
|
2488
2762
|
processUpload: null,
|
|
@@ -2500,91 +2774,145 @@
|
|
|
2500
2774
|
};
|
|
2501
2775
|
|
|
2502
2776
|
class EditControlChangesEl extends Control {
|
|
2503
|
-
constructor(
|
|
2777
|
+
constructor(features) {
|
|
2504
2778
|
super({
|
|
2505
|
-
element: (
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
} }, I18N.labels.cancel),
|
|
2515
|
-
createElement("button", { type: "button", className: "btn btn-sm btn-primary", onClick: () => {
|
|
2516
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('apply', feature));
|
|
2517
|
-
} }, I18N.labels.apply),
|
|
2518
|
-
createElement("button", { type: "button", className: "btn btn-sm btn-danger-outline", onClick: () => {
|
|
2519
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('delete', feature));
|
|
2520
|
-
} }, I18N.labels.delete))))
|
|
2779
|
+
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
|
|
2780
|
+
? I18N_('editElements', features.length)
|
|
2781
|
+
: String(features[0].getId()) })] }), jsx("button", { type: "button", className: "btn btn-sm btn-danger", onClick: () => {
|
|
2782
|
+
this._dispatch('delete', features);
|
|
2783
|
+
}, children: I18N.labels.delete }), jsx("button", { type: "button", className: "btn btn-sm btn-secondary", onClick: () => {
|
|
2784
|
+
this._dispatch('cancel', features);
|
|
2785
|
+
}, children: I18N.labels.cancel }), jsx("button", { type: "button", className: "btn btn-sm btn-primary", onClick: () => {
|
|
2786
|
+
this._dispatch('apply', features);
|
|
2787
|
+
}, children: I18N.labels.save })] }) }))
|
|
2521
2788
|
});
|
|
2522
2789
|
}
|
|
2790
|
+
_dispatch(type, features) {
|
|
2791
|
+
const evt = new VectorSource.VectorSourceEvent(type, features[0]);
|
|
2792
|
+
evt.features = features;
|
|
2793
|
+
this.dispatchEvent(evt);
|
|
2794
|
+
}
|
|
2523
2795
|
}
|
|
2524
2796
|
|
|
2525
2797
|
// Ol
|
|
2798
|
+
/**
|
|
2799
|
+
* Modern vertex handle mimicking the round "extend line" buttons shown at
|
|
2800
|
+
* line endpoints: a soft halo for depth and a white disc with a subtle
|
|
2801
|
+
* border. When `highlighted` is `true` (e.g. hovering a vertex with the
|
|
2802
|
+
* Modify interaction) the handle gets bigger with a stronger halo and a
|
|
2803
|
+
* darker border.
|
|
2804
|
+
*
|
|
2805
|
+
* @param highlighted Whether to render the bigger/highlighted state
|
|
2806
|
+
* @param geometry Style geometry function to position the handles
|
|
2807
|
+
* @private
|
|
2808
|
+
*/
|
|
2809
|
+
const editVertexStyles = (highlighted = false, geometry) => {
|
|
2810
|
+
const halo = highlighted ? 10 : 8;
|
|
2811
|
+
const haloColor = highlighted
|
|
2812
|
+
? 'rgba(0, 0, 0, 0.40)'
|
|
2813
|
+
: 'rgba(0, 0, 0, 0.28)';
|
|
2814
|
+
const radius = highlighted ? 7 : 6;
|
|
2815
|
+
const border = highlighted ? '#4b5563' : '#b3b3b3';
|
|
2816
|
+
const borderWidth = highlighted ? 2 : 1.5;
|
|
2817
|
+
return [
|
|
2818
|
+
new style_js.Style(Object.assign({ image: new style_js.Circle({
|
|
2819
|
+
radius: halo,
|
|
2820
|
+
fill: new style_js.Fill({
|
|
2821
|
+
color: haloColor
|
|
2822
|
+
})
|
|
2823
|
+
}) }, (geometry ? { geometry } : {}))),
|
|
2824
|
+
new style_js.Style(Object.assign({ image: new style_js.Circle({
|
|
2825
|
+
radius: radius,
|
|
2826
|
+
fill: new style_js.Fill({
|
|
2827
|
+
color: '#ffffff'
|
|
2828
|
+
}),
|
|
2829
|
+
stroke: new style_js.Stroke({
|
|
2830
|
+
width: borderWidth,
|
|
2831
|
+
color: border
|
|
2832
|
+
})
|
|
2833
|
+
}) }, (geometry ? { geometry } : {})))
|
|
2834
|
+
];
|
|
2835
|
+
};
|
|
2836
|
+
/**
|
|
2837
|
+
* Extract a `MultiPoint` with every vertex of the given feature geometry
|
|
2838
|
+
* (polygons and multi-line strings are flattened). Returns `undefined` when
|
|
2839
|
+
* the geometry has no coordinates.
|
|
2840
|
+
*
|
|
2841
|
+
* @param feature
|
|
2842
|
+
* @private
|
|
2843
|
+
*/
|
|
2844
|
+
const getFeatureVertices = (feature) => {
|
|
2845
|
+
let geometry = feature.getGeometry();
|
|
2846
|
+
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2847
|
+
geometry = geometry.getGeometries()[0];
|
|
2848
|
+
}
|
|
2849
|
+
const coordinates = geometry.getCoordinates();
|
|
2850
|
+
let flatCoordinates = null;
|
|
2851
|
+
if (geometry instanceof geom_js.Polygon || geometry instanceof geom_js.MultiLineString) {
|
|
2852
|
+
flatCoordinates = coordinates.flat(1);
|
|
2853
|
+
}
|
|
2854
|
+
else if (geometry instanceof geom_js.MultiPolygon) {
|
|
2855
|
+
flatCoordinates = coordinates.flat(2);
|
|
2856
|
+
}
|
|
2857
|
+
else {
|
|
2858
|
+
flatCoordinates = coordinates;
|
|
2859
|
+
}
|
|
2860
|
+
if (!flatCoordinates || !flatCoordinates.length) {
|
|
2861
|
+
return;
|
|
2862
|
+
}
|
|
2863
|
+
return new geom_js.MultiPoint(flatCoordinates);
|
|
2864
|
+
};
|
|
2526
2865
|
/**
|
|
2527
2866
|
* Master style that handles two modes on the Edit Layer:
|
|
2528
2867
|
* - one is the basic, showing only the vertices
|
|
2529
2868
|
* - and the other when modify is active, showing bigger vertices
|
|
2530
2869
|
*
|
|
2870
|
+
* When `hovered` is `true`, a white/red halo is prepended to highlight the
|
|
2871
|
+
* feature, indicating it can be selected on click.
|
|
2872
|
+
*
|
|
2531
2873
|
* @param feature
|
|
2874
|
+
* @param hovered
|
|
2532
2875
|
* @private
|
|
2533
2876
|
*/
|
|
2534
|
-
function styleFunction(feature) {
|
|
2535
|
-
const getVertexs = (feature) => {
|
|
2536
|
-
let geometry = feature.getGeometry();
|
|
2537
|
-
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2538
|
-
geometry = geometry.getGeometries()[0];
|
|
2539
|
-
}
|
|
2540
|
-
const coordinates = geometry.getCoordinates();
|
|
2541
|
-
let flatCoordinates = null;
|
|
2542
|
-
if (geometry instanceof geom_js.Polygon ||
|
|
2543
|
-
geometry instanceof geom_js.MultiLineString) {
|
|
2544
|
-
flatCoordinates = coordinates.flat(1);
|
|
2545
|
-
}
|
|
2546
|
-
else if (geometry instanceof geom_js.MultiPolygon) {
|
|
2547
|
-
flatCoordinates = coordinates.flat(2);
|
|
2548
|
-
}
|
|
2549
|
-
else {
|
|
2550
|
-
flatCoordinates = coordinates;
|
|
2551
|
-
}
|
|
2552
|
-
if (!flatCoordinates || !flatCoordinates.length) {
|
|
2553
|
-
return;
|
|
2554
|
-
}
|
|
2555
|
-
return new geom_js.MultiPoint(flatCoordinates);
|
|
2556
|
-
};
|
|
2877
|
+
function styleFunction(feature, hovered = false) {
|
|
2557
2878
|
let geometry = feature.getGeometry();
|
|
2558
2879
|
let type = geometry.getType();
|
|
2559
2880
|
if (geometry instanceof geom_js.GeometryCollection) {
|
|
2560
2881
|
geometry = geometry.getGeometries()[0];
|
|
2561
2882
|
type = geometry.getType();
|
|
2562
2883
|
}
|
|
2884
|
+
const haloStyle = () => {
|
|
2885
|
+
return [
|
|
2886
|
+
new style_js.Style({
|
|
2887
|
+
image: new style_js.Circle({
|
|
2888
|
+
radius: 9,
|
|
2889
|
+
fill: new style_js.Fill({
|
|
2890
|
+
color: 'rgba(255, 255, 255, 0.9)'
|
|
2891
|
+
}),
|
|
2892
|
+
stroke: new style_js.Stroke({
|
|
2893
|
+
color: '#ff0000',
|
|
2894
|
+
width: 3
|
|
2895
|
+
})
|
|
2896
|
+
}),
|
|
2897
|
+
stroke: new style_js.Stroke({
|
|
2898
|
+
color: 'rgba(255, 255, 255, 0.9)',
|
|
2899
|
+
width: 8
|
|
2900
|
+
}),
|
|
2901
|
+
fill: new style_js.Fill({
|
|
2902
|
+
color: 'rgba(255, 255, 255, 0.9)'
|
|
2903
|
+
})
|
|
2904
|
+
})
|
|
2905
|
+
];
|
|
2906
|
+
};
|
|
2563
2907
|
switch (type) {
|
|
2564
2908
|
case GeometryType.Point:
|
|
2565
2909
|
case GeometryType.MultiPoint:
|
|
2566
2910
|
if (getMode() === Modes.Edit) {
|
|
2567
|
-
return
|
|
2568
|
-
new style_js.Style({
|
|
2569
|
-
image: new style_js.Circle({
|
|
2570
|
-
radius: 6,
|
|
2571
|
-
fill: new style_js.Fill({
|
|
2572
|
-
color: '#000000'
|
|
2573
|
-
})
|
|
2574
|
-
})
|
|
2575
|
-
}),
|
|
2576
|
-
new style_js.Style({
|
|
2577
|
-
image: new style_js.Circle({
|
|
2578
|
-
radius: 4,
|
|
2579
|
-
fill: new style_js.Fill({
|
|
2580
|
-
color: '#ff0000'
|
|
2581
|
-
})
|
|
2582
|
-
})
|
|
2583
|
-
})
|
|
2584
|
-
];
|
|
2911
|
+
return editVertexStyles();
|
|
2585
2912
|
}
|
|
2586
2913
|
else {
|
|
2587
2914
|
return [
|
|
2915
|
+
...(hovered ? haloStyle() : []),
|
|
2588
2916
|
new style_js.Style({
|
|
2589
2917
|
image: new style_js.Circle({
|
|
2590
2918
|
radius: 5,
|
|
@@ -2616,19 +2944,7 @@
|
|
|
2616
2944
|
color: 'rgba(255, 0, 0, 0.7)'
|
|
2617
2945
|
})
|
|
2618
2946
|
}),
|
|
2619
|
-
|
|
2620
|
-
image: new style_js.Circle({
|
|
2621
|
-
radius: 4,
|
|
2622
|
-
fill: new style_js.Fill({
|
|
2623
|
-
color: '#ff0000'
|
|
2624
|
-
}),
|
|
2625
|
-
stroke: new style_js.Stroke({
|
|
2626
|
-
width: 2,
|
|
2627
|
-
color: 'rgba(5, 5, 5, 0.9)'
|
|
2628
|
-
})
|
|
2629
|
-
}),
|
|
2630
|
-
geometry: (feature) => getVertexs(feature)
|
|
2631
|
-
}),
|
|
2947
|
+
...editVertexStyles(false, getFeatureVertices),
|
|
2632
2948
|
new style_js.Style({
|
|
2633
2949
|
stroke: new style_js.Stroke({
|
|
2634
2950
|
color: 'rgba(255, 255, 255, 0.7)',
|
|
@@ -2639,6 +2955,7 @@
|
|
|
2639
2955
|
}
|
|
2640
2956
|
else {
|
|
2641
2957
|
return [
|
|
2958
|
+
...(hovered ? haloStyle() : []),
|
|
2642
2959
|
new style_js.Style({
|
|
2643
2960
|
image: new style_js.Circle({
|
|
2644
2961
|
radius: 2,
|
|
@@ -2646,7 +2963,7 @@
|
|
|
2646
2963
|
color: '#000000'
|
|
2647
2964
|
})
|
|
2648
2965
|
}),
|
|
2649
|
-
geometry: (feature) =>
|
|
2966
|
+
geometry: (feature) => getFeatureVertices(feature)
|
|
2650
2967
|
}),
|
|
2651
2968
|
new style_js.Style({
|
|
2652
2969
|
stroke: new style_js.Stroke({
|
|
@@ -2661,22 +2978,67 @@
|
|
|
2661
2978
|
}
|
|
2662
2979
|
}
|
|
2663
2980
|
}
|
|
2664
|
-
|
|
2665
|
-
// Ol
|
|
2666
2981
|
/**
|
|
2667
|
-
*
|
|
2982
|
+
* Style used to highlight a feature after a query, drawn on a dedicated
|
|
2983
|
+
* highlight layer so the source layers keep their own rendering.
|
|
2668
2984
|
*
|
|
2669
2985
|
* @param feature
|
|
2670
2986
|
* @private
|
|
2671
2987
|
*/
|
|
2988
|
+
function queryStyleFunction(_feature) {
|
|
2989
|
+
return [
|
|
2990
|
+
new style_js.Style({
|
|
2991
|
+
image: new style_js.Circle({
|
|
2992
|
+
radius: 6,
|
|
2993
|
+
fill: new style_js.Fill({
|
|
2994
|
+
color: 'rgba(0, 170, 255, 0.4)'
|
|
2995
|
+
}),
|
|
2996
|
+
stroke: new style_js.Stroke({
|
|
2997
|
+
color: '#0066cc',
|
|
2998
|
+
width: 2
|
|
2999
|
+
})
|
|
3000
|
+
}),
|
|
3001
|
+
stroke: new style_js.Stroke({
|
|
3002
|
+
color: '#00aaff',
|
|
3003
|
+
width: 4
|
|
3004
|
+
}),
|
|
3005
|
+
fill: new style_js.Fill({
|
|
3006
|
+
color: 'rgba(0, 170, 255, 0.3)'
|
|
3007
|
+
})
|
|
3008
|
+
})
|
|
3009
|
+
];
|
|
3010
|
+
}
|
|
3011
|
+
|
|
3012
|
+
function tableSvg() {
|
|
3013
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z\"></path>\n</svg>", 'image/svg+xml')).firstChild;
|
|
3014
|
+
}
|
|
3015
|
+
|
|
3016
|
+
function formSvg() {
|
|
3017
|
+
return (new DOMParser().parseFromString("<svg version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\">\n <path d=\"M14 17H4v2h10v-2zm6-8H4v2h16V9zM4 15h16v-2H4v2zM4 5v2h16V5H4z\"></path>\n</svg>", 'image/svg+xml')).firstChild;
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
/**
|
|
3021
|
+
* Shows a fields form in a modal window to allow changes in the properties of
|
|
3022
|
+
* one or several features. When editing multiple features, a "table mode"
|
|
3023
|
+
* button renders one feature per row, spreadsheet-style, so each feature can be
|
|
3024
|
+
* edited separately (like an Excel sheet). Otherwise a single form is shown
|
|
3025
|
+
* where fields whose value differs across the selection show the "multiple
|
|
3026
|
+
* values" placeholder and only the fields actually changed are applied to the
|
|
3027
|
+
* whole selection.
|
|
3028
|
+
*
|
|
3029
|
+
* @param features
|
|
3030
|
+
* @private
|
|
3031
|
+
*/
|
|
2672
3032
|
class EditFieldsModal extends Observable {
|
|
2673
3033
|
constructor(options) {
|
|
2674
3034
|
super();
|
|
2675
3035
|
this._options = options;
|
|
2676
|
-
this.
|
|
3036
|
+
this._tableMode = false;
|
|
3037
|
+
this._fieldList = [];
|
|
3038
|
+
this._modal = new Modal(Object.assign(Object.assign({}, this._options.modal), { header: true, headerClose: true, title: '', content: '<div></div>', footer: `
|
|
2677
3039
|
<button
|
|
2678
3040
|
type="button"
|
|
2679
|
-
class="btn btn-sm btn-
|
|
3041
|
+
class="btn btn-sm btn-danger"
|
|
2680
3042
|
data-action="delete"
|
|
2681
3043
|
data-dismiss="modal"
|
|
2682
3044
|
>
|
|
@@ -2701,75 +3063,208 @@
|
|
|
2701
3063
|
this._modal.on('dismiss', (modal, event) => {
|
|
2702
3064
|
// On saving changes
|
|
2703
3065
|
if (event.target.dataset.action === 'save') {
|
|
2704
|
-
|
|
2705
|
-
.
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
this._feature.changed();
|
|
2712
|
-
addFeatureToEditedList(this._feature);
|
|
2713
|
-
this.dispatchEvent(new VectorSource.VectorSourceEvent('save', this._feature));
|
|
3066
|
+
if (this._tableMode) {
|
|
3067
|
+
this._saveTable(modal);
|
|
3068
|
+
}
|
|
3069
|
+
else {
|
|
3070
|
+
this._saveForm(modal);
|
|
3071
|
+
}
|
|
3072
|
+
this._dispatch('save', this._features);
|
|
2714
3073
|
}
|
|
2715
3074
|
else if (event.target.dataset.action === 'delete') {
|
|
2716
|
-
this.
|
|
3075
|
+
this._dispatch('delete', this._features);
|
|
2717
3076
|
}
|
|
2718
3077
|
});
|
|
2719
3078
|
}
|
|
2720
|
-
show(
|
|
2721
|
-
this.
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
const
|
|
3079
|
+
show(features) {
|
|
3080
|
+
this._features = Array.isArray(features) ? features : [features];
|
|
3081
|
+
// Keep the form (current behavior) as default entry view
|
|
3082
|
+
this._tableMode = false;
|
|
3083
|
+
const multiple = this._features.length > 1;
|
|
3084
|
+
const modalTitle = multiple
|
|
3085
|
+
? I18N_('editElements', this._features.length)
|
|
3086
|
+
: `${I18N.labels.editElement} ${this._features[0].getId()} `;
|
|
3087
|
+
const layerName = this._features[0].get('_layerName_');
|
|
2725
3088
|
// Data schema from the geoserver
|
|
2726
3089
|
const layer = getStoredLayer(layerName);
|
|
2727
|
-
const
|
|
3090
|
+
const describeFeatureType = layer.getDescribeFeatureType()._parsed;
|
|
3091
|
+
this._fieldList = describeFeatureType.properties.filter((field) => field.name !== describeFeatureType.geomField);
|
|
3092
|
+
this._layer = layer;
|
|
3093
|
+
this._modal._html.dialog.classList.remove('ol-wfst--edit-table-modal');
|
|
3094
|
+
this._renderBody();
|
|
3095
|
+
this._modal._html.header.innerHTML = modalTitle;
|
|
3096
|
+
this._modal.show();
|
|
3097
|
+
}
|
|
3098
|
+
_renderBody() {
|
|
3099
|
+
const multiple = this._features.length > 1;
|
|
2728
3100
|
this._modal._html.body.innerHTML = '';
|
|
2729
|
-
this._modal._html.body.append(
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
3101
|
+
this._modal._html.body.append(jsxs("div", { children: [multiple && (jsxs("div", { className: "ol-wfst--edit-modal-notice", children: [jsx("span", { className: "ol-wfst--edit-modal-notice-txt", children: I18N_('multipleEditNotice', this._features.length) }), jsx("button", { type: "button", className: "ol-wfst--edit-modal-toolbar-btn", title: this._tableMode
|
|
3102
|
+
? I18N.labels.formMode
|
|
3103
|
+
: I18N.labels.tableMode, onClick: () => this._toggleTable(), children: this._tableMode ? formSvg() : tableSvg() })] })), this._tableMode ? this._renderTable() : this._renderForm()] }));
|
|
3104
|
+
}
|
|
3105
|
+
_toggleTable() {
|
|
3106
|
+
this._tableMode = !this._tableMode;
|
|
3107
|
+
this._modal._html.dialog.classList.toggle('ol-wfst--edit-table-modal', this._tableMode);
|
|
3108
|
+
this._renderBody();
|
|
3109
|
+
}
|
|
3110
|
+
_renderForm() {
|
|
3111
|
+
const multiple = this._features.length > 1;
|
|
3112
|
+
return (jsx("form", { autocomplete: "false", children: this._fieldList.flatMap((field) => {
|
|
3113
|
+
const key = field.name;
|
|
3114
|
+
const values = this._features.map((feature) => feature.get(key));
|
|
3115
|
+
const uniqueValues = Array.from(new Set(values.map((value) => String(value !== null && value !== void 0 ? value : ''))));
|
|
3116
|
+
const hasMultipleValues = multiple && uniqueValues.length > 1;
|
|
3117
|
+
const value = hasMultipleValues
|
|
3118
|
+
? null
|
|
3119
|
+
: uniqueValues[0] || null;
|
|
3120
|
+
const type = this._inputType(field.type);
|
|
3121
|
+
let input = (jsx("input", { placeholder: hasMultipleValues
|
|
3122
|
+
? I18N.labels.multipleValues
|
|
3123
|
+
: 'NULL', className: 'ol-wfst--input-field-input' +
|
|
3124
|
+
(hasMultipleValues
|
|
3125
|
+
? ' ol-wfst--input-multiple'
|
|
3126
|
+
: ''), type: type, name: key, value: value, "data-multiple": hasMultipleValues ? 'true' : null }));
|
|
3127
|
+
if (this._layer.beforeShowFieldsModal) {
|
|
3128
|
+
const hookInput = this._layer.beforeShowFieldsModal(field, value, input);
|
|
3129
|
+
if (!hookInput) {
|
|
3130
|
+
return [];
|
|
3131
|
+
}
|
|
3132
|
+
if (typeof hookInput === 'string') {
|
|
3133
|
+
input = new DOMParser().parseFromString(hookInput, 'text/html').body.childNodes[0];
|
|
3134
|
+
}
|
|
3135
|
+
else {
|
|
3136
|
+
input = hookInput;
|
|
3137
|
+
}
|
|
2758
3138
|
}
|
|
2759
|
-
if (
|
|
2760
|
-
input =
|
|
3139
|
+
if (hasMultipleValues) {
|
|
3140
|
+
input.dataset.multiple = 'true';
|
|
3141
|
+
input.classList.add('ol-wfst--input-multiple');
|
|
2761
3142
|
}
|
|
2762
|
-
|
|
2763
|
-
|
|
3143
|
+
return (jsxs("div", { className: "ol-wfst--input-field-container", children: [jsx("label", { className: "ol-wfst--input-field-label", htmlFor: key, children: key }), input] }));
|
|
3144
|
+
}) }));
|
|
3145
|
+
}
|
|
3146
|
+
_renderTable() {
|
|
3147
|
+
return (jsx("form", { autocomplete: "false", children: jsx("div", { className: "ol-wfst--edit-table-wrap", children: jsxs("table", { className: "ol-wfst--edit-table", children: [jsx("thead", { children: jsxs("tr", { children: [jsx("th", { children: I18N.labels.feature }), jsx("th", { children: I18N.labels.geometry }), this._fieldList.map((field) => (jsx("th", { children: field.name })))] }) }), jsx("tbody", { children: this._features.map((feature, idx) => {
|
|
3148
|
+
var _a;
|
|
3149
|
+
return (jsxs("tr", { children: [jsx("td", { className: "ol-wfst--edit-table-fid", children: String((_a = feature.getId()) !== null && _a !== void 0 ? _a : idx + 1) }), jsx("td", { className: "ol-wfst--edit-table-geom", children: geometryTypeLabel(feature.getGeometry()) }), this._fieldList.map((field) => {
|
|
3150
|
+
var _a;
|
|
3151
|
+
const key = field.name;
|
|
3152
|
+
const type = this._inputType(field.type);
|
|
3153
|
+
const value = (_a = feature.get(key)) !== null && _a !== void 0 ? _a : '';
|
|
3154
|
+
let input = (jsx("input", { type: type, name: key, className: "ol-wfst--input-field-input", "data-feat-idx": String(idx), value: String(value) }));
|
|
3155
|
+
if (this._layer.beforeShowFieldsModal) {
|
|
3156
|
+
const hookInput = this._layer.beforeShowFieldsModal(field, value, input);
|
|
3157
|
+
if (!hookInput) {
|
|
3158
|
+
return jsx("td", {});
|
|
3159
|
+
}
|
|
3160
|
+
if (typeof hookInput === 'string') {
|
|
3161
|
+
input =
|
|
3162
|
+
new DOMParser().parseFromString(hookInput, 'text/html').body
|
|
3163
|
+
.childNodes[0];
|
|
3164
|
+
}
|
|
3165
|
+
else {
|
|
3166
|
+
input = hookInput;
|
|
3167
|
+
}
|
|
3168
|
+
input.setAttribute('name', key);
|
|
3169
|
+
input.setAttribute('data-feat-idx', String(idx));
|
|
3170
|
+
input.classList.add('ol-wfst--input-field-input');
|
|
3171
|
+
}
|
|
3172
|
+
return jsx("td", { children: input });
|
|
3173
|
+
})] }));
|
|
3174
|
+
}) })] }) }) }));
|
|
3175
|
+
}
|
|
3176
|
+
_inputType(typeXsd) {
|
|
3177
|
+
switch (typeXsd) {
|
|
3178
|
+
case 'xsd:double':
|
|
3179
|
+
case 'xsd:number':
|
|
3180
|
+
case 'xsd:int':
|
|
3181
|
+
return 'number';
|
|
3182
|
+
case 'xsd:date':
|
|
3183
|
+
return 'date';
|
|
3184
|
+
case 'xsd:date-time':
|
|
3185
|
+
return 'datetime';
|
|
3186
|
+
case 'xsd:string':
|
|
3187
|
+
default:
|
|
3188
|
+
return 'text';
|
|
3189
|
+
}
|
|
3190
|
+
}
|
|
3191
|
+
_saveForm(modal) {
|
|
3192
|
+
const formElements = modal.el.querySelector('form')
|
|
3193
|
+
.elements;
|
|
3194
|
+
const multipleFields = new Set(Array.from(formElements)
|
|
3195
|
+
.filter((el) => el.dataset.multiple === 'true')
|
|
3196
|
+
.map((el) => el.name));
|
|
3197
|
+
Array.from(formElements).forEach((el) => {
|
|
3198
|
+
const value = el.value;
|
|
3199
|
+
const field = el.name;
|
|
3200
|
+
// Do not overwrite untouched fields that had
|
|
3201
|
+
// multiple values in the selection
|
|
3202
|
+
if (multipleFields.has(field) && value === '') {
|
|
3203
|
+
return;
|
|
3204
|
+
}
|
|
3205
|
+
this._features.forEach((feature) => {
|
|
3206
|
+
if (feature.get(field) !== value) {
|
|
3207
|
+
feature.set(field, value, /* isSilent = */ true);
|
|
2764
3208
|
}
|
|
3209
|
+
});
|
|
3210
|
+
});
|
|
3211
|
+
this._features.forEach((feature) => {
|
|
3212
|
+
feature.changed();
|
|
3213
|
+
addFeatureToEditedList(feature);
|
|
3214
|
+
});
|
|
3215
|
+
}
|
|
3216
|
+
_saveTable(modal) {
|
|
3217
|
+
const inputList = Array.from(modal.el.querySelectorAll('form input[data-feat-idx]'));
|
|
3218
|
+
const grouped = new Map();
|
|
3219
|
+
inputList.forEach((input) => {
|
|
3220
|
+
const idx = Number(input.dataset.featIdx);
|
|
3221
|
+
if (!grouped.has(idx)) {
|
|
3222
|
+
grouped.set(idx, []);
|
|
2765
3223
|
}
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
3224
|
+
grouped.get(idx).push(input);
|
|
3225
|
+
});
|
|
3226
|
+
grouped.forEach((inputs, idx) => {
|
|
3227
|
+
const feature = this._features[idx];
|
|
3228
|
+
if (!feature) {
|
|
3229
|
+
return;
|
|
3230
|
+
}
|
|
3231
|
+
inputs.forEach((input) => {
|
|
3232
|
+
const field = input.name;
|
|
3233
|
+
const value = input.value;
|
|
3234
|
+
if (feature.get(field) !== value) {
|
|
3235
|
+
feature.set(field, value, /* isSilent = */ true);
|
|
3236
|
+
}
|
|
3237
|
+
});
|
|
3238
|
+
});
|
|
3239
|
+
this._features.forEach((feature) => {
|
|
3240
|
+
feature.changed();
|
|
3241
|
+
addFeatureToEditedList(feature);
|
|
3242
|
+
});
|
|
3243
|
+
}
|
|
3244
|
+
_dispatch(type, features) {
|
|
3245
|
+
const evt = new VectorSource.VectorSourceEvent(type, features[0]);
|
|
3246
|
+
evt.features = features;
|
|
3247
|
+
this.dispatchEvent(evt);
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
function geometryTypeLabel(geometry) {
|
|
3251
|
+
if (!geometry) {
|
|
3252
|
+
return '-';
|
|
3253
|
+
}
|
|
3254
|
+
if (geometry instanceof GeometryCollection) {
|
|
3255
|
+
const types = new Set();
|
|
3256
|
+
const collect = (geom) => {
|
|
3257
|
+
if (geom instanceof GeometryCollection) {
|
|
3258
|
+
geom.getGeometriesArray().forEach(collect);
|
|
3259
|
+
}
|
|
3260
|
+
else {
|
|
3261
|
+
types.add(geom.getType());
|
|
3262
|
+
}
|
|
3263
|
+
};
|
|
3264
|
+
collect(geometry);
|
|
3265
|
+
return types.size ? Array.from(types).join(', ') : '-';
|
|
2772
3266
|
}
|
|
3267
|
+
return geometry.getType();
|
|
2773
3268
|
}
|
|
2774
3269
|
|
|
2775
3270
|
/**
|
|
@@ -3588,8 +4083,8 @@
|
|
|
3588
4083
|
wfsTransactionVersion: '1.1.0',
|
|
3589
4084
|
projection: DEFAULT_GEOSERVER_SRS,
|
|
3590
4085
|
lockFeatureParams: {
|
|
3591
|
-
expiry: 5,
|
|
3592
|
-
lockId: '
|
|
4086
|
+
expiry: 5, // minutes
|
|
4087
|
+
lockId: 'WFST-editor',
|
|
3593
4088
|
releaseAction: 'SOME'
|
|
3594
4089
|
}
|
|
3595
4090
|
},
|
|
@@ -3607,6 +4102,7 @@
|
|
|
3607
4102
|
this._insertFeatures = [];
|
|
3608
4103
|
this._updateFeatures = [];
|
|
3609
4104
|
this._deleteFeatures = [];
|
|
4105
|
+
this._lockedId = this._options.advanced.lockFeatureParams.lockId;
|
|
3610
4106
|
// Formats
|
|
3611
4107
|
this._formatWFS = new WFS();
|
|
3612
4108
|
this._formatGeoJSON = new GeoJSON();
|
|
@@ -3945,7 +4441,7 @@
|
|
|
3945
4441
|
if (this.hasLockFeature &&
|
|
3946
4442
|
this.getUseLockFeature() &&
|
|
3947
4443
|
transactionType !== TransactionType.Insert) {
|
|
3948
|
-
payload = payload.replace(`</Transaction>`, `<LockId>${this.
|
|
4444
|
+
payload = payload.replace(`</Transaction>`, `<LockId>${this._lockedId}</LockId></Transaction>`);
|
|
3949
4445
|
}
|
|
3950
4446
|
const headers = Object.assign({ 'Content-Type': 'text/xml' }, this.getHeaders());
|
|
3951
4447
|
const response = await fetch(this.getUrl(), {
|
|
@@ -3973,6 +4469,9 @@
|
|
|
3973
4469
|
if (transactionType !== TransactionType.Delete) {
|
|
3974
4470
|
this._removeFeatures(features);
|
|
3975
4471
|
}
|
|
4472
|
+
features.forEach((feature) => {
|
|
4473
|
+
removeFeatureFromEditList(feature);
|
|
4474
|
+
});
|
|
3976
4475
|
wlayer.refresh();
|
|
3977
4476
|
showLoading(false);
|
|
3978
4477
|
this._insertFeatures = [];
|
|
@@ -4032,30 +4531,34 @@
|
|
|
4032
4531
|
const featureProperties = feature.getProperties();
|
|
4033
4532
|
delete featureProperties.boundedBy;
|
|
4034
4533
|
delete featureProperties._layerName_;
|
|
4534
|
+
delete featureProperties._editOverlayCoord_;
|
|
4035
4535
|
const clone = new Feature(featureProperties);
|
|
4036
4536
|
clone.setId(feature.getId());
|
|
4037
4537
|
return clone;
|
|
4038
4538
|
}
|
|
4039
4539
|
/**
|
|
4040
|
-
* Lock
|
|
4041
|
-
* to avoid changes from multiples
|
|
4540
|
+
* Lock one or several features in the geoserver. Useful before editing,
|
|
4541
|
+
* to avoid changes from multiples users. Locking several features at once
|
|
4542
|
+
* with a single request keeps the lock covered by one LockId
|
|
4042
4543
|
*
|
|
4043
|
-
* @param
|
|
4544
|
+
* @param featureIds
|
|
4044
4545
|
* @param layerName
|
|
4045
4546
|
* @param retry
|
|
4046
4547
|
* @public
|
|
4047
4548
|
*/
|
|
4048
|
-
async lockFeature(
|
|
4549
|
+
async lockFeature(featureIds, layerName, retry = 0) {
|
|
4049
4550
|
const params = new URLSearchParams({
|
|
4050
4551
|
service: 'wfs',
|
|
4051
4552
|
version: this.getAdvanced().lockFeatureVersion,
|
|
4052
4553
|
request: 'LockFeature',
|
|
4053
4554
|
typeName: layerName,
|
|
4054
4555
|
expiry: String(this._options.advanced.lockFeatureParams.expiry),
|
|
4055
|
-
|
|
4556
|
+
handle: this._options.advanced.lockFeatureParams.lockId,
|
|
4056
4557
|
releaseAction: this._options.advanced.lockFeatureParams.releaseAction,
|
|
4057
4558
|
exceptions: 'application/json',
|
|
4058
|
-
featureid:
|
|
4559
|
+
featureid: Array.isArray(featureIds)
|
|
4560
|
+
? featureIds.join(',')
|
|
4561
|
+
: String(featureIds)
|
|
4059
4562
|
});
|
|
4060
4563
|
const url_fetch = this.getUrl() + '?' + params.toString();
|
|
4061
4564
|
try {
|
|
@@ -4076,7 +4579,7 @@
|
|
|
4076
4579
|
if (exceptions[0].code === 'CannotLockAllFeatures') {
|
|
4077
4580
|
// Maybe the Feature is already blocked, ant thats trigger error, so, we try one locking more time again
|
|
4078
4581
|
if (!retry) {
|
|
4079
|
-
this.lockFeature(
|
|
4582
|
+
this.lockFeature(featureIds, layerName, 1);
|
|
4080
4583
|
}
|
|
4081
4584
|
else {
|
|
4082
4585
|
throw error;
|
|
@@ -4088,21 +4591,20 @@
|
|
|
4088
4591
|
}
|
|
4089
4592
|
}
|
|
4090
4593
|
catch (err) {
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4594
|
+
// XML response (not JSON): parse the real lock id returned by
|
|
4595
|
+
// the server so the transaction references the lock that was
|
|
4596
|
+
// actually created instead of the configured one
|
|
4597
|
+
try {
|
|
4598
|
+
const dataDoc = new window.DOMParser().parseFromString(data, 'text/xml');
|
|
4599
|
+
const lockIdNode = dataDoc.getElementsByTagName('wfs:LockId')[0] ||
|
|
4600
|
+
dataDoc.getElementsByTagNameNS('http://www.opengis.net/wfs', 'LockId')[0];
|
|
4601
|
+
if (lockIdNode && lockIdNode.textContent) {
|
|
4602
|
+
this._lockedId = lockIdNode.textContent;
|
|
4603
|
+
}
|
|
4604
|
+
}
|
|
4605
|
+
catch (parseErr) {
|
|
4606
|
+
// Keep the configured lockId
|
|
4103
4607
|
}
|
|
4104
|
-
|
|
4105
|
-
*/
|
|
4106
4608
|
}
|
|
4107
4609
|
return data;
|
|
4108
4610
|
}
|
|
@@ -4126,36 +4628,109 @@
|
|
|
4126
4628
|
GeoserverProperty["ISLOADED"] = "isLoaded";
|
|
4127
4629
|
})(GeoserverProperty || (GeoserverProperty = {}));
|
|
4128
4630
|
|
|
4129
|
-
|
|
4631
|
+
function editFieldsSvg() {
|
|
4632
|
+
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;
|
|
4633
|
+
}
|
|
4130
4634
|
|
|
4131
|
-
|
|
4635
|
+
function editGeomSvg() {
|
|
4636
|
+
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;
|
|
4637
|
+
}
|
|
4132
4638
|
|
|
4133
4639
|
class EditOverlay extends Overlay {
|
|
4134
|
-
constructor(feature, coordinate = null) {
|
|
4640
|
+
constructor(feature = null, coordinate = null, id) {
|
|
4135
4641
|
super({
|
|
4136
|
-
id: feature.getId(),
|
|
4137
|
-
position: coordinate ||
|
|
4642
|
+
id: id !== null && id !== void 0 ? id : (feature ? feature.getId() : undefined),
|
|
4643
|
+
position: coordinate ||
|
|
4644
|
+
(feature
|
|
4645
|
+
? extent_js.getCenter(feature.getGeometry().getExtent())
|
|
4646
|
+
: undefined),
|
|
4138
4647
|
positioning: 'center-center',
|
|
4139
4648
|
offset: [0, -40],
|
|
4140
4649
|
stopEvent: true,
|
|
4141
|
-
element: (
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
4146
|
-
createElement("img", { src: img$1, alt: I18N.labels.editFields }))),
|
|
4147
|
-
createElement("div", { className: "ol-wfst--edit-button-cnt", onClick: () => {
|
|
4148
|
-
this.dispatchEvent('editGeom');
|
|
4149
|
-
} },
|
|
4150
|
-
createElement("button", { class: "ol-wfst--edit-button", type: "button", title: I18N.labels.editGeom },
|
|
4151
|
-
createElement("img", { src: img, alt: I18N.labels.editGeom })))))
|
|
4650
|
+
element: (jsxs("div", { children: [jsx("div", { className: "ol-wfst--edit-button-cnt", onClick: () => {
|
|
4651
|
+
this.dispatchEvent('editFields');
|
|
4652
|
+
}, 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: () => {
|
|
4653
|
+
this.dispatchEvent('editGeom');
|
|
4654
|
+
}, children: jsx("button", { class: "ol-wfst--edit-button", type: "button", title: I18N.labels.editGeom, children: editGeomSvg() }) })] }))
|
|
4152
4655
|
});
|
|
4153
4656
|
}
|
|
4154
4657
|
}
|
|
4155
4658
|
|
|
4156
|
-
|
|
4659
|
+
function addLineSvg() {
|
|
4660
|
+
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;
|
|
4661
|
+
}
|
|
4662
|
+
|
|
4663
|
+
class ExtendLineOverlay extends Overlay {
|
|
4664
|
+
constructor(feature, coordinate, side, component = 0, onClick) {
|
|
4665
|
+
super({
|
|
4666
|
+
position: coordinate,
|
|
4667
|
+
positioning: 'center-center',
|
|
4668
|
+
stopEvent: true,
|
|
4669
|
+
element: (jsx("div", { className: "ol-wfst--extend-button-cnt", children: jsx("button", { className: "ol-wfst--extend-button", type: "button", title: I18N.labels.continueLine, onClick: () => {
|
|
4670
|
+
onClick(side);
|
|
4671
|
+
}, children: addLineSvg() }) }))
|
|
4672
|
+
});
|
|
4673
|
+
this.feature = feature;
|
|
4674
|
+
this.side = side;
|
|
4675
|
+
this.component = component;
|
|
4676
|
+
}
|
|
4677
|
+
}
|
|
4678
|
+
|
|
4679
|
+
const QUERY_OVERLAY_ID = 'ol-wfst--query-overlay';
|
|
4157
4680
|
/**
|
|
4158
|
-
*
|
|
4681
|
+
* Popup overlay that shows the attributes of a queried feature and its
|
|
4682
|
+
* layer label. The geometry is highlighted separately on a dedicated layer.
|
|
4683
|
+
*
|
|
4684
|
+
* @extends {ol/Overlay~Overlay}
|
|
4685
|
+
* @param feature
|
|
4686
|
+
* @param coordinate
|
|
4687
|
+
* @param layer
|
|
4688
|
+
* @private
|
|
4689
|
+
*/
|
|
4690
|
+
class QueryOverlay extends Overlay {
|
|
4691
|
+
constructor(feature, coordinate, layer) {
|
|
4692
|
+
const describeFeatureType = layer.getDescribeFeatureType();
|
|
4693
|
+
const parsed = describeFeatureType === null || describeFeatureType === void 0 ? void 0 : describeFeatureType._parsed;
|
|
4694
|
+
const fields = (parsed === null || parsed === void 0 ? void 0 : parsed.properties) || [];
|
|
4695
|
+
const geomField = parsed === null || parsed === void 0 ? void 0 : parsed.geomField;
|
|
4696
|
+
const rows = fields
|
|
4697
|
+
.filter((field) => field.name !== geomField)
|
|
4698
|
+
.map((field) => {
|
|
4699
|
+
let value = feature.get(field.name);
|
|
4700
|
+
if (value === undefined || value === null) {
|
|
4701
|
+
value = '';
|
|
4702
|
+
}
|
|
4703
|
+
else if (typeof value === 'object') {
|
|
4704
|
+
value = JSON.stringify(value);
|
|
4705
|
+
}
|
|
4706
|
+
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) })] }));
|
|
4707
|
+
});
|
|
4708
|
+
super({
|
|
4709
|
+
id: QUERY_OVERLAY_ID,
|
|
4710
|
+
position: coordinate || extent_js.getCenter(feature.getGeometry().getExtent()),
|
|
4711
|
+
positioning: 'bottom-center',
|
|
4712
|
+
offset: [0, -14],
|
|
4713
|
+
stopEvent: true,
|
|
4714
|
+
autoPan: true,
|
|
4715
|
+
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: () => {
|
|
4716
|
+
this.dispatchEvent('close');
|
|
4717
|
+
}, 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 })] }))
|
|
4718
|
+
});
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
|
|
4722
|
+
function fullscreenSvg() {
|
|
4723
|
+
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;
|
|
4724
|
+
}
|
|
4725
|
+
|
|
4726
|
+
function fullscreenExitSvg() {
|
|
4727
|
+
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;
|
|
4728
|
+
}
|
|
4729
|
+
|
|
4730
|
+
const controlElement = document.createElement('div');
|
|
4731
|
+
const INIT_LOADING_TIMEOUT = 20000;
|
|
4732
|
+
/**
|
|
4733
|
+
* Tiny WFS-T client to insert (drawing/uploading), modify and delete
|
|
4159
4734
|
* features on GeoServers using OpenLayers. Layers with these types
|
|
4160
4735
|
* of geometries are supported: "GeometryCollection" (in this case, you can
|
|
4161
4736
|
* choose the geometry type of each element to draw), "Point", "MultiPoint",
|
|
@@ -4182,6 +4757,14 @@
|
|
|
4182
4757
|
}
|
|
4183
4758
|
});
|
|
4184
4759
|
this._initialized = false;
|
|
4760
|
+
this._queryOverlayId = 'ol-wfst--query-overlay';
|
|
4761
|
+
this._shiftPressed = false;
|
|
4762
|
+
this._hoveredFeature = null;
|
|
4763
|
+
this._keySelectBound = false;
|
|
4764
|
+
// Editing
|
|
4765
|
+
this._editFeaturesOriginal = {};
|
|
4766
|
+
this._multiOverlayId = 'ol-wfst--multi-edit-overlay';
|
|
4767
|
+
this._extendOverlays = [];
|
|
4185
4768
|
setLang(options.language, options.i18n);
|
|
4186
4769
|
const defaultOptions = getDefaultOptions();
|
|
4187
4770
|
this._options = deepObjectAssign(defaultOptions, options);
|
|
@@ -4222,8 +4805,10 @@
|
|
|
4222
4805
|
if (layers.length) {
|
|
4223
4806
|
let layerRendered = 0;
|
|
4224
4807
|
let layersNumber = 0; // Only count visibles
|
|
4808
|
+
showLoading();
|
|
4809
|
+
const layerInitPromises = [];
|
|
4225
4810
|
layers.forEach((layer) => {
|
|
4226
|
-
if (layer.
|
|
4811
|
+
if (layer.isVisible(this._view))
|
|
4227
4812
|
layersNumber++;
|
|
4228
4813
|
layer.on('layerRendered', () => {
|
|
4229
4814
|
layerRendered++;
|
|
@@ -4237,15 +4822,17 @@
|
|
|
4237
4822
|
}
|
|
4238
4823
|
});
|
|
4239
4824
|
layer.on('change:describeFeatureType', () => {
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4245
|
-
|
|
4246
|
-
|
|
4247
|
-
|
|
4248
|
-
|
|
4825
|
+
if (this._options.showControl && layers.length > 1) {
|
|
4826
|
+
const domEl = this._layersControl.addLayerEl(layer);
|
|
4827
|
+
layer.on('change:isVisible', () => {
|
|
4828
|
+
const layerNotVisible = 'ol-wfst--layer-not-visible';
|
|
4829
|
+
const visible = layer.isVisibleByZoom();
|
|
4830
|
+
if (visible)
|
|
4831
|
+
domEl.classList.remove(layerNotVisible);
|
|
4832
|
+
else
|
|
4833
|
+
domEl.classList.add(layerNotVisible);
|
|
4834
|
+
});
|
|
4835
|
+
}
|
|
4249
4836
|
layer.set(BaseLayerProperty.ISVISIBLE, this._currentZoom > layer.getMinZoom());
|
|
4250
4837
|
this.dispatchEvent(new WfstEvent({
|
|
4251
4838
|
type: 'describeFeatureType',
|
|
@@ -4253,13 +4840,21 @@
|
|
|
4253
4840
|
data: layer.getDescribeFeatureType()
|
|
4254
4841
|
}));
|
|
4255
4842
|
});
|
|
4256
|
-
layer._init();
|
|
4843
|
+
layerInitPromises.push(layer._init());
|
|
4257
4844
|
this._map.addLayer(layer);
|
|
4258
4845
|
setMapLayers({
|
|
4259
4846
|
[layer.get(BaseLayerProperty.NAME)]: layer
|
|
4260
4847
|
});
|
|
4261
4848
|
});
|
|
4262
4849
|
this._createMapElements(this._options.showControl, this._options.active);
|
|
4850
|
+
// Show the loading bar until every layer has resolved its
|
|
4851
|
+
// DescribeFeatureType (success or error), with a fail-safe
|
|
4852
|
+
// timeout so it never stays visible on a dead connection.
|
|
4853
|
+
await Promise.race([
|
|
4854
|
+
Promise.all(layerInitPromises),
|
|
4855
|
+
new Promise((resolve) => setTimeout(resolve, INIT_LOADING_TIMEOUT))
|
|
4856
|
+
]);
|
|
4857
|
+
showLoading(false);
|
|
4263
4858
|
}
|
|
4264
4859
|
}
|
|
4265
4860
|
catch (err) {
|
|
@@ -4274,6 +4869,7 @@
|
|
|
4274
4869
|
this._map = super.getMap();
|
|
4275
4870
|
this._view = this._map.getView();
|
|
4276
4871
|
this._viewport = this._map.getViewport();
|
|
4872
|
+
this._map.getTargetElement().classList.add('ol-wfst--map');
|
|
4277
4873
|
setMap(this._map);
|
|
4278
4874
|
//@ts-expect-error
|
|
4279
4875
|
this._uploads.on('addedFeatures', ({ features }) => {
|
|
@@ -4292,17 +4888,26 @@
|
|
|
4292
4888
|
});
|
|
4293
4889
|
});
|
|
4294
4890
|
// @ts-expect-error
|
|
4295
|
-
this._editFields.on('save', ({ feature }) => {
|
|
4296
|
-
|
|
4297
|
-
this.
|
|
4891
|
+
this._editFields.on('save', async ({ feature, features }) => {
|
|
4892
|
+
const list = features || [feature];
|
|
4893
|
+
const ok = await this._transactEditList(list);
|
|
4894
|
+
if (ok) {
|
|
4895
|
+
Array.from(list).forEach((f) => this._collectionModify.remove(f));
|
|
4896
|
+
}
|
|
4298
4897
|
});
|
|
4299
4898
|
// @ts-expect-error
|
|
4300
|
-
this._editFields.on('delete', ({
|
|
4301
|
-
this._deleteFeature(
|
|
4899
|
+
this._editFields.on('delete', ({ features }) => {
|
|
4900
|
+
this._deleteFeature(features, true);
|
|
4302
4901
|
});
|
|
4303
4902
|
this._addMapEvents();
|
|
4903
|
+
this._addMiddleButtonPan();
|
|
4304
4904
|
initModal(this._options['modal']);
|
|
4305
|
-
this.
|
|
4905
|
+
this._layersWidgetDiv = document.createElement('div');
|
|
4906
|
+
this._layersWidgetDiv.className = 'ol-wfst--layers-control';
|
|
4907
|
+
this._layersWidgetDiv.append(initLoading());
|
|
4908
|
+
this._map.addControl(new Control({
|
|
4909
|
+
element: this._layersWidgetDiv
|
|
4910
|
+
}));
|
|
4306
4911
|
this._initMapAndLayers();
|
|
4307
4912
|
}
|
|
4308
4913
|
/**
|
|
@@ -4318,8 +4923,18 @@
|
|
|
4318
4923
|
this._prepareEditLayer();
|
|
4319
4924
|
this._addInteractions();
|
|
4320
4925
|
this._addInteractionHandlers();
|
|
4926
|
+
this._prepareHighlightLayer();
|
|
4927
|
+
this._addQueryInteraction();
|
|
4321
4928
|
if (showControl) {
|
|
4322
4929
|
this._addMapControl();
|
|
4930
|
+
if (this._options.fullscreen) {
|
|
4931
|
+
this._map.addControl(new FullScreen({
|
|
4932
|
+
className: 'ol-wfst--fullscreen',
|
|
4933
|
+
tipLabel: I18N.labels.fullscreen,
|
|
4934
|
+
label: fullscreenSvg(),
|
|
4935
|
+
labelActive: fullscreenExitSvg()
|
|
4936
|
+
}));
|
|
4937
|
+
}
|
|
4323
4938
|
}
|
|
4324
4939
|
// By default, init in edit mode
|
|
4325
4940
|
this.activateEditMode(active);
|
|
@@ -4338,9 +4953,10 @@
|
|
|
4338
4953
|
this._interactionWfsSelect = new Select({
|
|
4339
4954
|
hitTolerance: 10,
|
|
4340
4955
|
style: (feature) => styleFunction(feature),
|
|
4341
|
-
toggleCondition: condition_js.
|
|
4956
|
+
toggleCondition: condition_js.shiftKeyOnly, // Allow adding features to the selection with shift
|
|
4342
4957
|
filter: (feature, layer) => {
|
|
4343
4958
|
return (getMode() !== Modes.Edit &&
|
|
4959
|
+
getMode() !== Modes.Query &&
|
|
4344
4960
|
layer &&
|
|
4345
4961
|
layer instanceof WfsLayer &&
|
|
4346
4962
|
layer === getActiveLayerToInsertEls());
|
|
@@ -4349,6 +4965,11 @@
|
|
|
4349
4965
|
this._map.addInteraction(this._interactionWfsSelect);
|
|
4350
4966
|
this._interactionWfsSelect.on('select', ({ selected, deselected, mapBrowserEvent }) => {
|
|
4351
4967
|
const coordinate = mapBrowserEvent.coordinate;
|
|
4968
|
+
// Clear hover to avoid keeping the style on the selected feature
|
|
4969
|
+
this._clearHoverState();
|
|
4970
|
+
if (getMode() === Modes.Query) {
|
|
4971
|
+
return;
|
|
4972
|
+
}
|
|
4352
4973
|
if (selected.length) {
|
|
4353
4974
|
selected.forEach((feature) => {
|
|
4354
4975
|
if (!isFeatureEdited(feature)) {
|
|
@@ -4358,6 +4979,7 @@
|
|
|
4358
4979
|
this._addFeatureToEditMode(feature, coordinate, layer.get(BaseLayerProperty.NAME));
|
|
4359
4980
|
}
|
|
4360
4981
|
});
|
|
4982
|
+
this._lockSelectedFeatures();
|
|
4361
4983
|
}
|
|
4362
4984
|
if (deselected.length) {
|
|
4363
4985
|
if (getMode() !== Modes.Edit) {
|
|
@@ -4379,18 +5001,33 @@
|
|
|
4379
5001
|
this._interactionSelectModify = new Select({
|
|
4380
5002
|
style: (feature) => styleFunction(feature),
|
|
4381
5003
|
layers: [getEditLayer()],
|
|
4382
|
-
toggleCondition: condition_js.
|
|
5004
|
+
toggleCondition: condition_js.shiftKeyOnly, // Allow adding features with shift
|
|
4383
5005
|
removeCondition: () => (getMode() === Modes.Edit ? true : false) // Prevent deselect on clicking outside the feature
|
|
4384
5006
|
});
|
|
4385
5007
|
this._map.addInteraction(this._interactionSelectModify);
|
|
5008
|
+
// When clicking without shift, replace the current selection: remove
|
|
5009
|
+
// any previously selected and not re-selected feature from the edit list
|
|
5010
|
+
this._interactionSelectModify.on('select', ({ selected, mapBrowserEvent }) => {
|
|
5011
|
+
if (!selected.length ||
|
|
5012
|
+
!mapBrowserEvent ||
|
|
5013
|
+
condition_js.shiftKeyOnly(mapBrowserEvent)) {
|
|
5014
|
+
return;
|
|
5015
|
+
}
|
|
5016
|
+
const selectedSet = new Set(selected);
|
|
5017
|
+
Array.from(this._collectionModify.getArray()).forEach((feature) => {
|
|
5018
|
+
if (!selectedSet.has(feature)) {
|
|
5019
|
+
this._collectionModify.remove(feature);
|
|
5020
|
+
}
|
|
5021
|
+
});
|
|
5022
|
+
});
|
|
4386
5023
|
this._collectionModify =
|
|
4387
5024
|
this._interactionSelectModify.getFeatures();
|
|
4388
|
-
this._keyClickWms = this._map.on(this._options.evtType, async (evt) => {
|
|
5025
|
+
this._keyClickWms = this._map.on(this._options.evtType || 'singleclick', async (evt) => {
|
|
4389
5026
|
if (this._map.hasFeatureAtPixel(evt.pixel)) {
|
|
4390
5027
|
return;
|
|
4391
5028
|
}
|
|
4392
5029
|
// Only get other features if editmode is disabled
|
|
4393
|
-
if (getMode() !== Modes.Edit) {
|
|
5030
|
+
if (getMode() !== Modes.Edit && getMode() !== Modes.Query) {
|
|
4394
5031
|
const layer = getActiveLayerToInsertEls();
|
|
4395
5032
|
// If layer is hidden or is a wfs, skip
|
|
4396
5033
|
if (!layer.getVisible() ||
|
|
@@ -4402,8 +5039,14 @@
|
|
|
4402
5039
|
if (!(features === null || features === void 0 ? void 0 : features.length)) {
|
|
4403
5040
|
return;
|
|
4404
5041
|
}
|
|
4405
|
-
//
|
|
5042
|
+
// Without shift, replace the current selection
|
|
5043
|
+
if (!condition_js.shiftKeyOnly(evt)) {
|
|
5044
|
+
Array.from(this._collectionModify.getArray()).forEach((feature) => {
|
|
5045
|
+
this._collectionModify.remove(feature);
|
|
5046
|
+
});
|
|
5047
|
+
}
|
|
4406
5048
|
this._addFeatureToEditMode(features[0], evt.coordinate, layer.get(BaseLayerProperty.NAME));
|
|
5049
|
+
this._lockSelectedFeatures();
|
|
4407
5050
|
}
|
|
4408
5051
|
});
|
|
4409
5052
|
};
|
|
@@ -4414,20 +5057,9 @@
|
|
|
4414
5057
|
prepareWmsInteraction();
|
|
4415
5058
|
}
|
|
4416
5059
|
this._interactionModify = new Modify({
|
|
4417
|
-
style: () => {
|
|
5060
|
+
style: (feature) => {
|
|
4418
5061
|
if (getMode() === Modes.Edit) {
|
|
4419
|
-
return
|
|
4420
|
-
image: new CircleStyle({
|
|
4421
|
-
radius: 6,
|
|
4422
|
-
fill: new Fill({
|
|
4423
|
-
color: '#ff0000'
|
|
4424
|
-
}),
|
|
4425
|
-
stroke: new Stroke({
|
|
4426
|
-
width: 2,
|
|
4427
|
-
color: 'rgba(5, 5, 5, 0.9)'
|
|
4428
|
-
})
|
|
4429
|
-
})
|
|
4430
|
-
});
|
|
5062
|
+
return editVertexStyles(feature.get('existing') === true);
|
|
4431
5063
|
}
|
|
4432
5064
|
else {
|
|
4433
5065
|
return;
|
|
@@ -4443,6 +5075,175 @@
|
|
|
4443
5075
|
source: getEditLayer().getSource()
|
|
4444
5076
|
});
|
|
4445
5077
|
this._map.addInteraction(this._interactionSnap);
|
|
5078
|
+
this._addSelectionInteractions();
|
|
5079
|
+
}
|
|
5080
|
+
/**
|
|
5081
|
+
* Interactions to select features by drawing a box (DragBox) or a freehand
|
|
5082
|
+
* polygon (freehand Draw lasso). The selected features are added to the
|
|
5083
|
+
* edit list, keeping the same behaviour as the single click selection.
|
|
5084
|
+
*
|
|
5085
|
+
* @private
|
|
5086
|
+
*/
|
|
5087
|
+
_addSelectionInteractions() {
|
|
5088
|
+
const selectionStyle = new Style({
|
|
5089
|
+
fill: new Fill({
|
|
5090
|
+
color: 'rgba(255, 200, 0, 0.1)'
|
|
5091
|
+
}),
|
|
5092
|
+
stroke: new Stroke({
|
|
5093
|
+
color: '#ffc800',
|
|
5094
|
+
width: 2,
|
|
5095
|
+
lineDash: [6, 4]
|
|
5096
|
+
})
|
|
5097
|
+
});
|
|
5098
|
+
// Box selection (dragging a rectangle)
|
|
5099
|
+
this._interactionDragBox = new DragBox({
|
|
5100
|
+
condition: (evt) => condition_js.primaryAction(evt) && this._isSelectMode(SelectionMode.Box),
|
|
5101
|
+
className: 'ol-wfst--dragbox'
|
|
5102
|
+
});
|
|
5103
|
+
this._interactionDragBox.on('boxend', (evt) => {
|
|
5104
|
+
if (!this._isSelectMode(SelectionMode.Box)) {
|
|
5105
|
+
return;
|
|
5106
|
+
}
|
|
5107
|
+
// Without shift, replace the current selection
|
|
5108
|
+
if (!evt.mapBrowserEvent.originalEvent.shiftKey) {
|
|
5109
|
+
this._collectionModify.clear();
|
|
5110
|
+
}
|
|
5111
|
+
this._selectByGeometry(this._interactionDragBox.getGeometry());
|
|
5112
|
+
});
|
|
5113
|
+
this._map.addInteraction(this._interactionDragBox);
|
|
5114
|
+
// Freehand selection (holding the mouse to draw a closed lasso)
|
|
5115
|
+
this._interactionFreehandSelect = new Draw({
|
|
5116
|
+
type: GeometryType.Polygon,
|
|
5117
|
+
freehand: true,
|
|
5118
|
+
stopClick: true,
|
|
5119
|
+
style: selectionStyle,
|
|
5120
|
+
condition: (evt) => condition_js.primaryAction(evt) && this._isSelectMode(SelectionMode.Freehand)
|
|
5121
|
+
});
|
|
5122
|
+
this._interactionFreehandSelect.on('drawend', (evt) => {
|
|
5123
|
+
if (!this._isSelectMode(SelectionMode.Freehand)) {
|
|
5124
|
+
return;
|
|
5125
|
+
}
|
|
5126
|
+
const geometry = evt.feature.getGeometry();
|
|
5127
|
+
const ring = geometry ? geometry.getCoordinates()[0] : [];
|
|
5128
|
+
// Ignore degenerate lassos (a simple click without dragging)
|
|
5129
|
+
if (!ring.length || ring.length < 4 || geometry.getArea() <= 0) {
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
// Without shift, replace the current selection
|
|
5133
|
+
if (!this._shiftPressed) {
|
|
5134
|
+
this._collectionModify.clear();
|
|
5135
|
+
}
|
|
5136
|
+
this._selectByGeometry(geometry);
|
|
5137
|
+
});
|
|
5138
|
+
this._map.addInteraction(this._interactionFreehandSelect);
|
|
5139
|
+
this._refreshSelectionInteractions();
|
|
5140
|
+
}
|
|
5141
|
+
/**
|
|
5142
|
+
* Whether a selection tool mode is currently usable: only outside the
|
|
5143
|
+
* edit/draw modes.
|
|
5144
|
+
*
|
|
5145
|
+
* @param mode
|
|
5146
|
+
* @private
|
|
5147
|
+
*/
|
|
5148
|
+
_isSelectMode(mode) {
|
|
5149
|
+
return getMode() === null && getSelectionMode() === mode;
|
|
5150
|
+
}
|
|
5151
|
+
/**
|
|
5152
|
+
* Enable/disable the box and freehand selection interactions according to
|
|
5153
|
+
* the current selection tool and map mode.
|
|
5154
|
+
*
|
|
5155
|
+
* @private
|
|
5156
|
+
*/
|
|
5157
|
+
_refreshSelectionInteractions() {
|
|
5158
|
+
const active = getMode() === null;
|
|
5159
|
+
if (this._interactionDragBox) {
|
|
5160
|
+
this._interactionDragBox.setActive(active && getSelectionMode() === SelectionMode.Box);
|
|
5161
|
+
}
|
|
5162
|
+
if (this._interactionFreehandSelect) {
|
|
5163
|
+
this._interactionFreehandSelect.setActive(active && getSelectionMode() === SelectionMode.Freehand);
|
|
5164
|
+
}
|
|
5165
|
+
}
|
|
5166
|
+
/**
|
|
5167
|
+
* Select the features that intersect the given polygon on the active layer
|
|
5168
|
+
* and add them to the edit list. WFS features are picked from the already
|
|
5169
|
+
* loaded source, while WMS features are requested to the GeoServer with a
|
|
5170
|
+
* WFS GetFeature INTERSECTS filter.
|
|
5171
|
+
*
|
|
5172
|
+
* @param geometry
|
|
5173
|
+
* @private
|
|
5174
|
+
*/
|
|
5175
|
+
async _selectByGeometry(geometry) {
|
|
5176
|
+
const layer = getActiveLayerToInsertEls();
|
|
5177
|
+
if (!layer || !layer.getVisible() || !layer.isVisibleByZoom()) {
|
|
5178
|
+
showError(I18N.errors.layerNotVisible);
|
|
5179
|
+
return;
|
|
5180
|
+
}
|
|
5181
|
+
let features;
|
|
5182
|
+
if (layer instanceof WfsLayer) {
|
|
5183
|
+
const candidates = layer
|
|
5184
|
+
.getSource()
|
|
5185
|
+
.getFeaturesInExtent(geometry.getExtent());
|
|
5186
|
+
// The extent is only a bounding box; for the freehand lasso be
|
|
5187
|
+
// more precise and keep the features whose vertices fall inside it
|
|
5188
|
+
if (getSelectionMode() === SelectionMode.Freehand) {
|
|
5189
|
+
features = candidates.filter((feature) => {
|
|
5190
|
+
const featureGeom = feature.getGeometry();
|
|
5191
|
+
if (!featureGeom) {
|
|
5192
|
+
return false;
|
|
5193
|
+
}
|
|
5194
|
+
const flatCoords = featureGeom.getFlatCoordinates();
|
|
5195
|
+
const stride = featureGeom.getStride();
|
|
5196
|
+
for (let i = 0; i < flatCoords.length; i += stride) {
|
|
5197
|
+
if (geometry.intersectsCoordinate([
|
|
5198
|
+
flatCoords[i],
|
|
5199
|
+
flatCoords[i + 1]
|
|
5200
|
+
])) {
|
|
5201
|
+
return true;
|
|
5202
|
+
}
|
|
5203
|
+
}
|
|
5204
|
+
return false;
|
|
5205
|
+
});
|
|
5206
|
+
}
|
|
5207
|
+
else {
|
|
5208
|
+
features = candidates;
|
|
5209
|
+
}
|
|
5210
|
+
}
|
|
5211
|
+
else if (layer instanceof WmsLayer) {
|
|
5212
|
+
features = await layer._getFeaturesInGeometry(geometry);
|
|
5213
|
+
}
|
|
5214
|
+
if (features) {
|
|
5215
|
+
this._selectFeatures(features);
|
|
5216
|
+
}
|
|
5217
|
+
}
|
|
5218
|
+
/**
|
|
5219
|
+
* Add the given features to the edit mode:
|
|
5220
|
+
* remove them from their original layer and push them into the edit
|
|
5221
|
+
* collection so they can be modified/deleted like the ones selected
|
|
5222
|
+
* with a single click.
|
|
5223
|
+
*
|
|
5224
|
+
* @param features
|
|
5225
|
+
* @private
|
|
5226
|
+
*/
|
|
5227
|
+
_selectFeatures(features) {
|
|
5228
|
+
if (!features || !features.length) {
|
|
5229
|
+
return;
|
|
5230
|
+
}
|
|
5231
|
+
const layer = getActiveLayerToInsertEls();
|
|
5232
|
+
const layerName = layer.get(BaseLayerProperty.NAME);
|
|
5233
|
+
const selectedIds = new Set(this._collectionModify.getArray().map((feature) => feature.getId()));
|
|
5234
|
+
features.forEach((feature) => {
|
|
5235
|
+
if (isFeatureEdited(feature) || selectedIds.has(feature.getId())) {
|
|
5236
|
+
return;
|
|
5237
|
+
}
|
|
5238
|
+
// WFS features are stored in their layer source: remove them from
|
|
5239
|
+
// there while they are being edited
|
|
5240
|
+
if (layer instanceof WfsLayer &&
|
|
5241
|
+
layer.getSource().hasFeature(feature)) {
|
|
5242
|
+
layer.getSource().removeFeature(feature);
|
|
5243
|
+
}
|
|
5244
|
+
this._addFeatureToEditMode(feature, null, layerName);
|
|
5245
|
+
});
|
|
5246
|
+
this._lockSelectedFeatures();
|
|
4446
5247
|
}
|
|
4447
5248
|
/**
|
|
4448
5249
|
* Layer to store temporary the elements to be edited
|
|
@@ -4451,6 +5252,166 @@
|
|
|
4451
5252
|
_prepareEditLayer() {
|
|
4452
5253
|
this._map.addLayer(getEditLayer());
|
|
4453
5254
|
}
|
|
5255
|
+
/**
|
|
5256
|
+
* Create the dedicated layer used to highlight the queried feature, so
|
|
5257
|
+
* the source layers keep their own rendering untouched.
|
|
5258
|
+
* @private
|
|
5259
|
+
*/
|
|
5260
|
+
_prepareHighlightLayer() {
|
|
5261
|
+
this._highlightSource = new VectorSource();
|
|
5262
|
+
this._highlightLayer = new VectorLayer({
|
|
5263
|
+
source: this._highlightSource,
|
|
5264
|
+
style: (feature) => queryStyleFunction(),
|
|
5265
|
+
zIndex: 100
|
|
5266
|
+
});
|
|
5267
|
+
this._map.addLayer(this._highlightLayer);
|
|
5268
|
+
}
|
|
5269
|
+
/**
|
|
5270
|
+
* Listen for clicks while the query mode is active: get the clicked
|
|
5271
|
+
* feature of the active layer (from the already loaded source for WFS,
|
|
5272
|
+
* or through a GetFeatureInfo request for WMS), highlight it and show
|
|
5273
|
+
* its attributes in a popup.
|
|
5274
|
+
* @private
|
|
5275
|
+
*/
|
|
5276
|
+
_addQueryInteraction() {
|
|
5277
|
+
this._keyClickQuery = this._map.on(this._options.evtType || 'singleclick', async (evt) => {
|
|
5278
|
+
if (getMode() !== Modes.Query) {
|
|
5279
|
+
return;
|
|
5280
|
+
}
|
|
5281
|
+
const layer = getActiveLayerToInsertEls();
|
|
5282
|
+
// Skip hidden layers
|
|
5283
|
+
if (!layer.getVisible() || !layer.isVisibleByZoom()) {
|
|
5284
|
+
return;
|
|
5285
|
+
}
|
|
5286
|
+
let feature = null;
|
|
5287
|
+
if (layer instanceof WfsLayer) {
|
|
5288
|
+
const featuresAtPixel = this._map.getFeaturesAtPixel(evt.pixel, {
|
|
5289
|
+
hitTolerance: 10,
|
|
5290
|
+
layerFilter: (candidate) => candidate === layer
|
|
5291
|
+
});
|
|
5292
|
+
if (featuresAtPixel && featuresAtPixel.length) {
|
|
5293
|
+
feature = featuresAtPixel[0];
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
else if (layer instanceof WmsLayer) {
|
|
5297
|
+
const features = await layer._getFeaturesByClickEvent(evt);
|
|
5298
|
+
if (features === null || features === void 0 ? void 0 : features.length) {
|
|
5299
|
+
feature = features[0];
|
|
5300
|
+
}
|
|
5301
|
+
}
|
|
5302
|
+
// Click on empty space: clear the previous result
|
|
5303
|
+
if (!feature) {
|
|
5304
|
+
this._clearQueryResult();
|
|
5305
|
+
return;
|
|
5306
|
+
}
|
|
5307
|
+
this._showQueryResult(feature, layer, evt.coordinate);
|
|
5308
|
+
});
|
|
5309
|
+
}
|
|
5310
|
+
/**
|
|
5311
|
+
* Highlight the given feature and attach a popup with its attributes.
|
|
5312
|
+
* @param feature
|
|
5313
|
+
* @param layer
|
|
5314
|
+
* @param coordinate
|
|
5315
|
+
* @private
|
|
5316
|
+
*/
|
|
5317
|
+
_showQueryResult(feature, layer, coordinate) {
|
|
5318
|
+
this._clearQueryResult();
|
|
5319
|
+
this._highlightSource.addFeature(feature);
|
|
5320
|
+
this._queryOverlay = new QueryOverlay(feature, coordinate, layer);
|
|
5321
|
+
// @ts-expect-error
|
|
5322
|
+
this._queryOverlay.on('close', () => {
|
|
5323
|
+
this._clearQueryResult();
|
|
5324
|
+
});
|
|
5325
|
+
this._map.addOverlay(this._queryOverlay);
|
|
5326
|
+
}
|
|
5327
|
+
/**
|
|
5328
|
+
* Remove the query popup overlay if any.
|
|
5329
|
+
* @private
|
|
5330
|
+
*/
|
|
5331
|
+
_removeQueryOverlay() {
|
|
5332
|
+
if (this._queryOverlay) {
|
|
5333
|
+
this._map.removeOverlay(this._queryOverlay);
|
|
5334
|
+
this._queryOverlay = null;
|
|
5335
|
+
return;
|
|
5336
|
+
}
|
|
5337
|
+
const overlay = this._map.getOverlayById(this._queryOverlayId);
|
|
5338
|
+
if (overlay) {
|
|
5339
|
+
this._map.removeOverlay(overlay);
|
|
5340
|
+
}
|
|
5341
|
+
}
|
|
5342
|
+
/**
|
|
5343
|
+
* Clear the highlight and remove the query popup.
|
|
5344
|
+
* @private
|
|
5345
|
+
*/
|
|
5346
|
+
_clearQueryResult() {
|
|
5347
|
+
var _a;
|
|
5348
|
+
(_a = this._highlightSource) === null || _a === void 0 ? void 0 : _a.clear();
|
|
5349
|
+
this._removeQueryOverlay();
|
|
5350
|
+
}
|
|
5351
|
+
/**
|
|
5352
|
+
* Activate/deactivate the query mode: while it is active, clicking a
|
|
5353
|
+
* feature of the active layer shows its info in a popup instead of
|
|
5354
|
+
* selecting it for editing.
|
|
5355
|
+
*
|
|
5356
|
+
* @param bool
|
|
5357
|
+
* @public
|
|
5358
|
+
*/
|
|
5359
|
+
activateQueryMode(bool = true) {
|
|
5360
|
+
var _a, _b;
|
|
5361
|
+
if (bool) {
|
|
5362
|
+
// While a geometry/fields edit is in progress the tools toolbar is
|
|
5363
|
+
// hidden)Skip, so the only way out is Apply, Discard or Delete.
|
|
5364
|
+
// Guard here too so programmatic calls cannot leave the edit mode.
|
|
5365
|
+
if (getMode() === Modes.Edit) {
|
|
5366
|
+
return;
|
|
5367
|
+
}
|
|
5368
|
+
// If features are merely selected (not being edited), deselect
|
|
5369
|
+
// them without transacting: each removal restores the feature to
|
|
5370
|
+
// its original layer and turns the edit mode off.
|
|
5371
|
+
if (this._collectionModify.getLength()) {
|
|
5372
|
+
this._collectionModify
|
|
5373
|
+
.getArray()
|
|
5374
|
+
.slice()
|
|
5375
|
+
.forEach((feature) => {
|
|
5376
|
+
this._collectionModify.remove(feature);
|
|
5377
|
+
});
|
|
5378
|
+
}
|
|
5379
|
+
// Leave any draw interaction active
|
|
5380
|
+
if (this._interactionDraw) {
|
|
5381
|
+
this._map.removeInteraction(this._interactionDraw);
|
|
5382
|
+
this._viewport.classList.remove('draw-mode');
|
|
5383
|
+
}
|
|
5384
|
+
resetStateButtons();
|
|
5385
|
+
deactivateSelectModeButton();
|
|
5386
|
+
activateQueryButton();
|
|
5387
|
+
this._clearHoverState();
|
|
5388
|
+
activateMode(Modes.Query);
|
|
5389
|
+
// Disable the edit selection interactions while querying
|
|
5390
|
+
if (this._interactionSelectModify) {
|
|
5391
|
+
this._interactionSelectModify.setActive(false);
|
|
5392
|
+
}
|
|
5393
|
+
if (this._interactionWfsSelect) {
|
|
5394
|
+
this._interactionWfsSelect.setActive(false);
|
|
5395
|
+
}
|
|
5396
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(false);
|
|
5397
|
+
this._syncExtendLineOverlays();
|
|
5398
|
+
this._refreshSelectionInteractions();
|
|
5399
|
+
}
|
|
5400
|
+
else {
|
|
5401
|
+
activateMode(null);
|
|
5402
|
+
deactivateQueryButton();
|
|
5403
|
+
activateSelectModeButton(getSelectionMode());
|
|
5404
|
+
if (this._interactionSelectModify) {
|
|
5405
|
+
this._interactionSelectModify.setActive(true);
|
|
5406
|
+
}
|
|
5407
|
+
if (this._interactionWfsSelect) {
|
|
5408
|
+
this._interactionWfsSelect.setActive(true);
|
|
5409
|
+
}
|
|
5410
|
+
(_b = this._interactionModify) === null || _b === void 0 ? void 0 : _b.setActive(true);
|
|
5411
|
+
this._refreshSelectionInteractions();
|
|
5412
|
+
}
|
|
5413
|
+
this._clearQueryResult();
|
|
5414
|
+
}
|
|
4454
5415
|
/**
|
|
4455
5416
|
* @private
|
|
4456
5417
|
*/
|
|
@@ -4465,16 +5426,74 @@
|
|
|
4465
5426
|
return;
|
|
4466
5427
|
}
|
|
4467
5428
|
if (key === 'Delete') {
|
|
4468
|
-
const selectedFeatures = this._collectionModify;
|
|
4469
|
-
if (selectedFeatures) {
|
|
4470
|
-
|
|
4471
|
-
this._deleteFeature(feature, true);
|
|
4472
|
-
});
|
|
5429
|
+
const selectedFeatures = this._collectionModify.getArray();
|
|
5430
|
+
if (selectedFeatures.length) {
|
|
5431
|
+
this._deleteFeature(selectedFeatures, true);
|
|
4473
5432
|
}
|
|
4474
5433
|
}
|
|
4475
5434
|
});
|
|
4476
5435
|
};
|
|
4477
5436
|
keyboardEvents();
|
|
5437
|
+
/**
|
|
5438
|
+
* Change the cursor to a pointing hand and highlight the feature
|
|
5439
|
+
* when hovering over a WFS feature of the active layer.
|
|
5440
|
+
* @private
|
|
5441
|
+
*/
|
|
5442
|
+
const pointerMoveHandler = (evt) => {
|
|
5443
|
+
if (evt.dragging) {
|
|
5444
|
+
return;
|
|
5445
|
+
}
|
|
5446
|
+
// In edit mode, give pointer feedback on the editable
|
|
5447
|
+
// vertices/segments
|
|
5448
|
+
if (getMode() === Modes.Edit) {
|
|
5449
|
+
this._clearHoverState();
|
|
5450
|
+
this._updateEditCursor(evt);
|
|
5451
|
+
return;
|
|
5452
|
+
}
|
|
5453
|
+
// Only show the hover outside edit and draw modes
|
|
5454
|
+
if (getMode() !== null) {
|
|
5455
|
+
this._clearHoverState();
|
|
5456
|
+
return;
|
|
5457
|
+
}
|
|
5458
|
+
const layer = getActiveLayerToInsertEls();
|
|
5459
|
+
// Hover only works on visible WFS layers
|
|
5460
|
+
if (!layer ||
|
|
5461
|
+
!layer.getVisible() ||
|
|
5462
|
+
!layer.isVisibleByZoom() ||
|
|
5463
|
+
!(layer instanceof WfsLayer)) {
|
|
5464
|
+
this._clearHoverState();
|
|
5465
|
+
return;
|
|
5466
|
+
}
|
|
5467
|
+
const features = this._map.getFeaturesAtPixel(evt.pixel, {
|
|
5468
|
+
hitTolerance: 10,
|
|
5469
|
+
layerFilter: (candidate) => candidate === layer
|
|
5470
|
+
});
|
|
5471
|
+
if (features && features.length) {
|
|
5472
|
+
const feature = features[0];
|
|
5473
|
+
if (feature !== this._hoveredFeature) {
|
|
5474
|
+
this._clearHoverState();
|
|
5475
|
+
this._hoveredFeature = feature;
|
|
5476
|
+
this._hoveredFeature.setStyle((f) => styleFunction(f, true));
|
|
5477
|
+
}
|
|
5478
|
+
if (this._viewport.style.cursor !== 'pointer') {
|
|
5479
|
+
this._viewport.style.cursor = 'pointer';
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
else {
|
|
5483
|
+
this._clearHoverState();
|
|
5484
|
+
}
|
|
5485
|
+
};
|
|
5486
|
+
this._map.on('pointermove', pointerMoveHandler);
|
|
5487
|
+
// Store the shift state when a gesture starts, so the freehand
|
|
5488
|
+
// selection can decide between adding or replacing the selection
|
|
5489
|
+
this._viewport.addEventListener('pointerdown', (evt) => {
|
|
5490
|
+
this._shiftPressed = evt.shiftKey;
|
|
5491
|
+
});
|
|
5492
|
+
// Clear the hover before firing a click, otherwise the Select interaction
|
|
5493
|
+
// would capture the hover style as the original style of the feature and
|
|
5494
|
+
// restore it on deselect, leaving the highlight permanently enabled.
|
|
5495
|
+
this._viewport.addEventListener('pointerdown', () => this._clearHoverState());
|
|
5496
|
+
this._viewport.addEventListener('pointerleave', () => this._clearHoverState(), { once: false });
|
|
4478
5497
|
this._map.on('moveend', () => {
|
|
4479
5498
|
this._currentZoom = this._view.getZoom();
|
|
4480
5499
|
if (this._currentZoom !== this._lastZoom) {
|
|
@@ -4498,6 +5517,62 @@
|
|
|
4498
5517
|
}
|
|
4499
5518
|
});
|
|
4500
5519
|
}
|
|
5520
|
+
/**
|
|
5521
|
+
* Allow panning with the middle mouse button at all times, even when the
|
|
5522
|
+
* draw/freehand/box interactions are consuming the left button. The
|
|
5523
|
+
* middle pointerdown is intercepted in the capture phase so OpenLayers
|
|
5524
|
+
* never processes it and the active tool is not affected.
|
|
5525
|
+
*
|
|
5526
|
+
* @private
|
|
5527
|
+
*/
|
|
5528
|
+
_addMiddleButtonPan() {
|
|
5529
|
+
const viewport = this._viewport;
|
|
5530
|
+
let panning = false;
|
|
5531
|
+
let lastX = 0;
|
|
5532
|
+
let lastY = 0;
|
|
5533
|
+
const isMiddleButton = (evt) => evt.pointerType === 'mouse' && evt.button === 1;
|
|
5534
|
+
const stopPanning = () => {
|
|
5535
|
+
panning = false;
|
|
5536
|
+
};
|
|
5537
|
+
viewport.addEventListener('pointerdown', (evt) => {
|
|
5538
|
+
if (!isMiddleButton(evt)) {
|
|
5539
|
+
return;
|
|
5540
|
+
}
|
|
5541
|
+
evt.preventDefault();
|
|
5542
|
+
evt.stopPropagation();
|
|
5543
|
+
viewport.setPointerCapture(evt.pointerId);
|
|
5544
|
+
panning = true;
|
|
5545
|
+
lastX = evt.clientX;
|
|
5546
|
+
lastY = evt.clientY;
|
|
5547
|
+
if (this._view.getAnimating()) {
|
|
5548
|
+
this._view.cancelAnimations();
|
|
5549
|
+
}
|
|
5550
|
+
}, true);
|
|
5551
|
+
viewport.addEventListener('pointermove', (evt) => {
|
|
5552
|
+
if (!panning) {
|
|
5553
|
+
return;
|
|
5554
|
+
}
|
|
5555
|
+
evt.preventDefault();
|
|
5556
|
+
const delta = [lastX - evt.clientX, evt.clientY - lastY];
|
|
5557
|
+
lastX = evt.clientX;
|
|
5558
|
+
lastY = evt.clientY;
|
|
5559
|
+
const resolution = this._view.getResolution();
|
|
5560
|
+
const rotation = this._view.getRotation();
|
|
5561
|
+
const cosAngle = Math.cos(rotation);
|
|
5562
|
+
const sinAngle = Math.sin(rotation);
|
|
5563
|
+
const x = (delta[0] * cosAngle - delta[1] * sinAngle) * resolution;
|
|
5564
|
+
const y = (delta[1] * cosAngle + delta[0] * sinAngle) * resolution;
|
|
5565
|
+
this._view.adjustCenterInternal([x, y]);
|
|
5566
|
+
});
|
|
5567
|
+
const endPan = () => {
|
|
5568
|
+
if (!panning) {
|
|
5569
|
+
return;
|
|
5570
|
+
}
|
|
5571
|
+
stopPanning();
|
|
5572
|
+
};
|
|
5573
|
+
viewport.addEventListener('pointerup', endPan);
|
|
5574
|
+
viewport.addEventListener('pointercancel', endPan);
|
|
5575
|
+
}
|
|
4501
5576
|
/**
|
|
4502
5577
|
* Add map handlers
|
|
4503
5578
|
* @private
|
|
@@ -4507,13 +5582,19 @@
|
|
|
4507
5582
|
// This prevent events fired on select and deselect features that has no changes and should
|
|
4508
5583
|
// not be updated in the geoserver
|
|
4509
5584
|
this._interactionModify.on('modifyend', (evt) => {
|
|
4510
|
-
|
|
4511
|
-
|
|
5585
|
+
evt.features.forEach((feature) => {
|
|
5586
|
+
addFeatureToEditedList(feature);
|
|
5587
|
+
});
|
|
4512
5588
|
super.dispatchEvent(evt);
|
|
4513
5589
|
});
|
|
4514
5590
|
this._interactionModify.on('modifystart', (evt) => {
|
|
4515
5591
|
super.dispatchEvent(evt);
|
|
4516
5592
|
});
|
|
5593
|
+
// Keep the "continue drawing" buttons glued to the line endpoints
|
|
5594
|
+
// @ts-expect-error - 'modify' is a valid Modify event not typed in the `on` signature
|
|
5595
|
+
this._interactionModify.on('modify', () => {
|
|
5596
|
+
this._updateExtendOverlayPositions();
|
|
5597
|
+
});
|
|
4517
5598
|
this._onDeselectFeatureEvent();
|
|
4518
5599
|
this._onRemoveFeatureEvent();
|
|
4519
5600
|
}
|
|
@@ -4525,6 +5606,9 @@
|
|
|
4525
5606
|
this._layersControl = new LayersControl(this._options.showUpload ? this._uploads : null, this._options.uploadFormats);
|
|
4526
5607
|
// @ts-expect-error
|
|
4527
5608
|
this._layersControl.on('drawMode', () => {
|
|
5609
|
+
if (getMode() === Modes.Query) {
|
|
5610
|
+
this.activateQueryMode(false);
|
|
5611
|
+
}
|
|
4528
5612
|
if (getMode() === Modes.Draw) {
|
|
4529
5613
|
resetStateButtons();
|
|
4530
5614
|
this.activateEditMode();
|
|
@@ -4551,9 +5635,37 @@
|
|
|
4551
5635
|
this.activateDrawMode(getActiveLayerToInsertEls());
|
|
4552
5636
|
}
|
|
4553
5637
|
});
|
|
5638
|
+
[
|
|
5639
|
+
['selectModeSingle', SelectionMode.Single],
|
|
5640
|
+
['selectModeBox', SelectionMode.Box],
|
|
5641
|
+
['selectModeFreehand', SelectionMode.Freehand]
|
|
5642
|
+
].forEach(([evtName, mode]) => {
|
|
5643
|
+
// @ts-expect-error
|
|
5644
|
+
this._layersControl.on(evtName, () => {
|
|
5645
|
+
// Leaving the draw mode when a select tool is chosen
|
|
5646
|
+
if (getMode() === Modes.Draw) {
|
|
5647
|
+
resetStateButtons();
|
|
5648
|
+
this.activateEditMode();
|
|
5649
|
+
}
|
|
5650
|
+
// Leaving the query mode when a select tool is chosen
|
|
5651
|
+
if (getMode() === Modes.Query) {
|
|
5652
|
+
this.activateQueryMode(false);
|
|
5653
|
+
}
|
|
5654
|
+
setSelectionMode(mode);
|
|
5655
|
+
activateSelectModeButton(mode);
|
|
5656
|
+
this._refreshSelectionInteractions();
|
|
5657
|
+
});
|
|
5658
|
+
});
|
|
5659
|
+
// @ts-expect-error
|
|
5660
|
+
this._layersControl.on('queryMode', () => {
|
|
5661
|
+
this.activateQueryMode(getMode() !== Modes.Query);
|
|
5662
|
+
});
|
|
4554
5663
|
const controlEl = this._layersControl.render();
|
|
4555
5664
|
this._selectDraw = controlEl.querySelector('.wfst--tools-control--select-draw');
|
|
4556
|
-
this._controlWidgetToolsDiv.append(controlEl);
|
|
5665
|
+
this._controlWidgetToolsDiv.append(controlEl.querySelector('.wfst--tools-control--head'));
|
|
5666
|
+
if (this._options.layers.length > 1) {
|
|
5667
|
+
this._layersWidgetDiv.append(controlEl.querySelector('.wfst--tools-control--select-layers'));
|
|
5668
|
+
}
|
|
4557
5669
|
}
|
|
4558
5670
|
/**
|
|
4559
5671
|
*
|
|
@@ -4572,6 +5684,9 @@
|
|
|
4572
5684
|
_restoreFeatureToLayer(feature, layerName) {
|
|
4573
5685
|
layerName = layerName || feature.get('_layerName_');
|
|
4574
5686
|
const layer = getStoredMapLayers()[layerName];
|
|
5687
|
+
// Reset any per-feature style (e.g. hover) before returning the feature
|
|
5688
|
+
// to its layer, so it renders with the layer's default style.
|
|
5689
|
+
feature.setStyle(undefined);
|
|
4575
5690
|
layer.getSource().addFeature(feature);
|
|
4576
5691
|
}
|
|
4577
5692
|
/**
|
|
@@ -4605,10 +5720,18 @@
|
|
|
4605
5720
|
this._removeFeatureFromTmpLayer(feature);
|
|
4606
5721
|
}
|
|
4607
5722
|
};
|
|
5723
|
+
if (this._keySelectBound) {
|
|
5724
|
+
return;
|
|
5725
|
+
}
|
|
5726
|
+
this._keySelectBound = true;
|
|
4608
5727
|
// This is fired when a feature is deselected and fires the transaction process
|
|
5728
|
+
if (this._keySelect) {
|
|
5729
|
+
Observable.unByKey(this._keySelect);
|
|
5730
|
+
}
|
|
4609
5731
|
this._keySelect = this._collectionModify.on('remove', (evt) => {
|
|
4610
5732
|
const feature = evt.element;
|
|
4611
5733
|
this._deselectEditFeature(feature);
|
|
5734
|
+
this._syncEditOverlays();
|
|
4612
5735
|
checkIfFeatureIsChanged(feature);
|
|
4613
5736
|
this._editModeOff();
|
|
4614
5737
|
});
|
|
@@ -4629,6 +5752,7 @@
|
|
|
4629
5752
|
}
|
|
4630
5753
|
if (this._keySelect) {
|
|
4631
5754
|
Observable.unByKey(this._keySelect);
|
|
5755
|
+
this._keySelectBound = false;
|
|
4632
5756
|
}
|
|
4633
5757
|
const layerName = feature.get('_layerName_');
|
|
4634
5758
|
const ll = this.getLayerByName(layerName);
|
|
@@ -4643,28 +5767,80 @@
|
|
|
4643
5767
|
});
|
|
4644
5768
|
}
|
|
4645
5769
|
/**
|
|
5770
|
+
* Hide the whole tools toolbar (geometry/draw/select/query buttons and
|
|
5771
|
+
* the layer selector). It is hidden while a geometry/fields edit is in
|
|
5772
|
+
* progress so the only way out is Apply, Discard or Delete.
|
|
4646
5773
|
*
|
|
4647
|
-
* @param feature
|
|
4648
5774
|
* @private
|
|
4649
5775
|
*/
|
|
4650
|
-
|
|
4651
|
-
|
|
5776
|
+
_hideControls() {
|
|
5777
|
+
// While an edit is in progress the tools toolbar and the layers widget
|
|
5778
|
+
// disappear together: the only controls left on screen are Apply,
|
|
5779
|
+
// Discard and Delete.
|
|
5780
|
+
this._controlWidgetToolsDiv.classList.add('ol-wfst--controls--hidden');
|
|
5781
|
+
this._layersWidgetDiv.classList.add('ol-wfst--controls--hidden');
|
|
5782
|
+
}
|
|
5783
|
+
/**
|
|
5784
|
+
* Show the tools toolbar again.
|
|
5785
|
+
*
|
|
5786
|
+
* @private
|
|
5787
|
+
*/
|
|
5788
|
+
_showControls() {
|
|
5789
|
+
this._controlWidgetToolsDiv.classList.remove('ol-wfst--controls--hidden');
|
|
5790
|
+
this._layersWidgetDiv.classList.remove('ol-wfst--controls--hidden');
|
|
5791
|
+
}
|
|
5792
|
+
/**
|
|
5793
|
+
*
|
|
5794
|
+
* @param features
|
|
5795
|
+
* @private
|
|
5796
|
+
*/
|
|
5797
|
+
_editModeOn(features) {
|
|
5798
|
+
features = Array.isArray(features) ? features : [features];
|
|
5799
|
+
features.forEach((feature) => {
|
|
5800
|
+
this._editFeaturesOriginal[String(feature.getId())] =
|
|
5801
|
+
feature.clone();
|
|
5802
|
+
});
|
|
4652
5803
|
activateMode(Modes.Edit);
|
|
5804
|
+
// While a geometry/fields edit is in progress the whole tools toolbar
|
|
5805
|
+
// and the layers widget are hidden together, so the only way out is
|
|
5806
|
+
// Apply, Discard or Delete
|
|
5807
|
+
this._hideControls();
|
|
5808
|
+
// Show the "continue drawing" buttons on the line endpoints
|
|
5809
|
+
this._syncExtendLineOverlays();
|
|
4653
5810
|
// To refresh the style
|
|
4654
5811
|
getEditLayer().getSource().changed();
|
|
4655
|
-
this.
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
|
|
4660
|
-
|
|
5812
|
+
this._refreshSelectionInteractions();
|
|
5813
|
+
const multiOverlay = this._map.getOverlayById(this._multiOverlayId);
|
|
5814
|
+
if (multiOverlay) {
|
|
5815
|
+
this._map.removeOverlay(multiOverlay);
|
|
5816
|
+
}
|
|
5817
|
+
features.forEach((feature) => {
|
|
5818
|
+
this._removeOverlayHelper(feature);
|
|
5819
|
+
});
|
|
5820
|
+
this._controlApplyDiscardChanges = new EditControlChangesEl(features);
|
|
5821
|
+
this._controlApplyDiscardChanges.on('cancel', (evt) => {
|
|
5822
|
+
const list = evt.features;
|
|
5823
|
+
Array.from(list).forEach((feature) => {
|
|
5824
|
+
const original = this._editFeaturesOriginal[String(feature.getId())];
|
|
5825
|
+
if (original) {
|
|
5826
|
+
feature.setGeometry(original.getGeometry());
|
|
5827
|
+
}
|
|
5828
|
+
removeFeatureFromEditList(feature);
|
|
5829
|
+
this._collectionModify.remove(feature);
|
|
5830
|
+
});
|
|
4661
5831
|
});
|
|
4662
|
-
this._controlApplyDiscardChanges.on('apply', (
|
|
5832
|
+
this._controlApplyDiscardChanges.on('apply', async (evt) => {
|
|
4663
5833
|
showLoading();
|
|
4664
|
-
|
|
5834
|
+
const list = evt.features;
|
|
5835
|
+
const ok = await this._transactEditList(list);
|
|
5836
|
+
if (ok) {
|
|
5837
|
+
Array.from(list).forEach((feature) => {
|
|
5838
|
+
this._collectionModify.remove(feature);
|
|
5839
|
+
});
|
|
5840
|
+
}
|
|
4665
5841
|
});
|
|
4666
|
-
this._controlApplyDiscardChanges.on('delete', (
|
|
4667
|
-
this._deleteFeature(
|
|
5842
|
+
this._controlApplyDiscardChanges.on('delete', (evt) => {
|
|
5843
|
+
this._deleteFeature(evt.features, true);
|
|
4668
5844
|
});
|
|
4669
5845
|
this._map.addControl(this._controlApplyDiscardChanges);
|
|
4670
5846
|
}
|
|
@@ -4672,33 +5848,343 @@
|
|
|
4672
5848
|
* @private
|
|
4673
5849
|
*/
|
|
4674
5850
|
_editModeOff() {
|
|
5851
|
+
// Restore the tools toolbar and the layers widget together
|
|
5852
|
+
this._showControls();
|
|
4675
5853
|
activateMode(null);
|
|
5854
|
+
this._destroyExtendLineOverlays();
|
|
4676
5855
|
this._map.removeControl(this._controlApplyDiscardChanges);
|
|
5856
|
+
this._refreshSelectionInteractions();
|
|
5857
|
+
}
|
|
5858
|
+
/**
|
|
5859
|
+
* Get the first and last coordinate of every line component of a
|
|
5860
|
+
* LineString/MultiLineString geometry, including the line components
|
|
5861
|
+
* nested inside a GeometryCollection. Anything else returns an empty
|
|
5862
|
+
* array.
|
|
5863
|
+
*
|
|
5864
|
+
* @param geometry
|
|
5865
|
+
* @private
|
|
5866
|
+
*/
|
|
5867
|
+
_getLineEndPoints(geometry) {
|
|
5868
|
+
const endPoints = [];
|
|
5869
|
+
const appendLine = (line, collection) => {
|
|
5870
|
+
if (line instanceof LineString) {
|
|
5871
|
+
if (line.getCoordinates().length) {
|
|
5872
|
+
endPoints.push({
|
|
5873
|
+
first: line.getFirstCoordinate(),
|
|
5874
|
+
last: line.getLastCoordinate(),
|
|
5875
|
+
line,
|
|
5876
|
+
lineIndex: 0,
|
|
5877
|
+
collection
|
|
5878
|
+
});
|
|
5879
|
+
}
|
|
5880
|
+
return;
|
|
5881
|
+
}
|
|
5882
|
+
line.getCoordinates().forEach((coordinates, lineIndex) => {
|
|
5883
|
+
if (coordinates.length) {
|
|
5884
|
+
endPoints.push({
|
|
5885
|
+
first: coordinates[0],
|
|
5886
|
+
last: coordinates[coordinates.length - 1],
|
|
5887
|
+
line,
|
|
5888
|
+
lineIndex,
|
|
5889
|
+
collection
|
|
5890
|
+
});
|
|
5891
|
+
}
|
|
5892
|
+
});
|
|
5893
|
+
};
|
|
5894
|
+
// "getGeometries()" returns deep clones (see ol GeometryCollection),
|
|
5895
|
+
// but the extend/merge must work on the actual members.
|
|
5896
|
+
const collect = (geometry, collection) => {
|
|
5897
|
+
if (geometry instanceof LineString ||
|
|
5898
|
+
geometry instanceof MultiLineString) {
|
|
5899
|
+
appendLine(geometry, collection);
|
|
5900
|
+
}
|
|
5901
|
+
else if (geometry instanceof GeometryCollection) {
|
|
5902
|
+
geometry.getGeometriesArray().forEach((member) => {
|
|
5903
|
+
collect(member, geometry);
|
|
5904
|
+
});
|
|
5905
|
+
}
|
|
5906
|
+
};
|
|
5907
|
+
collect(geometry);
|
|
5908
|
+
return endPoints;
|
|
5909
|
+
}
|
|
5910
|
+
/**
|
|
5911
|
+
* Remove every "continue drawing" overlay and abort any active line
|
|
5912
|
+
* extension draw.
|
|
5913
|
+
*
|
|
5914
|
+
* @private
|
|
5915
|
+
*/
|
|
5916
|
+
_destroyExtendLineOverlays() {
|
|
5917
|
+
if (this._extendDraw) {
|
|
5918
|
+
this._finishExtendDraw();
|
|
5919
|
+
}
|
|
5920
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5921
|
+
this._map.removeOverlay(overlay);
|
|
5922
|
+
});
|
|
5923
|
+
this._extendOverlays = [];
|
|
5924
|
+
}
|
|
5925
|
+
/**
|
|
5926
|
+
* Keep the "continue drawing" overlays in sync with the current edit
|
|
5927
|
+
* selection: they are only shown while editing LineString/MultiLineString
|
|
5928
|
+
* features, and they are repositioned on the endpoints of each line
|
|
5929
|
+
* component.
|
|
5930
|
+
*
|
|
5931
|
+
* @private
|
|
5932
|
+
*/
|
|
5933
|
+
_syncExtendLineOverlays() {
|
|
5934
|
+
if (getMode() !== Modes.Edit || !this._collectionModify) {
|
|
5935
|
+
this._destroyExtendLineOverlays();
|
|
5936
|
+
return;
|
|
5937
|
+
}
|
|
5938
|
+
const items = this._collectionModify.getArray();
|
|
5939
|
+
// Remove overlays whose feature was deselected or lost its line geometry
|
|
5940
|
+
const removed = [];
|
|
5941
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5942
|
+
const stale = !items.includes(overlay.feature) ||
|
|
5943
|
+
overlay.component >=
|
|
5944
|
+
this._getLineEndPoints(overlay.feature.getGeometry())
|
|
5945
|
+
.length;
|
|
5946
|
+
if (stale) {
|
|
5947
|
+
this._map.removeOverlay(overlay);
|
|
5948
|
+
removed.push(overlay);
|
|
5949
|
+
}
|
|
5950
|
+
});
|
|
5951
|
+
this._extendOverlays = this._extendOverlays.filter((overlay) => !removed.includes(overlay));
|
|
5952
|
+
// Add overlays for new LineString/MultiLineString features
|
|
5953
|
+
items.forEach((feature) => {
|
|
5954
|
+
this._getLineEndPoints(feature.getGeometry()).forEach(({ first, last }, component) => {
|
|
5955
|
+
['start', 'end'].forEach((side) => {
|
|
5956
|
+
const exists = this._extendOverlays.some((overlay) => overlay.feature === feature &&
|
|
5957
|
+
overlay.component === component &&
|
|
5958
|
+
overlay.side === side);
|
|
5959
|
+
if (exists) {
|
|
5960
|
+
return;
|
|
5961
|
+
}
|
|
5962
|
+
const overlay = new ExtendLineOverlay(feature, side === 'start' ? first : last, side, component, () => this._startExtendLineDraw(feature, side, component));
|
|
5963
|
+
this._extendOverlays.push(overlay);
|
|
5964
|
+
this._map.addOverlay(overlay);
|
|
5965
|
+
});
|
|
5966
|
+
});
|
|
5967
|
+
});
|
|
5968
|
+
this._updateExtendOverlayPositions();
|
|
5969
|
+
}
|
|
5970
|
+
/**
|
|
5971
|
+
* Move the "continue drawing" overlays to the current position of their
|
|
5972
|
+
* line endpoints, e.g. after a vertex is dragged with the Modify
|
|
5973
|
+
* interaction.
|
|
5974
|
+
*
|
|
5975
|
+
* @private
|
|
5976
|
+
*/
|
|
5977
|
+
_updateExtendOverlayPositions() {
|
|
5978
|
+
this._extendOverlays.forEach((overlay) => {
|
|
5979
|
+
const point = this._getLineEndPoints(overlay.feature.getGeometry())[overlay.component];
|
|
5980
|
+
if (!point) {
|
|
5981
|
+
return;
|
|
5982
|
+
}
|
|
5983
|
+
overlay.setPosition(overlay.side === 'start' ? point.first : point.last);
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
/**
|
|
5987
|
+
* Start drawing a continuation of a line from one of its endpoints. The
|
|
5988
|
+
* sketch is anchored on the clicked vertex, so the next clicks append the
|
|
5989
|
+
* new points. A double click (or clicking back on the last point) ends
|
|
5990
|
+
* the drawing and merges the segment into the feature.
|
|
5991
|
+
*
|
|
5992
|
+
* @param feature
|
|
5993
|
+
* @param side 'start' prepends the new segment, 'end' appends it
|
|
5994
|
+
* @param component index of the clicked line component
|
|
5995
|
+
* @private
|
|
5996
|
+
*/
|
|
5997
|
+
_startExtendLineDraw(feature, side, component = 0) {
|
|
5998
|
+
var _a;
|
|
5999
|
+
if (this._extendDraw || getMode() !== Modes.Edit) {
|
|
6000
|
+
return;
|
|
6001
|
+
}
|
|
6002
|
+
const points = this._getLineEndPoints(feature.getGeometry());
|
|
6003
|
+
if (!points.length || component >= points.length) {
|
|
6004
|
+
return;
|
|
6005
|
+
}
|
|
6006
|
+
const anchor = side === 'start' ? points[component].first : points[component].last;
|
|
6007
|
+
// While extending, the Modify/Select interactions must not grab the clicks
|
|
6008
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(false);
|
|
6009
|
+
if (this._interactionSelectModify) {
|
|
6010
|
+
this._interactionSelectModify.setActive(false);
|
|
6011
|
+
}
|
|
6012
|
+
if (this._interactionWfsSelect) {
|
|
6013
|
+
this._interactionWfsSelect.setActive(false);
|
|
6014
|
+
}
|
|
6015
|
+
this._extendDraw = new Draw({
|
|
6016
|
+
type: GeometryType.LineString,
|
|
6017
|
+
style: (f) => styleFunction(f),
|
|
6018
|
+
stopClick: true // To prevent firing a map/wms click
|
|
6019
|
+
});
|
|
6020
|
+
this._extendDraw.on('drawend', (evt) => {
|
|
6021
|
+
this._mergeExtendLine(feature, side, evt.feature, component);
|
|
6022
|
+
});
|
|
6023
|
+
this._extendDraw.on('drawabort', () => {
|
|
6024
|
+
this._finishExtendDraw();
|
|
6025
|
+
});
|
|
6026
|
+
this._map.addInteraction(this._extendDraw);
|
|
6027
|
+
// Anchor the sketch on the clicked vertex: the user only has to digitize
|
|
6028
|
+
// the continuation points (this also dispatches the "drawstart" event)
|
|
6029
|
+
this._extendDraw.appendCoordinates([anchor]);
|
|
6030
|
+
}
|
|
6031
|
+
/**
|
|
6032
|
+
* Merge the just-drawn segment into the edited feature's line geometry and
|
|
6033
|
+
* restore the editing interactions.
|
|
6034
|
+
*
|
|
6035
|
+
* @param feature
|
|
6036
|
+
* @param side
|
|
6037
|
+
* @param sketch
|
|
6038
|
+
* @param component index of the extended line component
|
|
6039
|
+
* @private
|
|
6040
|
+
*/
|
|
6041
|
+
_mergeExtendLine(feature, side, sketch, component = 0) {
|
|
6042
|
+
const points = this._getLineEndPoints(feature.getGeometry());
|
|
6043
|
+
const point = points[component];
|
|
6044
|
+
if (!point) {
|
|
6045
|
+
this._finishExtendDraw();
|
|
6046
|
+
return;
|
|
6047
|
+
}
|
|
6048
|
+
const segment = sketch.getGeometry().getCoordinates();
|
|
6049
|
+
const { line, lineIndex } = point;
|
|
6050
|
+
if (line instanceof LineString) {
|
|
6051
|
+
const coords = line.getCoordinates();
|
|
6052
|
+
if (side === 'start') {
|
|
6053
|
+
line.setCoordinates([...segment.slice(1).reverse(), ...coords]);
|
|
6054
|
+
}
|
|
6055
|
+
else {
|
|
6056
|
+
line.setCoordinates([...coords, ...segment.slice(1)]);
|
|
6057
|
+
}
|
|
6058
|
+
}
|
|
6059
|
+
else if (line instanceof MultiLineString) {
|
|
6060
|
+
const coords = line.getCoordinates();
|
|
6061
|
+
if (side === 'start') {
|
|
6062
|
+
coords[lineIndex] = [
|
|
6063
|
+
...segment.slice(1).reverse(),
|
|
6064
|
+
...coords[lineIndex]
|
|
6065
|
+
];
|
|
6066
|
+
}
|
|
6067
|
+
else {
|
|
6068
|
+
coords[lineIndex] = [...coords[lineIndex], ...segment.slice(1)];
|
|
6069
|
+
}
|
|
6070
|
+
line.setCoordinates(coords);
|
|
6071
|
+
}
|
|
6072
|
+
// Only mark the feature as edited when the segment added new vertices
|
|
6073
|
+
if (segment.length > 1) {
|
|
6074
|
+
addFeatureToEditedList(feature);
|
|
6075
|
+
}
|
|
6076
|
+
this._finishExtendDraw();
|
|
6077
|
+
this._syncExtendLineOverlays();
|
|
4677
6078
|
}
|
|
4678
6079
|
/**
|
|
4679
|
-
* Remove
|
|
6080
|
+
* Remove the active line extension draw and restore the interactions that
|
|
6081
|
+
* were disabled while drawing.
|
|
6082
|
+
*
|
|
6083
|
+
* @private
|
|
6084
|
+
*/
|
|
6085
|
+
_finishExtendDraw() {
|
|
6086
|
+
var _a;
|
|
6087
|
+
if (this._extendDraw) {
|
|
6088
|
+
this._map.removeInteraction(this._extendDraw);
|
|
6089
|
+
this._extendDraw = null;
|
|
6090
|
+
}
|
|
6091
|
+
if (getMode() !== Modes.Edit) {
|
|
6092
|
+
return;
|
|
6093
|
+
}
|
|
6094
|
+
(_a = this._interactionModify) === null || _a === void 0 ? void 0 : _a.setActive(true);
|
|
6095
|
+
if (this._interactionSelectModify) {
|
|
6096
|
+
this._interactionSelectModify.setActive(true);
|
|
6097
|
+
}
|
|
6098
|
+
if (this._interactionWfsSelect) {
|
|
6099
|
+
this._interactionWfsSelect.setActive(true);
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
/**
|
|
6103
|
+
* Send all the given features in a single WFS-T Update transaction per
|
|
6104
|
+
* layer, so a multi selection is saved with only one request. The "edited"
|
|
6105
|
+
* flag is cleared before transacting, so a deselect fired while the
|
|
6106
|
+
* request is in flight does not trigger a duplicate transaction.
|
|
6107
|
+
*
|
|
6108
|
+
* @param list
|
|
6109
|
+
* @returns false when there is nothing to transact or the transaction fails
|
|
6110
|
+
* @private
|
|
6111
|
+
*/
|
|
6112
|
+
async _transactEditList(list) {
|
|
6113
|
+
if (!list.length) {
|
|
6114
|
+
return true;
|
|
6115
|
+
}
|
|
6116
|
+
const byLayer = {};
|
|
6117
|
+
list.forEach((feature) => {
|
|
6118
|
+
const layerName = feature.get('_layerName_');
|
|
6119
|
+
if (!byLayer[layerName]) {
|
|
6120
|
+
byLayer[layerName] = [];
|
|
6121
|
+
}
|
|
6122
|
+
byLayer[layerName].push(feature);
|
|
6123
|
+
});
|
|
6124
|
+
for (const layerName of Object.keys(byLayer)) {
|
|
6125
|
+
const layer = this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName);
|
|
6126
|
+
if (!layer) {
|
|
6127
|
+
continue;
|
|
6128
|
+
}
|
|
6129
|
+
byLayer[layerName].forEach((feature) => {
|
|
6130
|
+
removeFeatureFromEditList(feature);
|
|
6131
|
+
});
|
|
6132
|
+
let ok;
|
|
6133
|
+
try {
|
|
6134
|
+
ok = await layer.transactFeatures(TransactionType.Update, byLayer[layerName]);
|
|
6135
|
+
}
|
|
6136
|
+
catch (err) {
|
|
6137
|
+
return false;
|
|
6138
|
+
}
|
|
6139
|
+
if (ok === false) {
|
|
6140
|
+
return false;
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
return true;
|
|
6144
|
+
}
|
|
6145
|
+
/**
|
|
6146
|
+
* Remove features from the edit Layer and from the Geoserver
|
|
4680
6147
|
*
|
|
4681
6148
|
* @param feature
|
|
6149
|
+
* @param askToConfirm
|
|
4682
6150
|
* @private
|
|
4683
6151
|
*/
|
|
4684
|
-
_deleteFeature(feature,
|
|
6152
|
+
_deleteFeature(feature, askToConfirm) {
|
|
4685
6153
|
const deleteEl = () => {
|
|
6154
|
+
var _a;
|
|
4686
6155
|
const features = Array.isArray(feature) ? feature : [feature];
|
|
4687
6156
|
features.forEach((feature) => {
|
|
4688
6157
|
feature.set('_delete_', true, true);
|
|
4689
6158
|
getEditLayer().getSource().removeFeature(feature);
|
|
4690
6159
|
});
|
|
4691
6160
|
this._collectionModify.clear();
|
|
4692
|
-
|
|
4693
|
-
|
|
6161
|
+
this._editModeOff();
|
|
6162
|
+
this._syncEditOverlays();
|
|
6163
|
+
const layerName = (_a = features[0]) === null || _a === void 0 ? void 0 : _a.get('_layerName_');
|
|
6164
|
+
const layer = layerName
|
|
6165
|
+
? this._options.layers.find((layer) => layer.get(BaseLayerProperty.NAME) === layerName)
|
|
6166
|
+
: null;
|
|
4694
6167
|
if (layer instanceof WfsLayer) {
|
|
4695
|
-
|
|
6168
|
+
features.forEach((feature) => {
|
|
6169
|
+
this._interactionWfsSelect.getFeatures().remove(feature);
|
|
6170
|
+
});
|
|
4696
6171
|
}
|
|
4697
6172
|
};
|
|
4698
|
-
if (
|
|
4699
|
-
const
|
|
4700
|
-
|
|
4701
|
-
|
|
6173
|
+
if (askToConfirm) {
|
|
6174
|
+
const features = Array.isArray(feature) ? feature : [feature];
|
|
6175
|
+
const message = features.length > 1
|
|
6176
|
+
? I18N_('confirmDeleteElements', features.length)
|
|
6177
|
+
: I18N.labels.confirmDelete;
|
|
6178
|
+
const confirmModal = Modal.confirm(message, Object.assign(Object.assign({}, this._options.modal), { footer: `
|
|
6179
|
+
<button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">
|
|
6180
|
+
${I18N.labels.cancel}
|
|
6181
|
+
</button>
|
|
6182
|
+
<button type="button" class="btn btn-sm btn-danger" data-action="delete" data-dismiss="modal">
|
|
6183
|
+
${I18N.labels.delete}
|
|
6184
|
+
</button>
|
|
6185
|
+
` }));
|
|
6186
|
+
confirmModal.show().once('dismiss', function (modal, event) {
|
|
6187
|
+
if (event.target.dataset.action == 'delete') {
|
|
4702
6188
|
deleteEl();
|
|
4703
6189
|
}
|
|
4704
6190
|
});
|
|
@@ -4716,33 +6202,102 @@
|
|
|
4716
6202
|
* @private
|
|
4717
6203
|
*/
|
|
4718
6204
|
_addFeatureToEditMode(feature, coordinate = null, layerName = null) {
|
|
4719
|
-
// For now, only allow one element at time
|
|
4720
|
-
// @TODO: allow edit multiples elements
|
|
4721
|
-
if (this._collectionModify.getLength())
|
|
4722
|
-
return;
|
|
4723
6205
|
if (layerName) {
|
|
4724
6206
|
// Store the layer information inside the feature
|
|
4725
6207
|
feature.set('_layerName_', layerName);
|
|
4726
6208
|
}
|
|
6209
|
+
if (coordinate) {
|
|
6210
|
+
// Keep the click coordinate to position the overlay helper
|
|
6211
|
+
feature.set('_editOverlayCoord_', coordinate);
|
|
6212
|
+
}
|
|
4727
6213
|
const props = feature ? feature.getProperties() : '';
|
|
4728
6214
|
if (props) {
|
|
4729
6215
|
if (feature.getGeometry()) {
|
|
4730
6216
|
getEditLayer().getSource().addFeature(feature);
|
|
4731
6217
|
this._collectionModify.push(feature);
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
|
|
4735
|
-
|
|
4736
|
-
|
|
4737
|
-
|
|
4738
|
-
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
|
|
4745
|
-
|
|
6218
|
+
this._syncEditOverlays();
|
|
6219
|
+
}
|
|
6220
|
+
}
|
|
6221
|
+
}
|
|
6222
|
+
/**
|
|
6223
|
+
* Lock the whole current selection with a single LockFeature request per
|
|
6224
|
+
* layer, so the transaction LockId covers all the selected features
|
|
6225
|
+
*
|
|
6226
|
+
* @private
|
|
6227
|
+
*/
|
|
6228
|
+
async _lockSelectedFeatures() {
|
|
6229
|
+
const byLayer = {};
|
|
6230
|
+
this._collectionModify.getArray().forEach((selected) => {
|
|
6231
|
+
const layerName = selected.get('_layerName_');
|
|
6232
|
+
if (!layerName) {
|
|
6233
|
+
return;
|
|
6234
|
+
}
|
|
6235
|
+
(byLayer[layerName] || (byLayer[layerName] = [])).push(selected.getId());
|
|
6236
|
+
});
|
|
6237
|
+
const locks = [];
|
|
6238
|
+
Object.keys(byLayer).forEach((layerName) => {
|
|
6239
|
+
const layer = getStoredLayer(layerName);
|
|
6240
|
+
if (layer) {
|
|
6241
|
+
locks.push(layer.maybeLockFeature(byLayer[layerName]));
|
|
6242
|
+
}
|
|
6243
|
+
});
|
|
6244
|
+
if (locks.length) {
|
|
6245
|
+
await Promise.all(locks);
|
|
6246
|
+
}
|
|
6247
|
+
}
|
|
6248
|
+
/**
|
|
6249
|
+
* Create an EditOverlay helper and attach the edit listeners
|
|
6250
|
+
*
|
|
6251
|
+
* @param feature
|
|
6252
|
+
* @param coordinate
|
|
6253
|
+
* @param id
|
|
6254
|
+
* @private
|
|
6255
|
+
*/
|
|
6256
|
+
_createEditOverlay(feature = null, coordinate = null, id = null) {
|
|
6257
|
+
coordinate =
|
|
6258
|
+
coordinate || (feature ? feature.get('_editOverlayCoord_') : null);
|
|
6259
|
+
const overlay = new EditOverlay(feature, coordinate, id);
|
|
6260
|
+
// @ts-expect-error
|
|
6261
|
+
overlay.on('editFields', () => {
|
|
6262
|
+
this._editFields.show(this._collectionModify.getArray());
|
|
6263
|
+
});
|
|
6264
|
+
// @ts-expect-error
|
|
6265
|
+
overlay.on('editGeom', () => {
|
|
6266
|
+
this._editModeOn(this._collectionModify.getArray());
|
|
6267
|
+
});
|
|
6268
|
+
this._map.addOverlay(overlay);
|
|
6269
|
+
}
|
|
6270
|
+
/**
|
|
6271
|
+
* Sync the edit overlay helpers with the current selection: a single
|
|
6272
|
+
* per-feature overlay while a feature is selected, or a unique overlay
|
|
6273
|
+
* centered between all the selected features when editing several at once
|
|
6274
|
+
*
|
|
6275
|
+
* @private
|
|
6276
|
+
*/
|
|
6277
|
+
_syncEditOverlays() {
|
|
6278
|
+
if (getMode() === Modes.Edit) {
|
|
6279
|
+
return;
|
|
6280
|
+
}
|
|
6281
|
+
const multiOverlay = this._map.getOverlayById(this._multiOverlayId);
|
|
6282
|
+
if (multiOverlay) {
|
|
6283
|
+
this._map.removeOverlay(multiOverlay);
|
|
6284
|
+
}
|
|
6285
|
+
const items = this._collectionModify.getArray();
|
|
6286
|
+
if (items.length > 1) {
|
|
6287
|
+
items.forEach((feature) => {
|
|
6288
|
+
this._removeOverlayHelper(feature);
|
|
6289
|
+
});
|
|
6290
|
+
const extent = extent_js.createEmpty();
|
|
6291
|
+
items.forEach((feature) => {
|
|
6292
|
+
extent_js.extend(extent, feature.getGeometry().getExtent());
|
|
6293
|
+
});
|
|
6294
|
+
this._createEditOverlay(null, extent_js.getCenter(extent), this._multiOverlayId);
|
|
6295
|
+
}
|
|
6296
|
+
else if (items.length === 1) {
|
|
6297
|
+
const feature = items[0];
|
|
6298
|
+
const featureId = feature.getId();
|
|
6299
|
+
if (featureId && !this._map.getOverlayById(featureId)) {
|
|
6300
|
+
this._createEditOverlay(feature);
|
|
4746
6301
|
}
|
|
4747
6302
|
}
|
|
4748
6303
|
}
|
|
@@ -4789,15 +6344,20 @@
|
|
|
4789
6344
|
if (!layer.getVisible()) {
|
|
4790
6345
|
return;
|
|
4791
6346
|
}
|
|
6347
|
+
// Clear hover style while drawing
|
|
6348
|
+
this._clearHoverState();
|
|
4792
6349
|
activateDrawButton();
|
|
6350
|
+
deactivateSelectModeButton();
|
|
4793
6351
|
this._viewport.classList.add('draw-mode');
|
|
4794
6352
|
addDrawInteraction(layer);
|
|
4795
6353
|
}
|
|
4796
6354
|
else {
|
|
4797
6355
|
this._map.removeInteraction(this._interactionDraw);
|
|
4798
6356
|
this._viewport.classList.remove('draw-mode');
|
|
6357
|
+
activateSelectModeButton(getSelectionMode());
|
|
4799
6358
|
}
|
|
4800
6359
|
activateMode(layer ? Modes.Draw : null);
|
|
6360
|
+
this._refreshSelectionInteractions();
|
|
4801
6361
|
}
|
|
4802
6362
|
/**
|
|
4803
6363
|
* Activate/desactivate the edit mode
|
|
@@ -4813,6 +6373,8 @@
|
|
|
4813
6373
|
else {
|
|
4814
6374
|
// Deselct features
|
|
4815
6375
|
this._collectionModify.clear();
|
|
6376
|
+
// Clear hover state when leaving the edit mode
|
|
6377
|
+
this._clearHoverState();
|
|
4816
6378
|
}
|
|
4817
6379
|
if (this._interactionSelectModify) {
|
|
4818
6380
|
this._interactionSelectModify.setActive(bool);
|
|
@@ -4820,6 +6382,61 @@
|
|
|
4820
6382
|
this._interactionModify.setActive(bool);
|
|
4821
6383
|
if (this._interactionWfsSelect)
|
|
4822
6384
|
this._interactionWfsSelect.setActive(bool);
|
|
6385
|
+
this._refreshSelectionInteractions();
|
|
6386
|
+
}
|
|
6387
|
+
/**
|
|
6388
|
+
* Reset the cursor and remove the hover style of the last hovered feature
|
|
6389
|
+
* @private
|
|
6390
|
+
*/
|
|
6391
|
+
_clearHoverState() {
|
|
6392
|
+
if (this._viewport) {
|
|
6393
|
+
this._viewport.style.cursor = '';
|
|
6394
|
+
}
|
|
6395
|
+
if (this._hoveredFeature) {
|
|
6396
|
+
this._hoveredFeature.setStyle(undefined);
|
|
6397
|
+
this._hoveredFeature = null;
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
/**
|
|
6401
|
+
* Change the map cursor while in edit mode: `move` when the pointer is
|
|
6402
|
+
* over an editable vertex and `copy` when it is over a segment (where a
|
|
6403
|
+
* new vertex can be inserted), mirroring the Modify interaction hit
|
|
6404
|
+
* tolerance.
|
|
6405
|
+
*
|
|
6406
|
+
* @param evt
|
|
6407
|
+
* @private
|
|
6408
|
+
*/
|
|
6409
|
+
_updateEditCursor(evt) {
|
|
6410
|
+
if (!this._collectionModify) {
|
|
6411
|
+
return;
|
|
6412
|
+
}
|
|
6413
|
+
const tolerance = 12;
|
|
6414
|
+
const toleranceSq = tolerance * tolerance;
|
|
6415
|
+
const pixel = evt.pixel;
|
|
6416
|
+
for (const feature of this._collectionModify.getArray()) {
|
|
6417
|
+
const vertices = getFeatureVertices(feature);
|
|
6418
|
+
const coordinates = vertices ? vertices.getCoordinates() : [];
|
|
6419
|
+
if (!coordinates.length) {
|
|
6420
|
+
continue;
|
|
6421
|
+
}
|
|
6422
|
+
// Hovering an existing vertex (draggable handle)
|
|
6423
|
+
for (const coordinate of coordinates) {
|
|
6424
|
+
const vertexPixel = this._map.getPixelFromCoordinate(coordinate);
|
|
6425
|
+
if (coordinate_js.squaredDistance(vertexPixel, pixel) <= toleranceSq) {
|
|
6426
|
+
this._viewport.style.cursor = 'move';
|
|
6427
|
+
return;
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
// Hovering a segment (allows inserting a new vertex)
|
|
6431
|
+
for (let i = 0; i < coordinates.length - 1; i++) {
|
|
6432
|
+
const start = this._map.getPixelFromCoordinate(coordinates[i]);
|
|
6433
|
+
const end = this._map.getPixelFromCoordinate(coordinates[i + 1]);
|
|
6434
|
+
if (coordinate_js.squaredDistanceToSegment(pixel, [start, end]) <= toleranceSq) {
|
|
6435
|
+
this._viewport.style.cursor = 'copy';
|
|
6436
|
+
return;
|
|
6437
|
+
}
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
4823
6440
|
}
|
|
4824
6441
|
/**
|
|
4825
6442
|
* Remove the overlay helper atttached to a specify feature
|