qwc2 2025.10.29 → 2025.11.5

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.
@@ -483,7 +483,7 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
483
483
  });
484
484
  _defineProperty(_this, "addSHPLayer", /*#__PURE__*/function () {
485
485
  var _ref6 = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(file) {
486
- var _yield$import3, _BrowserFileSystem, load, _yield$import4, ShapefileLoader, _yield$import5, ZipLoader, fileMap, EXTENSIONS, files, fileName, name, ext, fileList, blob, f, list, fileSystem, fetch, filename, data;
486
+ var _yield$import3, _BrowserFileSystem, load, _yield$import4, Proj4Projection, _yield$import5, ShapefileLoader, _yield$import6, ZipLoader, mimeTypes, projections, fileMap, EXTENSIONS, files, fileName, name, ext, fileList, blob, f, list, fileSystem, fetch, filename, data;
487
487
  return _regeneratorRuntime().wrap(function _callee2$(_context2) {
488
488
  while (1) switch (_context2.prev = _context2.next) {
489
489
  case 0:
@@ -494,22 +494,33 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
494
494
  _BrowserFileSystem = _yield$import3._BrowserFileSystem;
495
495
  load = _yield$import3.load;
496
496
  _context2.next = 7;
497
- return import('@loaders.gl/shapefile');
497
+ return import('@math.gl/proj4');
498
498
  case 7:
499
499
  _yield$import4 = _context2.sent;
500
- ShapefileLoader = _yield$import4.ShapefileLoader;
500
+ Proj4Projection = _yield$import4.Proj4Projection;
501
501
  _context2.next = 11;
502
- return import('@loaders.gl/zip');
502
+ return import('@loaders.gl/shapefile');
503
503
  case 11:
504
504
  _yield$import5 = _context2.sent;
505
- ZipLoader = _yield$import5.ZipLoader;
506
- if (!(file.type === "application/zip")) {
507
- _context2.next = 51;
505
+ ShapefileLoader = _yield$import5.ShapefileLoader;
506
+ _context2.next = 15;
507
+ return import('@loaders.gl/zip');
508
+ case 15:
509
+ _yield$import6 = _context2.sent;
510
+ ZipLoader = _yield$import6.ZipLoader;
511
+ // Import SHP layer from ZIP. Zip must contain all the required files : shp, dbf, shx, prj, cpg
512
+ mimeTypes = ['application/zip', 'application/zip-compressed', 'application/x-zip-compressed'];
513
+ if (!mimeTypes.includes(file.type)) {
514
+ _context2.next = 58;
508
515
  break;
509
516
  }
510
- _context2.next = 16;
517
+ projections = ConfigUtils.getConfigProp("projections") || [];
518
+ if (projections) {
519
+ Proj4Projection.defineProjectionAliases(projections);
520
+ }
521
+ _context2.next = 23;
511
522
  return load(file, ZipLoader);
512
- case 16:
523
+ case 23:
513
524
  fileMap = _context2.sent;
514
525
  EXTENSIONS = ['shp', 'shx', 'dbf', 'cpg', 'prj'];
515
526
  files = {}; // Iterate through all the files in ZIP to get a list of (SHP + sidecar files) by filename
@@ -528,14 +539,14 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
528
539
  }
529
540
  // Load each SHP with sidecar files as GeoJSON features
530
541
  _context2.t0 = _regeneratorRuntime().keys(files);
531
- case 21:
542
+ case 28:
532
543
  if ((_context2.t1 = _context2.t0()).done) {
533
- _context2.next = 50;
544
+ _context2.next = 57;
534
545
  break;
535
546
  }
536
547
  f = _context2.t1.value;
537
548
  if (!Object.hasOwn(files, f)) {
538
- _context2.next = 48;
549
+ _context2.next = 55;
539
550
  break;
540
551
  }
541
552
  list = files[f];
@@ -543,8 +554,8 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
543
554
  fetch = fileSystem.fetch.bind(fileSystem.fetch);
544
555
  filename = "".concat(f, ".shp"); // Load SHP and reproject to mapCrs
545
556
  data = null;
546
- _context2.prev = 29;
547
- _context2.next = 32;
557
+ _context2.prev = 36;
558
+ _context2.next = 39;
548
559
  return load(filename, ShapefileLoader, {
549
560
  fetch: fetch,
550
561
  shapefile: {
@@ -556,15 +567,15 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
556
567
  _targetCrs: _this.props.mapCrs
557
568
  }
558
569
  });
559
- case 32:
570
+ case 39:
560
571
  data = _context2.sent;
561
- _context2.next = 47;
572
+ _context2.next = 54;
562
573
  break;
563
- case 35:
564
- _context2.prev = 35;
565
- _context2.t2 = _context2["catch"](29);
566
- _context2.prev = 37;
567
- _context2.next = 40;
574
+ case 42:
575
+ _context2.prev = 42;
576
+ _context2.t2 = _context2["catch"](36);
577
+ _context2.prev = 44;
578
+ _context2.next = 47;
568
579
  return load(filename, ShapefileLoader, {
569
580
  fetch: fetch,
570
581
  shapefile: {
@@ -576,17 +587,17 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
576
587
  _targetCrs: _this.props.mapCrs
577
588
  }
578
589
  });
579
- case 40:
590
+ case 47:
580
591
  data = _context2.sent;
581
592
  /* eslint-disable-next-line */
582
593
  alert(LocaleUtils.tr("importlayer.shpreprojectionerror"));
583
- _context2.next = 47;
594
+ _context2.next = 54;
584
595
  break;
585
- case 44:
586
- _context2.prev = 44;
587
- _context2.t3 = _context2["catch"](37);
596
+ case 51:
597
+ _context2.prev = 51;
598
+ _context2.t3 = _context2["catch"](44);
588
599
  data = null;
589
- case 47:
600
+ case 54:
590
601
  if (data) {
591
602
  data.crs = {
592
603
  type: "name",
@@ -597,19 +608,19 @@ var ImportLayer = /*#__PURE__*/function (_React$Component) {
597
608
  // Add data as GeoJSON layer
598
609
  _this.addGeoJSONLayer(f, data);
599
610
  }
600
- case 48:
601
- _context2.next = 21;
611
+ case 55:
612
+ _context2.next = 28;
602
613
  break;
603
- case 50:
614
+ case 57:
604
615
  _this.setState({
605
616
  file: null,
606
617
  addingLayer: false
607
618
  });
608
- case 51:
619
+ case 58:
609
620
  case "end":
610
621
  return _context2.stop();
611
622
  }
612
- }, _callee2, null, [[29, 35], [37, 44]]);
623
+ }, _callee2, null, [[36, 42], [44, 51]]);
613
624
  }));
614
625
  return function (_x2) {
615
626
  return _ref6.apply(this, arguments);
@@ -687,7 +687,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
687
687
  _this2.objectMap = {};
688
688
  // Add 3d tiles
689
689
  (((_this2$props$theme$ma9 = _this2.props.theme.map3d) === null || _this2$props$theme$ma9 === void 0 ? void 0 : _this2$props$theme$ma9.tiles3d) || []).forEach(function (entry) {
690
- var _entry$title;
690
+ var _entry$visibility, _entry$title;
691
691
  var tiles = new Tiles3D({
692
692
  url: MiscUtils.resolveAssetsPath(entry.url),
693
693
  errorTarget: 32
@@ -723,7 +723,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
723
723
  _this2.instance.add(tiles);
724
724
  _this2.objectMap[entry.name] = tiles;
725
725
  sceneObjects[entry.name] = {
726
- visibility: true,
726
+ visibility: (_entry$visibility = entry.visibility) !== null && _entry$visibility !== void 0 ? _entry$visibility : true,
727
727
  opacity: 255,
728
728
  layertree: true,
729
729
  title: (_entry$title = entry.title) !== null && _entry$title !== void 0 ? _entry$title : entry.name,
@@ -740,7 +740,10 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
740
740
 
741
741
  // Add other objects
742
742
  (((_this2$props$theme$ma10 = _this2.props.theme.map3d) === null || _this2$props$theme$ma10 === void 0 ? void 0 : _this2$props$theme$ma10.objects3d) || []).forEach(function (entry) {
743
- importGltf(MiscUtils.resolveAssetsPath(entry.url), entry.name, _this2.state.sceneContext);
743
+ var _entry$visibility2;
744
+ importGltf(MiscUtils.resolveAssetsPath(entry.url), entry.name, _this2.state.sceneContext, {
745
+ visibility: (_entry$visibility2 = entry.visibility) !== null && _entry$visibility2 !== void 0 ? _entry$visibility2 : true
746
+ });
744
747
  });
745
748
  _this2.setState(function (state) {
746
749
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2025.10.29",
3
+ "version": "2025.11.05",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -142,7 +142,8 @@ var Cyclomedia = /*#__PURE__*/function (_React$Component) {
142
142
  styleOptions: {
143
143
  img: "cyclomedia-cone",
144
144
  rotation: posData.yaw,
145
- size: dimensions
145
+ size: dimensions,
146
+ anchor: [0.5, 1]
146
147
  }
147
148
  };
148
149
  var layer = {
@@ -181,7 +182,7 @@ var Cyclomedia = /*#__PURE__*/function (_React$Component) {
181
182
  }
182
183
  }
183
184
  var loginOauth = !!_this.props.clientId && !_this.state.loginFailed;
184
- return "\n <!DOCTYPE html>\n <html>\n <head>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react@16.12.0/umd/react.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react-dom@16.12.0/umd/react-dom.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://streetsmart.cyclomedia.com/api/v".concat(_this.props.cyclomediaVersion, "/StreetSmartApi.js\"></script>\n <script type=\"text/javascript\">\n let apiInitialized = false;\n let initCallback = null;\n let posCallback = null;\n let measureCallback = null;\n\n function initApi() {\n StreetSmartApi.init({\n targetElement: document.getElementById(\"streetsmartApi\"),\n username: \"").concat(_this.state.username || undefined, "\",\n password: \"").concat(_this.state.password || undefined, "\",\n apiKey: \"").concat(_this.props.apikey, "\",\n clientId: \"").concat(_this.props.clientId, "\",\n loginOauth: ").concat(loginOauth, ",\n loginRedirectUri: \"").concat(_this.props.loginRedirectUri, "\",\n logoutRedirectUri: \"").concat(_this.props.logoutRedirectUri, "\",\n srs: \"").concat(_this.props.projection, "\",\n locale: \"").concat(lang, "\",\n configurationUrl: 'https://atlas.cyclomedia.com/configuration',\n addressSettings: {\n locale: \"us\",\n database: \"Nokia\"\n }\n }).then(() => {\n apiInitialized = true;\n if (initCallback) {\n initCallback(true);\n }\n }, (e) => {\n apiInitialized = false;\n if (initCallback) {\n initCallback(false, e.message);\n }\n });\n }\n function openImage(posStr, crs) {\n if (!apiInitialized) {\n return;\n }\n StreetSmartApi.open(posStr, {\n viewerType: StreetSmartApi.ViewerType.PANORAMA,\n srs: crs,\n panoramaViewer: {\n closable: false,\n maximizable: true,\n replace: true,\n recordingsVisible: true,\n navbarVisible: true,\n timeTravelVisible: true,\n measureTypeButtonVisible: true,\n measureTypeButtonStart: true,\n measureTypeButtonToggle: true,\n },\n }).then((result) => {\n if (result && result[0]){\n window.panoramaViewer = result[0];\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.IMAGE_CHANGE, changeView);\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.VIEW_CHANGE, changeView);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_CHANGED, changeMeasurement);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_STOPPED, stopMeasurement);\n }\n }).catch((reason) => {\n console.log('Failed to create component(s) through API: ' + reason);\n });\n }\n function changeView() {\n if (posCallback) {\n const recording = window.panoramaViewer.getRecording();\n const orientation = window.panoramaViewer.getOrientation();\n const pos = recording.xyz;\n const posData = {\n pos: [pos[0], pos[1]],\n crs: recording.srs,\n yaw: orientation.yaw * Math.PI / 180,\n hFov: orientation.hFov * Math.PI / 180.0\n }\n posCallback(posData);\n }\n }\n function changeMeasurement(e) {\n measureCallback(e.detail.activeMeasurement);\n }\n function stopMeasurement() {\n measureCallback(null);\n }\n function registerCallbacks(_initCallback, _posCallback, _measureCallback) {\n initCallback = _initCallback;\n posCallback = _posCallback;\n measureCallback = _measureCallback;\n }\n </script>\n <style>\n html, body, #streetsmartApi {height: 100%;}\n </style>\n </head>\n <body style=\"margin: 0\">\n <div id=\"streetsmartApi\">\n </div>\n </body>\n </html>\n ");
185
+ return "\n <!DOCTYPE html>\n <html>\n <head>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react@18.3.1/umd/react.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://streetsmart.cyclomedia.com/api/v".concat(_this.props.cyclomediaVersion, "/StreetSmartApi.js\"></script>\n <script type=\"text/javascript\">\n let apiInitialized = false;\n let initCallback = null;\n let posCallback = null;\n let measureCallback = null;\n\n function initApi() {\n StreetSmartApi.init({\n targetElement: document.getElementById(\"streetsmartApi\"),\n username: \"").concat(_this.state.username || undefined, "\",\n password: \"").concat(_this.state.password || undefined, "\",\n apiKey: \"").concat(_this.props.apikey, "\",\n clientId: \"").concat(_this.props.clientId, "\",\n loginOauth: ").concat(loginOauth, ",\n loginRedirectUri: \"").concat(_this.props.loginRedirectUri, "\",\n logoutRedirectUri: \"").concat(_this.props.logoutRedirectUri, "\",\n srs: \"").concat(_this.props.projection, "\",\n locale: \"").concat(lang, "\",\n configurationUrl: 'https://atlas.cyclomedia.com/configuration',\n addressSettings: {\n locale: \"us\",\n database: \"Nokia\"\n }\n }).then(() => {\n apiInitialized = true;\n if (initCallback) {\n initCallback(true);\n }\n }, (e) => {\n apiInitialized = false;\n if (initCallback) {\n initCallback(false, e.message);\n }\n });\n }\n function openImage(posStr, crs) {\n if (!apiInitialized) {\n return;\n }\n StreetSmartApi.open(posStr, {\n viewerType: StreetSmartApi.ViewerType.PANORAMA,\n srs: crs,\n panoramaViewer: {\n closable: false,\n maximizable: true,\n replace: true,\n recordingsVisible: true,\n navbarVisible: true,\n timeTravelVisible: true,\n measureTypeButtonVisible: true,\n measureTypeButtonStart: true,\n measureTypeButtonToggle: true,\n },\n }).then((result) => {\n if (result && result[0]){\n window.panoramaViewer = result[0];\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.IMAGE_CHANGE, changeView);\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.VIEW_CHANGE, changeView);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_CHANGED, changeMeasurement);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_STOPPED, stopMeasurement);\n }\n }).catch((reason) => {\n console.log('Failed to create component(s) through API: ' + reason);\n });\n }\n function changeView() {\n if (posCallback) {\n const recording = window.panoramaViewer.getRecording();\n const orientation = window.panoramaViewer.getOrientation();\n const pos = recording.xyz;\n const posData = {\n pos: [pos[0], pos[1]],\n crs: recording.srs,\n yaw: orientation.yaw * Math.PI / 180,\n hFov: orientation.hFov * Math.PI / 180.0\n }\n posCallback(posData);\n }\n }\n function changeMeasurement(e) {\n measureCallback(e.detail.activeMeasurement);\n }\n function stopMeasurement() {\n measureCallback(null);\n }\n function registerCallbacks(_initCallback, _posCallback, _measureCallback) {\n initCallback = _initCallback;\n posCallback = _posCallback;\n measureCallback = _measureCallback;\n }\n </script>\n <style>\n html, body, #streetsmartApi {height: 100%;}\n </style>\n </head>\n <body style=\"margin: 0\">\n <div id=\"streetsmartApi\">\n </div>\n </body>\n </html>\n ");
185
186
  });
186
187
  _defineProperty(_this, "addRecordingsWFS", function () {
187
188
  var layer = {
@@ -417,7 +418,7 @@ _defineProperty(Cyclomedia, "propTypes", {
417
418
  userInfos: PropTypes.object
418
419
  });
419
420
  _defineProperty(Cyclomedia, "defaultProps", {
420
- cyclomediaVersion: '24.1',
421
+ cyclomediaVersion: '25.7',
421
422
  displayMeasurements: true,
422
423
  geometry: {
423
424
  initialWidth: 480,
@@ -220,6 +220,15 @@ var Redlining = /*#__PURE__*/function (_React$Component) {
220
220
  geomType: null,
221
221
  text: ""
222
222
  }
223
+ } : null, toolEnabled("Clone") ? {
224
+ key: "Clone",
225
+ tooltip: LocaleUtils.tr("redlining.clone"),
226
+ icon: "clone",
227
+ data: {
228
+ action: "Clone",
229
+ geomType: null
230
+ },
231
+ disabled: !_this.props.redlining.selectedFeature
223
232
  } : null, {
224
233
  key: "Delete",
225
234
  tooltip: LocaleUtils.tr("redlining.delete"),
@@ -196,7 +196,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
196
196
  areaUnit: _this.props.redlining.areaUnit
197
197
  };
198
198
  MeasureUtils.updateFeatureMeasurements(feature, feature.get('shape'), _this.props.mapCrs, settings);
199
- } else {
199
+ } else if (feature.get('measurements')) {
200
200
  feature.set('measurements', undefined);
201
201
  feature.set('segment_labels', undefined);
202
202
  feature.set('label', '');
@@ -390,6 +390,7 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
390
390
  _this.commitFeatures(removed, _this.props.redlining);
391
391
  });
392
392
  _this.picking = true;
393
+ return transformInteraction;
393
394
  });
394
395
  _defineProperty(_this, "maybeEnterTemporaryDrawMode", function (ev) {
395
396
  var redliningLayer = _this.searchRedliningLayer(_this.props.redlining.layer);
@@ -537,6 +538,45 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
537
538
  _this.selectedFeatures = [];
538
539
  }
539
540
  });
541
+ _defineProperty(_this, "cloneCurrentFeatures", function () {
542
+ if (isEmpty(_this.selectedFeatures)) {
543
+ return;
544
+ }
545
+ var _shiftCoordinates = function shiftCoordinates(coords) {
546
+ if (typeof coords[0] === 'number') {
547
+ coords[0] += 10;
548
+ coords[1] += 10;
549
+ } else {
550
+ coords.map(_shiftCoordinates);
551
+ }
552
+ };
553
+ var cloneIds = [];
554
+ var newFeatureObjs = _this.selectedFeatures.map(function (feature) {
555
+ _this.deselectFeature(feature, false);
556
+ var featureObj = _this.serializeFeature(feature);
557
+ featureObj.id = uuidv4();
558
+ _shiftCoordinates(featureObj.geometry.coordinates);
559
+ cloneIds.push(featureObj.id);
560
+ return featureObj;
561
+ });
562
+ _this.updateRedliningState(true);
563
+ var layer = {
564
+ id: _this.props.redlining.layer,
565
+ title: _this.props.redlining.layerTitle,
566
+ role: LayerRole.USERLAYER
567
+ };
568
+ _this.props.addLayerFeatures(layer, newFeatureObjs);
569
+ _this.waitForFeatureAndLayer(_this.props.redlining.layer, cloneIds[0], function (l) {
570
+ var features = cloneIds.map(function (id) {
571
+ return l.getSource().getFeatureById(id);
572
+ });
573
+ _this.selectFeatures(features);
574
+ while (_this.interactions.length > 0) {
575
+ _this.props.map.removeInteraction(_this.interactions.shift());
576
+ }
577
+ _this.addTransformInteraction().setSelection(new ol.Collection(features));
578
+ });
579
+ });
540
580
  _defineProperty(_this, "updateRedliningState", function (firstSelection) {
541
581
  if (_this.selectedFeatures.length > 0) {
542
582
  var features = _this.selectedFeatures;
@@ -574,17 +614,23 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
574
614
  });
575
615
  _this.updateRedliningState(firstSelection);
576
616
  });
617
+ _defineProperty(_this, "deselectFeature", function (feature, updateState) {
618
+ var styleName = feature.get("shape") === "Text" ? "text" : "default";
619
+ var style = FeatureStyles[styleName](feature, feature.get('styleOptions'));
620
+ feature.setStyle(style);
621
+ feature.un('change', _this.updateMeasurements);
622
+ _this.selectedFeatures = _this.selectedFeatures.filter(function (f) {
623
+ return f !== feature;
624
+ });
625
+ if (updateState) {
626
+ _this.updateRedliningState(false);
627
+ }
628
+ });
577
629
  _defineProperty(_this, "commitFeatures", function (features, redliningProps) {
578
630
  var newFeature = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
579
631
  var featureObjects = features.map(function (feature) {
580
632
  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
- });
633
+ _this.deselectFeature(feature, false);
588
634
  var featureObj = _this.serializeFeature(feature);
589
635
  // Don't commit empty/invalid features
590
636
  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) {
@@ -631,12 +677,9 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
631
677
  _this.commitFeatures(_this.selectedFeatures, redliningProps, false);
632
678
  } else {
633
679
  _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);
680
+ _this.deselectFeature(feature, false);
638
681
  });
639
- _this.selectedFeatures = [];
682
+ _this.updateRedliningState(false);
640
683
  }
641
684
  _this.props.map.un('click', _this.maybeEnterTemporaryDrawMode);
642
685
  _this.picking = false;
@@ -773,6 +816,13 @@ var RedliningSupport = /*#__PURE__*/function (_React$Component) {
773
816
  }));
774
817
  return;
775
818
  }
819
+ if (this.props.redlining.action === 'Clone') {
820
+ this.cloneCurrentFeatures();
821
+ this.props.changeRedliningState(_objectSpread(_objectSpread({}, prevProps.redlining), {}, {
822
+ selectedFeature: null
823
+ }));
824
+ return;
825
+ }
776
826
  if (this.props.redlining.action === 'Export') {
777
827
  this["export"]();
778
828
  this.props.changeRedliningState(_objectSpread(_objectSpread({}, prevProps.redlining), {}, {
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Буфер",
501
501
  "bufferselectfeature": "Изберете функция, която да буферира...",
502
502
  "circle": "Кръг",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Изтриване на",
505
506
  "draw": "Начертайте",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Capa de buffer",
501
501
  "bufferselectfeature": "Seleccioni un element para enmagatzemar en bufer",
502
502
  "circle": "Circle",
503
+ "clone": "Clonar",
503
504
  "ctrlhint": "",
504
505
  "delete": "Eliminar",
505
506
  "draw": "Dibuixar",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "",
501
501
  "bufferselectfeature": "",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "",
505
506
  "draw": "",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Objektpuffer",
501
501
  "bufferselectfeature": "Zu pufferndes Objekt auswählen...",
502
502
  "circle": "Kreis",
503
+ "clone": "Kopieren",
503
504
  "ctrlhint": "Mehrfachselektion mit Ctrl...",
504
505
  "delete": "Löschen",
505
506
  "draw": "Zeichnen",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Objektpuffer",
501
501
  "bufferselectfeature": "Zu pufferndes Objekt auswählen...",
502
502
  "circle": "Kreis",
503
+ "clone": "Kopieren",
503
504
  "ctrlhint": "Mehrfachselektion mit Ctrl...",
504
505
  "delete": "Löschen",
505
506
  "draw": "Zeichnen",
@@ -447,7 +447,7 @@
447
447
  "angle": "Angle",
448
448
  "featureunsupported": "Numeric input unavailable for this geometry",
449
449
  "height": "Height",
450
- "nofeatureormultiple": "",
450
+ "nofeatureormultiple": "No or multiple active features",
451
451
  "side": "Side",
452
452
  "width": "Width",
453
453
  "windowtitle": "Numeric input"
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Buffer",
501
501
  "bufferselectfeature": "Select a feature to buffer...",
502
502
  "circle": "Circle",
503
+ "clone": "Clone",
503
504
  "ctrlhint": "Use Ctrl for multiselection...",
504
505
  "delete": "Delete",
505
506
  "draw": "Draw",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Bufer",
501
501
  "bufferselectfeature": "Seleccione un elemento para almacenar en bufer",
502
502
  "circle": "Círculo",
503
+ "clone": "Clonar",
503
504
  "ctrlhint": "",
504
505
  "delete": "Eliminar",
505
506
  "draw": "Dibujar",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Bufferi",
501
501
  "bufferselectfeature": "Valitse bufferin ominaisuus...",
502
502
  "circle": "Ympyrä",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Poista",
505
506
  "draw": "Piirrä",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Sortie",
501
501
  "bufferselectfeature": "Choisissez une entité pour le tampon...",
502
502
  "circle": "Cercle",
503
+ "clone": "Copier",
503
504
  "ctrlhint": "Ctrl pour sélection multiple...",
504
505
  "delete": "Effacer",
505
506
  "draw": "Dessiner",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "",
501
501
  "bufferselectfeature": "",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "",
505
506
  "draw": "",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Buffer",
501
501
  "bufferselectfeature": "Seleziona un oggetto...",
502
502
  "circle": "Cerchio",
503
+ "clone": "Copia",
503
504
  "ctrlhint": "Ctrl per selezione multipla...",
504
505
  "delete": "Elimina",
505
506
  "draw": "Disegna",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "バッファ",
501
501
  "bufferselectfeature": "バッファを適用する地物を選択...",
502
502
  "circle": "円",
503
+ "clone": "クローン",
503
504
  "ctrlhint": "",
504
505
  "delete": "削除",
505
506
  "draw": "作図",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Laag",
501
501
  "bufferselectfeature": "Selecteer object om te bufferen...",
502
502
  "circle": "Cirkel",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Verwijderen",
505
506
  "draw": "Tekenen",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Buffer",
501
501
  "bufferselectfeature": "Velg et objekt fra buffer",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Slett",
505
506
  "draw": "Tegn",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Buforuj",
501
501
  "bufferselectfeature": "Wybierz element do buforowania",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Usuń",
505
506
  "draw": "Rysuj",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Nome do buffer",
501
501
  "bufferselectfeature": "Selecione uma característica para o buffer...",
502
502
  "circle": "Círculo",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Excluir",
505
506
  "draw": "Traçar",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Nome da Camada de Tampão",
501
501
  "bufferselectfeature": "Selecione uma característica para o tampão...",
502
502
  "circle": "Círculo",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Excluir",
505
506
  "draw": "Desenhar",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Zonă tampon",
501
501
  "bufferselectfeature": "Selectați o entitate...",
502
502
  "circle": "Cerc",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Elimină",
505
506
  "draw": "Desenare",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "",
501
501
  "bufferselectfeature": "",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "",
505
506
  "draw": "",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Buffert",
501
501
  "bufferselectfeature": "Välj ett objekt att buffra",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Radera",
505
506
  "draw": "Rita",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "Tampon",
501
501
  "bufferselectfeature": "Tampon atılacak bir obje seçiniz...",
502
502
  "circle": "Daire",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "Sil",
505
506
  "draw": "Çiz",
@@ -429,6 +429,7 @@
429
429
  "redlining.bufferlayername",
430
430
  "redlining.bufferselectfeature",
431
431
  "redlining.circle",
432
+ "redlining.clone",
432
433
  "redlining.ctrlhint",
433
434
  "redlining.delete",
434
435
  "redlining.draw",
@@ -500,6 +500,7 @@
500
500
  "bufferlayername": "",
501
501
  "bufferselectfeature": "",
502
502
  "circle": "",
503
+ "clone": "",
503
504
  "ctrlhint": "",
504
505
  "delete": "",
505
506
  "draw": "",
@@ -333,14 +333,14 @@ export function computeExpressionFields(editConfig, feature, editIface, mapCrs,
333
333
  } catch (e) {
334
334
  /* eslint-disable-next-line */
335
335
  console.warn("Failed to sort expressions, they probably contain cyclic dependencies");
336
- fieldExpressions = Object.entries(fieldExpressions).map(function (res, _ref4) {
336
+ fieldExpressions = Object.entries(fieldExpressions).map(function (_ref4) {
337
337
  var _ref5 = _slicedToArray(_ref4, 2),
338
338
  field = _ref5[0],
339
339
  expression = _ref5[1];
340
- return [].concat(_toConsumableArray(res), [{
340
+ return {
341
341
  field: field,
342
342
  expression: expression
343
- }]);
343
+ };
344
344
  }, {});
345
345
  }
346
346
  // Evaluate expressions
@@ -350,7 +350,7 @@ export function computeExpressionFields(editConfig, feature, editIface, mapCrs,
350
350
  // Adjust values based on field type
351
351
  editConfig.fields.forEach(function (field) {
352
352
  var _field$constraints2;
353
- if (!((_field$constraints2 = field.constraints) !== null && _field$constraints2 !== void 0 && _field$constraints2.hidden)) {
353
+ if ((_field$constraints2 = field.constraints) !== null && _field$constraints2 !== void 0 && _field$constraints2.hidden) {
354
354
  // Remove hidden fields from result
355
355
  delete result[field.id];
356
356
  } else if (field.id in result && field.type === "date") {
package/utils/MapUtils.js CHANGED
@@ -40,15 +40,6 @@ var MapUtils = {
40
40
  dpi2dpm: function dpi2dpm(dpi) {
41
41
  return (dpi || DEFAULT_SCREEN_DPI) * (100 / 2.54);
42
42
  },
43
- /**
44
- * @param dpi {number} screen resolution in dots per inch.
45
- * @param projection {string} map projection.
46
- * @return {number} dots per map unit.
47
- */
48
- dpi2dpu: function dpi2dpu(dpi, projection) {
49
- var units = CoordinatesUtils.getUnits(projection);
50
- return METERS_PER_UNIT[units] * MapUtils.dpi2dpm(dpi);
51
- },
52
43
  /**
53
44
  * Get a list of scales for each zoom level of the Google Mercator.
54
45
  * @param minZoom {number} min zoom level.
@@ -75,9 +66,9 @@ var MapUtils = {
75
66
  */
76
67
  getResolutionsForScales: function getResolutionsForScales(scales, projection) {
77
68
  var dpi = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : DEFAULT_SCREEN_DPI;
78
- var dpu = MapUtils.dpi2dpu(dpi, projection);
69
+ var units = CoordinatesUtils.getUnits(projection);
79
70
  var resolutions = scales.map(function (scale) {
80
- return scale / dpu;
71
+ return scale * 0.0254 / (dpi * METERS_PER_UNIT[units]);
81
72
  });
82
73
  return resolutions;
83
74
  },