qwc2 2025.10.24 → 2025.10.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/components/FeatureAttributesWindow.js +3 -2
  2. package/components/NumericInputWindow.js +30 -22
  3. package/components/map/layers/MVTLayer.js +3 -0
  4. package/components/map/layers/VectorLayer.js +66 -65
  5. package/components/map3d/drawtool/EditTool3D.js +1 -1
  6. package/components/style/NumericInputWindow.css +19 -4
  7. package/components/widgets/TextInput.js +4 -2
  8. package/package.json +1 -1
  9. package/plugins/HeightProfile.js +3 -1
  10. package/plugins/Redlining.js +7 -1
  11. package/plugins/map/RedliningSupport.js +279 -219
  12. package/reducers/redlining.js +5 -3
  13. package/scripts/updateTranslations.js +1 -1
  14. package/static/translations/bg-BG.json +3 -3
  15. package/static/translations/ca-ES.json +3 -3
  16. package/static/translations/cs-CZ.json +3 -3
  17. package/static/translations/de-CH.json +3 -3
  18. package/static/translations/de-DE.json +3 -3
  19. package/static/translations/en-US.json +3 -3
  20. package/static/translations/es-ES.json +3 -3
  21. package/static/translations/fi-FI.json +3 -3
  22. package/static/translations/fr-FR.json +3 -3
  23. package/static/translations/hu-HU.json +3 -3
  24. package/static/translations/it-IT.json +3 -3
  25. package/static/translations/ja-JP.json +3 -3
  26. package/static/translations/nl-NL.json +3 -3
  27. package/static/translations/no-NO.json +3 -3
  28. package/static/translations/pl-PL.json +3 -3
  29. package/static/translations/pt-BR.json +3 -3
  30. package/static/translations/pt-PT.json +3 -3
  31. package/static/translations/ro-RO.json +3 -3
  32. package/static/translations/ru-RU.json +3 -3
  33. package/static/translations/sv-SE.json +3 -3
  34. package/static/translations/tr-TR.json +3 -3
  35. package/static/translations/tsconfig.json +3 -3
  36. package/static/translations/uk-UA.json +3 -3
  37. package/utils/EditingInterface.js +15 -4
  38. package/utils/LayerUtils.js +16 -6
@@ -31,6 +31,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
31
31
  import React from 'react';
32
32
  import { connect } from 'react-redux';
33
33
  import FileSaver from 'file-saver';
34
+ import isEmpty from 'lodash.isempty';
34
35
  import Mousetrap from 'mousetrap';
35
36
  import ol from 'openlayers';
36
37
  import PropTypes from 'prop-types';
@@ -140,16 +141,16 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
140
141
  });
141
142
  _defineProperty(_this, "updateCurrentFeature", function (feature) {
142
143
  var deletedKeys = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
143
- if (_this.currentFeature && _this.props.redlining.selectedFeature) {
144
+ if (_this.selectedFeatures.length === 1 && _this.props.redlining.selectedFeature) {
144
145
  if (feature.circleParams) {
145
146
  var circleParams = feature.circleParams;
146
- _this.currentFeature.setGeometry(new ol.geom.Circle(circleParams.center, circleParams.radius));
147
+ _this.selectedFeatures[0].setGeometry(new ol.geom.Circle(circleParams.center, circleParams.radius));
147
148
  } else {
148
- _this.currentFeature.getGeometry().setCoordinates(feature.geometry.coordinates);
149
+ _this.selectedFeatures[0].getGeometry().setCoordinates(feature.geometry.coordinates);
149
150
  }
150
- _this.currentFeature.setProperties(feature.properties, true);
151
+ _this.selectedFeatures[0].setProperties(feature.properties, true);
151
152
  deletedKeys.forEach(function (key) {
152
- _this.currentFeature.unset(key);
153
+ _this.selectedFeatures[0].unset(key);
153
154
  });
154
155
  _this.props.changeRedliningState({
155
156
  selectedFeature: feature,
@@ -174,16 +175,47 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
174
175
  var isText = feature.get("shape") === "Text";
175
176
  return _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, isText ? "textOutlineColor" : "borderColor", styleOptions.strokeColor), "strokeDash", styleOptions.strokeDash), "size", (styleOptions.strokeWidth - 1) * 2), isText ? "textFillColor" : "fillColor", styleOptions.fillColor), "text", label), "headmarker", styleOptions.headmarker), "tailmarker", styleOptions.tailmarker);
176
177
  });
177
- _defineProperty(_this, "updateFeatureStyle", function (styleProps) {
178
- var isText = _this.currentFeature.get("shape") === "Text";
178
+ _defineProperty(_this, "updateFeatureStyle", function (feature) {
179
+ _this.blockOnChange = true;
180
+ var styleProps = _this.props.redlining.style;
181
+ var isText = feature.get("shape") === "Text";
179
182
  var styleName = isText ? "text" : "default";
180
183
  var opts = _this.styleOptions(styleProps, isText);
181
- _this.blockOnChange = true;
182
- _this.currentFeature.set('label', styleProps.text);
183
- _this.currentFeature.set('styleName', styleName);
184
- _this.currentFeature.set('styleOptions', opts);
184
+ if (!feature.get('measurements')) {
185
+ feature.set('label', styleProps.text);
186
+ }
187
+ feature.set('styleName', styleName);
188
+ feature.set('styleOptions', opts);
185
189
  _this.blockOnChange = false;
186
190
  });
191
+ _defineProperty(_this, "toggleFeatureMeasurements", function (feature) {
192
+ if (_this.props.redlining.measurements) {
193
+ var settings = {
194
+ displayCrs: _this.props.displayCrs,
195
+ lenUnit: _this.props.redlining.lenUnit,
196
+ areaUnit: _this.props.redlining.areaUnit
197
+ };
198
+ MeasureUtils.updateFeatureMeasurements(feature, feature.get('shape'), _this.props.mapCrs, settings);
199
+ } else {
200
+ feature.set('measurements', undefined);
201
+ feature.set('segment_labels', undefined);
202
+ feature.set('label', '');
203
+ }
204
+ });
205
+ _defineProperty(_this, "updateMeasurements", function (ev) {
206
+ if (_this.blockOnChange) {
207
+ return;
208
+ }
209
+ var feature = ev.target;
210
+ if (feature.get('measurements')) {
211
+ var settings = {
212
+ displayCrs: _this.props.displayCrs,
213
+ lenUnit: _this.props.redlining.lenUnit,
214
+ areaUnit: _this.props.redlining.areaUnit
215
+ };
216
+ MeasureUtils.updateFeatureMeasurements(feature, feature.get('shape'), _this.props.mapCrs, settings);
217
+ }
218
+ });
187
219
  _defineProperty(_this, "styleFunction", function (feature) {
188
220
  var styleOptions = feature.get("styleOptions");
189
221
  var styleName = feature.get("styleName");
@@ -199,86 +231,11 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
199
231
  }
200
232
  return styles;
201
233
  });
202
- _defineProperty(_this, "setCurrentFeature", function (feature) {
203
- var _featureObj$shape;
204
- _this.currentFeature = feature;
205
- _this.currentFeature.setStyle(_this.styleFunction);
206
- var circleParams = _this.currentFeature.get('circleParams');
207
- if (circleParams) {
208
- _this.currentFeature.setGeometry(new ol.geom.Circle(circleParams.center, circleParams.radius));
209
- }
210
- var measurements = _this.currentFeature.get('measurements');
211
- var featureObj = _this.currentFeatureObject();
212
- var newRedliningState = {
213
- style: _this.styleProps(_this.currentFeature),
214
- measurements: !!_this.currentFeature.get('measurements'),
215
- selectedFeature: featureObj,
216
- geomType: (_featureObj$shape = featureObj === null || featureObj === void 0 ? void 0 : featureObj.shape) !== null && _featureObj$shape !== void 0 ? _featureObj$shape : _this.props.redlining.geomType
217
- };
218
- if (measurements) {
219
- newRedliningState.lenUnit = measurements.lenUnit;
220
- newRedliningState.areaUnit = measurements.areaUnit;
221
- }
222
- _this.props.changeRedliningState(newRedliningState);
223
- _this.currentFeature.on('change', _this.updateMeasurements);
224
- });
225
- _defineProperty(_this, "addDrawInteraction", function () {
226
- var geomTypeConfig = GeomTypeConfig[_this.props.redlining.geomType];
227
- if (!geomTypeConfig) {
228
- return;
229
- }
230
- var isFreeHand = _this.props.redlining.freehand;
231
- var drawInteraction = geomTypeConfig.drawInteraction({
232
- stopClick: true,
233
- condition: function condition(event) {
234
- return event.originalEvent.buttons === 1;
235
- },
236
- style: function style() {
237
- return _this.picking ? [] : FeatureStyles.sketchInteraction();
238
- },
239
- freehand: isFreeHand
240
- });
241
- drawInteraction.on('drawstart', function (evt) {
242
- if (_this.picking && _this.props.redlining.drawMultiple === false) {
243
- return;
244
- }
245
- _this.leaveTemporaryEditMode();
246
- _this.currentFeature = evt.feature;
247
- _this.currentFeature.setId(uuidv4());
248
- _this.currentFeature.set('shape', _this.props.redlining.geomType);
249
- _this.currentFeature.setStyle(_this.styleFunction);
250
- _this.updateFeatureStyle(_this.props.redlining.style);
251
- _this.currentFeature.on('change', _this.updateMeasurements);
252
- }, _this);
253
- drawInteraction.on('drawend', function () {
254
- var featureId = _this.currentFeature.getId();
255
- _this.commitCurrentFeature(_this.props.redlining, true);
256
- _this.enterTemporaryEditMode(featureId, _this.props.redlining.layer, geomTypeConfig.editTool);
257
- }, _this);
258
- _this.props.map.addInteraction(drawInteraction);
259
- _this.interactions.push(drawInteraction);
260
- _this.setState({
261
- showRecordLocation: geomTypeConfig.showRecordLocation
262
- });
263
- });
264
- _defineProperty(_this, "updateMeasurements", function () {
265
- if (_this.blockOnChange || !_this.currentFeature) {
266
- return;
267
- }
268
- var feature = _this.currentFeature;
269
- var hadMeasurements = !!feature.get('measurements');
270
- if (_this.props.redlining.measurements) {
271
- var settings = {
272
- displayCrs: _this.props.displayCrs,
273
- lenUnit: _this.props.redlining.lenUnit,
274
- areaUnit: _this.props.redlining.areaUnit
275
- };
276
- MeasureUtils.updateFeatureMeasurements(feature, feature.get('shape'), _this.props.mapCrs, settings);
277
- } else if (hadMeasurements) {
278
- feature.set('measurements', undefined);
279
- feature.set('segment_labels', undefined);
280
- feature.set('label', '');
281
- }
234
+ _defineProperty(_this, "searchRedliningLayer", function (layerId) {
235
+ var _this$props$map$getLa;
236
+ return (_this$props$map$getLa = _this.props.map.getLayers().getArray().find(function (l) {
237
+ return l.get('id') === layerId;
238
+ })) !== null && _this$props$map$getLa !== void 0 ? _this$props$map$getLa : null;
282
239
  });
283
240
  _defineProperty(_this, "waitForFeatureAndLayer", function (layerId, featureId, callback) {
284
241
  var redliningLayer = _this.searchRedliningLayer(layerId);
@@ -311,22 +268,59 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
311
268
  callback(redliningLayer, null);
312
269
  }
313
270
  });
271
+ _defineProperty(_this, "addDrawInteraction", function () {
272
+ var geomTypeConfig = GeomTypeConfig[_this.props.redlining.geomType];
273
+ if (!geomTypeConfig) {
274
+ return;
275
+ }
276
+ var isFreeHand = _this.props.redlining.freehand;
277
+ var drawInteraction = geomTypeConfig.drawInteraction({
278
+ stopClick: true,
279
+ condition: function condition(event) {
280
+ return event.originalEvent.buttons === 1;
281
+ },
282
+ style: function style() {
283
+ return _this.picking ? [] : FeatureStyles.sketchInteraction();
284
+ },
285
+ freehand: isFreeHand
286
+ });
287
+ drawInteraction.on('drawstart', function (ev) {
288
+ if (_this.picking && _this.props.redlining.drawMultiple === false) {
289
+ return;
290
+ }
291
+ _this.leaveTemporaryEditMode();
292
+ ev.feature.setId(uuidv4());
293
+ ev.feature.set('shape', _this.props.redlining.geomType);
294
+ _this.updateFeatureStyle(ev.feature);
295
+ _this.toggleFeatureMeasurements(ev.feature);
296
+ _this.selectFeatures([ev.feature]);
297
+ }, _this);
298
+ drawInteraction.on('drawend', function (ev) {
299
+ _this.commitFeatures([ev.feature], _this.props.redlining, true);
300
+ _this.enterTemporaryEditMode(ev.feature.getId(), _this.props.redlining.layer, geomTypeConfig.editTool);
301
+ }, _this);
302
+ _this.props.map.addInteraction(drawInteraction);
303
+ _this.interactions.push(drawInteraction);
304
+ _this.setState({
305
+ showRecordLocation: geomTypeConfig.showRecordLocation
306
+ });
307
+ });
314
308
  _defineProperty(_this, "enterTemporaryEditMode", function (featureId, layerId, editTool) {
315
309
  _this.waitForFeatureAndLayer(layerId, featureId, function (redliningLayer, feature) {
316
310
  if (!feature) {
317
311
  return;
318
312
  }
319
- _this.setCurrentFeature(feature);
313
+ _this.selectFeatures([feature]);
320
314
  if (editTool === 'Transform') {
321
- _this.setupTransformInteraction([_this.currentFeature]);
315
+ _this.setupTransformInteraction(redliningLayer, _this.selectedFeatures);
322
316
  } else {
323
- _this.setupModifyInteraction([_this.currentFeature]);
317
+ _this.setupModifyInteraction(_this.selectedFeatures);
324
318
  }
325
319
  _this.picking = true;
326
320
  });
327
321
  });
328
322
  _defineProperty(_this, "leaveTemporaryEditMode", function () {
329
- _this.commitCurrentFeature(_this.props.redlining);
323
+ _this.commitFeatures(_this.selectedFeatures, _this.props.redlining);
330
324
  if (_this.picking) {
331
325
  // Remove modify interactions
332
326
  _this.props.map.removeInteraction(_this.interactions.pop());
@@ -344,10 +338,10 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
344
338
  });
345
339
  var currentEditInteraction = null;
346
340
  selectInteraction.on('select', function (evt) {
347
- if (evt.selected.length === 1 && evt.selected[0] === _this.currentFeature) {
341
+ if (evt.selected.length === 1 && _this.selectedFeatures.includes(evt.selected[0])) {
348
342
  return;
349
343
  }
350
- _this.commitCurrentFeature(_this.props.redlining);
344
+ _this.commitFeatures(_this.selectedFeatures, _this.props.redlining);
351
345
  if (currentEditInteraction) {
352
346
  _this.props.map.removeInteraction(currentEditInteraction);
353
347
  _this.interactions = _this.interactions.filter(function (i) {
@@ -356,14 +350,14 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
356
350
  currentEditInteraction = null;
357
351
  }
358
352
  if (evt.selected.length === 1) {
359
- _this.setCurrentFeature(evt.selected[0]);
360
- var geomTypeConfig = GeomTypeConfig[_this.currentFeature.get('shape')];
353
+ _this.selectFeatures(evt.selected);
354
+ var geomTypeConfig = GeomTypeConfig[evt.selected[0].get('shape')];
361
355
  if (geomTypeConfig && geomTypeConfig.editTool === 'Transform') {
362
- currentEditInteraction = _this.setupTransformInteraction([_this.currentFeature]);
356
+ currentEditInteraction = _this.setupTransformInteraction(redliningLayer, [evt.selected[0]]);
363
357
  currentEditInteraction.on('select', function (ev) {
364
358
  // Clear selection when selecting a different feature, and let the parent select interaction deal with the new feature
365
- if (_this.currentFeature && ev.feature !== _this.currentFeature) {
366
- _this.commitCurrentFeature(_this.props.redlining);
359
+ if (!isEmpty(_this.selectedFeatures) && !_this.selectedFeatures.includes(ev.feature)) {
360
+ _this.commitFeatures(_this.selectedFeatures, _this.props.redlining);
367
361
  currentEditInteraction.setSelection(new ol.Collection());
368
362
  }
369
363
  });
@@ -384,26 +378,22 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
384
378
  if (!redliningLayer) {
385
379
  return;
386
380
  }
387
- var transformInteraction = _this.setupTransformInteraction();
381
+ var transformInteraction = _this.setupTransformInteraction(redliningLayer, [], true);
388
382
  transformInteraction.on('select', function (evt) {
389
- if (evt.feature === _this.currentFeature) {
390
- return;
391
- }
392
- _this.commitCurrentFeature(_this.props.redlining);
393
- if (evt.feature) {
394
- _this.setCurrentFeature(evt.feature);
395
- }
383
+ var added = evt.features.getArray().filter(function (x) {
384
+ return !_this.selectedFeatures.includes(x);
385
+ });
386
+ var removed = _this.selectedFeatures.filter(function (x) {
387
+ return !evt.features.getArray().includes(x);
388
+ });
389
+ _this.selectFeatures(added);
390
+ _this.commitFeatures(removed, _this.props.redlining);
396
391
  });
397
392
  _this.picking = true;
398
393
  });
399
- _defineProperty(_this, "addPickDrawInteraction", function () {
400
- _this.waitForFeatureAndLayer(_this.props.redlining.layer, null, function () {
401
- return _this.addPickInteraction();
402
- });
403
- });
404
394
  _defineProperty(_this, "maybeEnterTemporaryDrawMode", function (ev) {
405
395
  var redliningLayer = _this.searchRedliningLayer(_this.props.redlining.layer);
406
- if (_this.currentFeature || !_this.props.redlining.drawMultiple && redliningLayer.getSource().getFeatures().length > 0) {
396
+ if (_this.selectedFeatures.length || !_this.props.redlining.drawMultiple && redliningLayer.getSource().getFeatures().length > 0) {
407
397
  return;
408
398
  }
409
399
  var featureHit = false;
@@ -433,16 +423,15 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
433
423
  freehand: isFreeHand
434
424
  });
435
425
  drawInteraction.on('drawstart', function (evt) {
436
- _this.currentFeature = evt.feature;
437
- _this.currentFeature.setId(uuidv4());
438
- _this.currentFeature.set('shape', _this.props.redlining.geomType);
439
- _this.currentFeature.setStyle(_this.styleFunction);
440
- _this.updateFeatureStyle(_this.props.redlining.style);
441
- _this.currentFeature.on('change', _this.updateMeasurements);
426
+ evt.feature.setId(uuidv4());
427
+ evt.feature.set('shape', _this.props.redlining.geomType);
428
+ _this.updateFeatureStyle(evt.feature);
429
+ _this.toggleFeatureMeasurements(ev.feature);
430
+ _this.selectFeatures([evt.feature]);
442
431
  }, _this);
443
432
  drawInteraction.on('drawend', function () {
444
433
  // Draw end
445
- _this.commitCurrentFeature(_this.props.redlining, true);
434
+ _this.commitFeatures(_this.selectFeatures, _this.props.redlining, true);
446
435
  _this.reset(_this.props.redlining);
447
436
  // Ughh... Apparently we need to wait 250ms for the 'singleclick' event processing to finish to avoid pick interactions picking up the current event
448
437
  setTimeout(function () {
@@ -478,18 +467,38 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
478
467
  _this.interactions.push(modifyInteraction);
479
468
  return modifyInteraction;
480
469
  });
481
- _defineProperty(_this, "setupTransformInteraction", function () {
482
- var selectedFeatures = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
470
+ _defineProperty(_this, "setupTransformInteraction", function (redliningLayer) {
471
+ var selectedFeatures = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
472
+ var multi = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
483
473
  var transformInteraction = new ol.interaction.Transform({
484
474
  stretch: false,
475
+ addCondition: multi ? function (ev) {
476
+ return ev.originalEvent.ctrlKey;
477
+ } : null,
485
478
  keepAspectRatio: function keepAspectRatio(ev) {
486
- return _this.currentFeature ? GeomTypeConfig[_this.currentFeature.get('shape')].regular || ol.events.condition.shiftKeyOnly(ev) : false;
487
- }
479
+ return _this.selectedFeatures.find(function (f) {
480
+ return GeomTypeConfig[f.get('shape')].regular;
481
+ }) || ol.events.condition.shiftKeyOnly(ev);
482
+ },
483
+ layers: [redliningLayer],
484
+ translateFeature: true
488
485
  });
489
- transformInteraction.on('rotating', function (e) {
490
- if (_this.currentFeature.get('shape') === 'Text') {
491
- _this.currentFeature.set('rotation', -e.angle);
486
+ // Hacky workaround translateFeature interfering with ctrl-click to deselect selected features
487
+ var origHandleDownEvent = transformInteraction.handleDownEvent;
488
+ transformInteraction.handleDownEvent = function (evt) {
489
+ if (evt.originalEvent.ctrlKey) {
490
+ transformInteraction.set('translateFeature', false);
492
491
  }
492
+ var ret = origHandleDownEvent.call(transformInteraction, evt);
493
+ transformInteraction.set('translateFeature', true);
494
+ return ret;
495
+ };
496
+ transformInteraction.on('rotating', function (ev) {
497
+ ev.features.forEach(function (feature) {
498
+ if (feature.get('shape') === 'Text') {
499
+ feature.set('rotation', -ev.angle);
500
+ }
501
+ });
493
502
  });
494
503
  transformInteraction.on('rotateend', _this.updateSelectedFeature);
495
504
  transformInteraction.on('translateend', _this.updateSelectedFeature);
@@ -500,11 +509,19 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
500
509
  return transformInteraction;
501
510
  });
502
511
  _defineProperty(_this, "updateSelectedFeature", function () {
503
- var _featureObj$shape2;
504
- var featureObj = _this.currentFeatureObject();
512
+ var _featureObj$shape, _featureObj;
513
+ var featureObj = null;
514
+ if (_this.selectedFeatures.length === 1) {
515
+ featureObj = _this.serializeFeature(_this.selectedFeatures[0]);
516
+ } else if (_this.selectedFeatures.length > 1) {
517
+ featureObj = {
518
+ type: "FeatureCollection",
519
+ features: []
520
+ };
521
+ }
505
522
  _this.props.changeRedliningState({
506
523
  selectedFeature: featureObj,
507
- geomType: (_featureObj$shape2 = featureObj === null || featureObj === void 0 ? void 0 : featureObj.shape) !== null && _featureObj$shape2 !== void 0 ? _featureObj$shape2 : _this.props.redlining.geomType
524
+ geomType: (_featureObj$shape = (_featureObj = featureObj) === null || _featureObj === void 0 ? void 0 : _featureObj.shape) !== null && _featureObj$shape !== void 0 ? _featureObj$shape : _this.props.redlining.geomType
508
525
  });
509
526
  });
510
527
  _defineProperty(_this, "triggerDelete", function () {
@@ -512,61 +529,96 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
512
529
  action: "Delete"
513
530
  });
514
531
  });
515
- _defineProperty(_this, "deleteCurrentFeature", function () {
516
- if (_this.currentFeature) {
517
- _this.props.removeLayerFeatures(_this.props.redlining.layer, [_this.currentFeature.getId()], true);
518
- _this.currentFeature = null;
532
+ _defineProperty(_this, "deleteCurrentFeatures", function () {
533
+ if (_this.selectedFeatures.length) {
534
+ _this.props.removeLayerFeatures(_this.props.redlining.layer, _this.selectedFeatures.map(function (f) {
535
+ return f.getId();
536
+ }), true);
537
+ _this.selectedFeatures = [];
519
538
  }
520
539
  });
521
- _defineProperty(_this, "commitCurrentFeature", function (redliningProps) {
522
- var _feature$geometry;
523
- var newFeature = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
524
- var feature = _this.currentFeatureObject();
525
- if (!feature) {
526
- return null;
540
+ _defineProperty(_this, "updateRedliningState", function (firstSelection) {
541
+ if (_this.selectedFeatures.length > 0) {
542
+ var features = _this.selectedFeatures;
543
+ var featureObj = features.length === 1 ? _this.serializeFeature(features[0]) : {
544
+ type: "FeatureCollection",
545
+ features: []
546
+ };
547
+ var newRedliningState = {
548
+ selectedFeature: featureObj
549
+ };
550
+ if (firstSelection || _this.selectedFeatures.length === 1) {
551
+ var _featureObj$shape2;
552
+ newRedliningState.style = _this.styleProps(features[0]);
553
+ newRedliningState.measurements = !!features[0].get('measurements');
554
+ newRedliningState.geomType = (_featureObj$shape2 = featureObj === null || featureObj === void 0 ? void 0 : featureObj.shape) !== null && _featureObj$shape2 !== void 0 ? _featureObj$shape2 : _this.props.redlining.geomType;
555
+ var measurements = features[0].get('measurements');
556
+ if (measurements) {
557
+ newRedliningState.lenUnit = measurements.lenUnit;
558
+ newRedliningState.areaUnit = measurements.areaUnit;
559
+ }
560
+ }
561
+ _this.props.changeRedliningState(newRedliningState);
562
+ } else {
563
+ _this.props.changeRedliningState({
564
+ selectedFeature: null
565
+ });
527
566
  }
528
- // Don't commit empty/invalid features
529
- if (feature.shape === "Text" && !feature.properties.label || feature.shape === "Circle" && feature.circleParams.radius === 0 || ((_feature$geometry = feature.geometry) === null || _feature$geometry === void 0 ? void 0 : _feature$geometry.type) === "Polygon" && _this.currentFeature.getGeometry().getArea() === 0) {
530
- if (!newFeature) {
531
- _this.props.removeLayerFeatures(redliningProps.layer, [feature.id]);
567
+ });
568
+ _defineProperty(_this, "selectFeatures", function (features) {
569
+ var firstSelection = isEmpty(_this.selectedFeatures);
570
+ features.forEach(function (feature) {
571
+ feature.setStyle(_this.styleFunction);
572
+ feature.on('change', _this.updateMeasurements);
573
+ _this.selectedFeatures.push(feature);
574
+ });
575
+ _this.updateRedliningState(firstSelection);
576
+ });
577
+ _defineProperty(_this, "commitFeatures", function (features, redliningProps) {
578
+ var newFeature = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
579
+ var featureObjects = features.map(function (feature) {
580
+ var _featureObj$geometry;
581
+ var styleName = feature.get("shape") === "Text" ? "text" : "default";
582
+ var style = FeatureStyles[styleName](feature, feature.get('styleOptions'));
583
+ feature.setStyle(style);
584
+ feature.un('change', _this.updateMeasurements);
585
+ _this.selectedFeatures = _this.selectedFeatures.filter(function (f) {
586
+ return f !== feature;
587
+ });
588
+ var featureObj = _this.serializeFeature(feature);
589
+ // Don't commit empty/invalid features
590
+ if (featureObj.shape === "Text" && !featureObj.properties.label || featureObj.shape === "Circle" && featureObj.circleParams.radius === 0 || ((_featureObj$geometry = featureObj.geometry) === null || _featureObj$geometry === void 0 ? void 0 : _featureObj$geometry.type) === "Polygon" && feature.getGeometry().getArea() === 0) {
591
+ if (!newFeature) {
592
+ _this.props.removeLayerFeatures(redliningProps.layer, [featureObj.id]);
593
+ }
594
+ return null;
595
+ }
596
+ if (featureObj.shape === "Circle") {
597
+ var _featureObj$circlePar = featureObj.circleParams,
598
+ center = _featureObj$circlePar.center,
599
+ radius = _featureObj$circlePar.radius;
600
+ var deg2rad = Math.PI / 180;
601
+ featureObj.geometry.type = "Polygon";
602
+ featureObj.geometry.coordinates = [Array.apply(null, Array(91)).map(function (item, index) {
603
+ return [center[0] + radius * Math.cos(4 * index * deg2rad), center[1] + radius * Math.sin(4 * index * deg2rad)];
604
+ })];
532
605
  }
533
- _this.resetSelectedFeature();
606
+ if (featureObj.geometry.type === "LineString" || featureObj.geometry.type === "Polygon") {
607
+ featureObj.geometry.coordinates = VectorLayerUtils.removeDuplicateNodes(featureObj.geometry.coordinates);
608
+ }
609
+ return featureObj;
610
+ }).filter(Boolean);
611
+ if (isEmpty(featureObjects)) {
534
612
  return null;
535
613
  }
536
- if (feature.shape === "Circle") {
537
- var _feature$circleParams = feature.circleParams,
538
- center = _feature$circleParams.center,
539
- radius = _feature$circleParams.radius;
540
- var deg2rad = Math.PI / 180;
541
- feature.geometry.type = "Polygon";
542
- feature.geometry.coordinates = [Array.apply(null, Array(91)).map(function (item, index) {
543
- return [center[0] + radius * Math.cos(4 * index * deg2rad), center[1] + radius * Math.sin(4 * index * deg2rad)];
544
- })];
545
- }
546
- if (feature.geometry.type === "LineString" || feature.geometry.type === "Polygon") {
547
- feature.geometry.coordinates = VectorLayerUtils.removeDuplicateNodes(feature.geometry.coordinates);
548
- }
549
614
  var layer = {
550
615
  id: redliningProps.layer,
551
616
  title: redliningProps.layerTitle,
552
617
  role: LayerRole.USERLAYER
553
618
  };
554
- _this.props.addLayerFeatures(layer, [feature]);
555
- _this.resetSelectedFeature();
556
- return feature;
557
- });
558
- _defineProperty(_this, "resetSelectedFeature", function () {
559
- if (_this.currentFeature) {
560
- // Reset selection style
561
- var styleName = _this.currentFeature.get("shape") === "Text" ? "text" : "default";
562
- var style = FeatureStyles[styleName](_this.currentFeature, _this.currentFeature.get('styleOptions'));
563
- _this.currentFeature.setStyle(style);
564
- _this.currentFeature.un('change', _this.updateMeasurements);
565
- _this.currentFeature = null;
566
- _this.props.changeRedliningState({
567
- selectedFeature: null
568
- });
569
- }
619
+ _this.props.addLayerFeatures(layer, featureObjects);
620
+ _this.updateRedliningState();
621
+ return featureObjects;
570
622
  });
571
623
  _defineProperty(_this, "reset", function (redliningProps) {
572
624
  _this.setState({
@@ -576,50 +628,47 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
576
628
  _this.props.map.removeInteraction(_this.interactions.shift());
577
629
  }
578
630
  if (_this.picking) {
579
- _this.commitCurrentFeature(redliningProps, false);
631
+ _this.commitFeatures(_this.selectedFeatures, redliningProps, false);
580
632
  } else {
581
- _this.resetSelectedFeature();
633
+ _this.selectedFeatures.forEach(function (feature) {
634
+ var styleName = feature.get("shape") === "Text" ? "text" : "default";
635
+ var style = FeatureStyles[styleName](feature, feature.get('styleOptions'));
636
+ feature.setStyle(style);
637
+ feature.un('change', _this.updateMeasurements);
638
+ });
639
+ _this.selectedFeatures = [];
582
640
  }
583
641
  _this.props.map.un('click', _this.maybeEnterTemporaryDrawMode);
584
642
  _this.picking = false;
585
643
  });
586
- _defineProperty(_this, "searchRedliningLayer", function (layerId) {
587
- var _this$props$map$getLa;
588
- return (_this$props$map$getLa = _this.props.map.getLayers().getArray().find(function (l) {
589
- return l.get('id') === layerId;
590
- })) !== null && _this$props$map$getLa !== void 0 ? _this$props$map$getLa : null;
591
- });
592
- _defineProperty(_this, "currentFeatureObject", function () {
593
- if (!_this.currentFeature) {
594
- return null;
595
- }
644
+ _defineProperty(_this, "serializeFeature", function (feature) {
596
645
  var format = new ol.format.GeoJSON();
597
- var feature = format.writeFeatureObject(_this.currentFeature);
598
- if (_this.currentFeature.get("shape") === "Circle") {
599
- feature.circleParams = {
600
- center: _this.currentFeature.getGeometry().getCenter(),
601
- radius: _this.currentFeature.getGeometry().getRadius()
646
+ var featureObject = format.writeFeatureObject(feature);
647
+ if (feature.get("shape") === "Circle") {
648
+ featureObject.circleParams = {
649
+ center: feature.getGeometry().getCenter(),
650
+ radius: feature.getGeometry().getRadius()
602
651
  };
603
652
  }
604
- feature.styleName = _this.currentFeature.get('styleName');
605
- feature.styleOptions = _this.currentFeature.get('styleOptions');
606
- feature.shape = _this.currentFeature.get('shape');
607
- feature.measurements = _this.currentFeature.get('measurements');
608
- feature.crs = _this.props.mapCrs;
653
+ featureObject.styleName = feature.get('styleName');
654
+ featureObject.styleOptions = feature.get('styleOptions');
655
+ featureObject.shape = feature.get('shape');
656
+ featureObject.measurements = feature.get('measurements');
657
+ featureObject.crs = _this.props.mapCrs;
609
658
  // Don't pollute GeoJSON object properties with internal props
610
- delete feature.properties.styleName;
611
- delete feature.properties.styleOptions;
612
- delete feature.properties.shape;
613
- delete feature.properties.measurements;
614
- delete feature.properties.circleParams;
659
+ delete featureObject.properties.styleName;
660
+ delete featureObject.properties.styleOptions;
661
+ delete featureObject.properties.shape;
662
+ delete featureObject.properties.measurements;
663
+ delete featureObject.properties.circleParams;
615
664
  // Don't store empty label prop
616
- if (feature.properties.label === "") {
617
- delete feature.properties.label;
665
+ if (featureObject.properties.label === "") {
666
+ delete featureObject.properties.label;
618
667
  }
619
- return feature;
668
+ return featureObject;
620
669
  });
621
670
  _defineProperty(_this, "export", function () {
622
- var currentFeature = _this.commitCurrentFeature(_this.props.redlining);
671
+ var committedFeatures = _this.commitFeatures(_this.selectedFeatures, _this.props.redlining);
623
672
  var layer = _this.props.layers.find(function (l) {
624
673
  return l.id === _this.props.redlining.layer;
625
674
  });
@@ -630,7 +679,10 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
630
679
  var geojson = JSON.stringify({
631
680
  type: "FeatureCollection",
632
681
  features: layer.features.map(function (feature) {
633
- var newFeature = _objectSpread({}, feature.id === currentFeature.id ? currentFeature : feature);
682
+ var _committedFeatures$fi;
683
+ var newFeature = _objectSpread({}, (_committedFeatures$fi = committedFeatures.find(function (f) {
684
+ return f.id === feature.id;
685
+ })) !== null && _committedFeatures$fi !== void 0 ? _committedFeatures$fi : feature);
634
686
  newFeature.geometry = VectorLayerUtils.reprojectGeometry(feature.geometry, feature.crs || _this.props.mapCrs, 'EPSG:4326');
635
687
  delete newFeature.crs;
636
688
  return newFeature;
@@ -663,7 +715,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
663
715
  });
664
716
  _this.interactions = [];
665
717
  _this.picking = false;
666
- _this.currentFeature = null;
718
+ _this.selectedFeatures = [];
667
719
  _this.blockOnChange = false;
668
720
  _this.selectedTextStyle = function (feature, opts) {
669
721
  return new ol.style.Style({
@@ -704,6 +756,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
704
756
  return _createClass(RedliningSupport, [{
705
757
  key: "componentDidUpdate",
706
758
  value: function componentDidUpdate(prevProps) {
759
+ var _this2 = this;
707
760
  // Bind keyboard shortcuts to delete features
708
761
  if (this.props.redlining.action && !prevProps.redlining.action) {
709
762
  Mousetrap.bind('del', this.triggerDelete);
@@ -714,7 +767,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
714
767
  }
715
768
  // Handle delete action immediately and reset the redlining state to the previous action
716
769
  if (this.props.redlining.action === 'Delete') {
717
- this.deleteCurrentFeature();
770
+ this.deleteCurrentFeatures();
718
771
  this.props.changeRedliningState(_objectSpread(_objectSpread({}, prevProps.redlining), {}, {
719
772
  selectedFeature: null
720
773
  }));
@@ -725,6 +778,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
725
778
  this.props.changeRedliningState(_objectSpread(_objectSpread({}, prevProps.redlining), {}, {
726
779
  selectedFeature: null
727
780
  }));
781
+ return;
728
782
  }
729
783
  var recreateInteraction = this.props.redlining.action !== prevProps.redlining.action || this.props.redlining.layer !== prevProps.redlining.layer || ['Draw', 'PickDraw'].includes(this.props.redlining.action) && this.props.redlining.geomType !== prevProps.redlining.geomType || this.props.redlining.freehand !== prevProps.redlining.freehand || this.props.redlining.drawMultiple !== prevProps.redlining.drawMultiple;
730
784
  if (recreateInteraction) {
@@ -737,31 +791,37 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
737
791
  } else if (this.props.redlining.action === 'Pick' || this.props.redlining.action === 'Buffer') {
738
792
  this.addPickInteraction();
739
793
  } else if (this.props.redlining.action === 'PickDraw') {
740
- this.addPickDrawInteraction();
794
+ this.waitForFeatureAndLayer(this.props.redlining.layer, null, function () {
795
+ return _this2.addPickInteraction();
796
+ });
741
797
  }
742
798
  }
743
- if (this.currentFeature) {
799
+ if (this.selectedFeatures) {
744
800
  // Update feature style
745
801
  if (this.props.redlining.style !== prevProps.redlining.style) {
746
- this.updateFeatureStyle(this.props.redlining.style);
802
+ this.selectedFeatures.forEach(this.updateFeatureStyle);
747
803
  }
748
804
  // Update current feature measurements
749
- if (this.props.map.displayCrs !== prevProps.map.displayCrs || this.props.redlining.measurements !== prevProps.redlining.measurements || this.props.redlining.lenUnit !== prevProps.redlining.lenUnit || this.props.redlining.areaUnit !== prevProps.redlining.areaUnit) {
750
- this.currentFeature.changed();
805
+ if (this.props.redlining.measurements !== prevProps.redlining.measurements) {
806
+ this.selectedFeatures.forEach(this.toggleFeatureMeasurements);
807
+ } else if (this.props.map.displayCrs !== prevProps.map.displayCrs || this.props.redlining.lenUnit !== prevProps.redlining.lenUnit || this.props.redlining.areaUnit !== prevProps.redlining.areaUnit) {
808
+ this.selectedFeatures.forEach(function (feature) {
809
+ return feature.changed();
810
+ });
751
811
  }
752
812
  }
753
813
  }
754
814
  }, {
755
815
  key: "render",
756
816
  value: function render() {
757
- var _this2 = this;
817
+ var _this3 = this;
758
818
  var widgets = [];
759
819
  if (this.props.redlining.extraAction === "NumericInput") {
760
820
  widgets.push(/*#__PURE__*/React.createElement(NumericInputWindow, {
761
821
  feature: this.props.redlining.selectedFeature,
762
822
  key: "NumericInputWindow",
763
823
  onClose: function onClose() {
764
- return _this2.props.changeRedliningState({
824
+ return _this3.props.changeRedliningState({
765
825
  extraAction: null
766
826
  });
767
827
  },
@@ -773,7 +833,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
773
833
  key: "FeatureAttributesWindow",
774
834
  layerid: this.props.redlining.layer,
775
835
  onClose: function onClose() {
776
- return _this2.props.changeRedliningState({
836
+ return _this3.props.changeRedliningState({
777
837
  extraAction: null
778
838
  });
779
839
  },