qwc2 2026.3.18 → 2026.3.24

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 (44) hide show
  1. package/components/AttributeTableWidget.js +16 -3
  2. package/components/IdentifyViewer.js +9 -4
  3. package/components/map3d/Map3D.js +31 -2
  4. package/components/map3d/utils/MiscUtils3D.js +1 -1
  5. package/components/widgets/Input.js +2 -1
  6. package/components/widgets/style/ListInput.css +1 -0
  7. package/package.json +5 -2
  8. package/plugins/Editing.js +4 -0
  9. package/plugins/MapExport.js +4 -6
  10. package/plugins/Measure.js +4 -2
  11. package/plugins/Redlining.js +1 -1
  12. package/plugins/SensorThingsTool.js +3022 -0
  13. package/plugins/View3D.js +8 -0
  14. package/plugins/map/MeasurementSupport.js +7 -4
  15. package/plugins/map3d/Settings3D.js +20 -2
  16. package/plugins/map3d/TopBar3D.js +1 -0
  17. package/plugins/style/SensorThingsTool.css +193 -0
  18. package/static/translations/bg-BG.json +96 -0
  19. package/static/translations/ca-ES.json +96 -0
  20. package/static/translations/cs-CZ.json +96 -0
  21. package/static/translations/de-CH.json +96 -0
  22. package/static/translations/de-DE.json +96 -0
  23. package/static/translations/en-US.json +96 -0
  24. package/static/translations/es-ES.json +96 -0
  25. package/static/translations/fi-FI.json +96 -0
  26. package/static/translations/fr-FR.json +96 -0
  27. package/static/translations/hu-HU.json +96 -0
  28. package/static/translations/it-IT.json +96 -0
  29. package/static/translations/ja-JP.json +96 -0
  30. package/static/translations/nl-NL.json +96 -0
  31. package/static/translations/no-NO.json +96 -0
  32. package/static/translations/pl-PL.json +96 -0
  33. package/static/translations/pt-BR.json +96 -0
  34. package/static/translations/pt-PT.json +96 -0
  35. package/static/translations/ro-RO.json +96 -0
  36. package/static/translations/ru-RU.json +96 -0
  37. package/static/translations/sv-SE.json +96 -0
  38. package/static/translations/tr-TR.json +96 -0
  39. package/static/translations/tsconfig.json +78 -0
  40. package/static/translations/uk-UA.json +96 -0
  41. package/utils/QuickHull2D.js +135 -0
  42. package/utils/ServiceLayerUtils.js +4 -1
  43. package/utils/expr_grammar/grammar.js +15 -15
  44. package/utils/expr_grammar/grammar.ne +1 -1
@@ -38,7 +38,7 @@ import { connect } from 'react-redux';
38
38
  import FileSaver from 'file-saver';
39
39
  import isEmpty from 'lodash.isempty';
40
40
  import PropTypes from 'prop-types';
41
- import { LayerRole, addLayerFeatures, removeLayer } from '../actions/layers';
41
+ import { LayerRole, addLayerFeatures, removeLayer, refreshLayer } from '../actions/layers';
42
42
  import { zoomToExtent, zoomToPoint } from '../actions/map';
43
43
  import { setCurrentTask, setCurrentTaskBlocked } from '../actions/task';
44
44
  import EditComboField from '../components/EditComboField';
@@ -485,6 +485,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
485
485
  return newState;
486
486
  }, function () {
487
487
  if (reload) {
488
+ var mapPrefix = _this.state.curEditConfig.editDataset.split(".")[0];
489
+ _this.props.refreshLayer(function (layer) {
490
+ return layer.wms_name === mapPrefix;
491
+ });
488
492
  _this.reload(_this.state.loadedLayer, true);
489
493
  }
490
494
  });
@@ -566,6 +570,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
566
570
  alert(result);
567
571
  } else {
568
572
  _this.changedFiles = {};
573
+ var mapPrefix = _this.state.curEditConfig.editDataset.split(".")[0];
574
+ _this.props.refreshLayer(function (layer) {
575
+ return layer.wms_name === mapPrefix;
576
+ });
569
577
  _this.reload(_this.state.loadedLayer, true, {
570
578
  changedFeatureIdx: null,
571
579
  originalFeatureProps: null,
@@ -1029,7 +1037,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
1029
1037
  return _this2.updateFilter("filterField", ev.target.value);
1030
1038
  },
1031
1039
  value: this.state.filterField
1032
- }, showIdColumn ? /*#__PURE__*/React.createElement("option", {
1040
+ }, /*#__PURE__*/React.createElement("option", {
1041
+ disabled: true,
1042
+ value: ""
1043
+ }, LocaleUtils.tr("common.select")), showIdColumn ? /*#__PURE__*/React.createElement("option", {
1033
1044
  value: "<id>"
1034
1045
  }, this.translateFieldName(primaryKey)) : null, fields.map(function (field) {
1035
1046
  return /*#__PURE__*/React.createElement("option", {
@@ -1239,6 +1250,7 @@ _defineProperty(AttributeTableWidget, "propTypes", {
1239
1250
  mapCrs: PropTypes.string,
1240
1251
  mapScales: PropTypes.array,
1241
1252
  readOnly: PropTypes.bool,
1253
+ refreshLayer: PropTypes.func,
1242
1254
  removeLayer: PropTypes.func,
1243
1255
  setCurrentTask: PropTypes.func,
1244
1256
  setCurrentTaskBlocked: PropTypes.func,
@@ -1279,7 +1291,7 @@ _defineProperty(AttributeTableWidget, "defaultState", {
1279
1291
  originalFeatureProps: null,
1280
1292
  pageSize: 50,
1281
1293
  currentPage: 0,
1282
- filterField: "id",
1294
+ filterField: "",
1283
1295
  filterOp: "~",
1284
1296
  filterVal: "",
1285
1297
  sortField: null,
@@ -1302,6 +1314,7 @@ export default connect(function (state) {
1302
1314
  };
1303
1315
  }, {
1304
1316
  addLayerFeatures: addLayerFeatures,
1317
+ refreshLayer: refreshLayer,
1305
1318
  removeLayer: removeLayer,
1306
1319
  setCurrentTask: setCurrentTask,
1307
1320
  setCurrentTaskBlocked: setCurrentTaskBlocked,
@@ -620,9 +620,11 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
620
620
  var inlineExtaAttribs = false;
621
621
  var featureReports = _this.state.reports[layerid] || [];
622
622
  if (feature.featurereport) {
623
+ var parts = feature.featurereport.split(".");
623
624
  featureReports.push({
624
625
  title: null,
625
- template: feature.featurereport
626
+ template: parts.shift(),
627
+ format: parts.join(".")
626
628
  });
627
629
  }
628
630
  if (feature.type === "text") {
@@ -768,7 +770,7 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
768
770
  }) : null, /*#__PURE__*/React.createElement("span", null, [_this.props.showLayerTitles ? feature.layertitle : "", feature.displayname].filter(Boolean).join(": ")), zoomToFeatureButton, /*#__PURE__*/React.createElement(Icon, {
769
771
  icon: "info-sign",
770
772
  onClick: function onClick() {
771
- return _this.showLayerInfo(layerid);
773
+ return _this.showLayerInfo(layerid, feature.layerinfo);
772
774
  }
773
775
  }), /*#__PURE__*/React.createElement(Icon, {
774
776
  icon: "trash",
@@ -1122,12 +1124,15 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
1122
1124
  });
1123
1125
  });
1124
1126
  });
1125
- _defineProperty(_this, "showLayerInfo", function (layer) {
1127
+ _defineProperty(_this, "showLayerInfo", function (layer, infolayer) {
1126
1128
  var _layer$split = layer.split('#'),
1127
1129
  _layer$split2 = _slicedToArray(_layer$split, 2),
1128
1130
  layerUrl = _layer$split2[0],
1129
1131
  layerName = _layer$split2[1];
1130
- var match = LayerUtils.searchLayer(_this.props.layers, 'url', layerUrl, 'name', layerName);
1132
+ if (!infolayer) {
1133
+ infolayer = layerName;
1134
+ }
1135
+ var match = LayerUtils.searchLayer(_this.props.layers, 'url', layerUrl, 'name', infolayer);
1131
1136
  if (match) {
1132
1137
  _this.props.setActiveLayerInfo(match.layer, match.sublayer);
1133
1138
  }
@@ -52,7 +52,7 @@ import { fromUrl } from "geotiff";
52
52
  import isEmpty from 'lodash.isempty';
53
53
  import PropTypes from 'prop-types';
54
54
  import * as THREE from 'three';
55
- import { Vector2, CubeTextureLoader, Group, Raycaster, Mesh, Box3, Vector3, Matrix4, EventDispatcher } from 'three';
55
+ import { Vector2, CubeTextureLoader, Group, Raycaster, Mesh, Box3, Vector3, Matrix4, EventDispatcher, Points } from 'three';
56
56
  import { computeBoundsTree, disposeBoundsTree, computeBatchedBoundsTree, disposeBatchedBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';
57
57
  import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
58
58
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader';
@@ -147,6 +147,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
147
147
  updateSceneObject: function updateSceneObject(objectId, options, flags) {},
148
148
  zoomToObject: function zoomToObject(objectId) {},
149
149
  objectIsVisible: function objectIsVisible(objectId) {},
150
+ objectContainsPoints: function objectContainsPoints(object) {},
150
151
  getMap: function getMap() {},
151
152
  computeBoundsTree: function computeBoundsTree(object) {},
152
153
  setViewToExtent: function setViewToExtent(bounds, angle) {},
@@ -489,6 +490,9 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
489
490
  scene.userData.tilesetName = objectId;
490
491
  scene.userData.featureIdAttr = "id";
491
492
  Tiles3DStyle.applyTileStyle(scene, tiles.userData, _this2.state.sceneContext);
493
+ if (_this2.objectContainsPoints(tiles)) {
494
+ tiles.pointSize = _this2.state.sceneContext.settings.pointSize;
495
+ }
492
496
  _this2.computeBoundsTree(scene);
493
497
  _this2.instance.notifyChange(tiles);
494
498
  });
@@ -782,6 +786,17 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
782
786
  }
783
787
  return isVisible;
784
788
  });
789
+ _defineProperty(_this2, "objectContainsPoints", function (object) {
790
+ var containsPoints = false;
791
+ if (object !== null && object !== void 0 && object.tiles) {
792
+ object.traverse(function (child) {
793
+ if (child instanceof Points) {
794
+ containsPoints = true;
795
+ }
796
+ });
797
+ }
798
+ return containsPoints;
799
+ });
785
800
  _defineProperty(_this2, "getMap", function () {
786
801
  return _this2.map;
787
802
  });
@@ -999,7 +1014,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
999
1014
  });
1000
1015
 
1001
1016
  // Inspector
1002
- if (["1", "true"].includes((UrlParams.getParam("inspector") || "").toLowerCase())) {
1017
+ if ((process.env.NODE_ENV !== "production" || _this2.props.forceAllowInspector) && ["1", "true"].includes((UrlParams.getParam("inspector") || "").toLowerCase())) {
1003
1018
  var inspectorContainer = document.createElement("div");
1004
1019
  inspectorContainer.className = 'map3d-inspector';
1005
1020
  _this2.container.appendChild(inspectorContainer);
@@ -1368,6 +1383,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1368
1383
  _this2.state.sceneContext.updateSceneObject = _this2.updateSceneObject;
1369
1384
  _this2.state.sceneContext.zoomToObject = _this2.zoomToObject;
1370
1385
  _this2.state.sceneContext.objectIsVisible = _this2.objectIsVisible;
1386
+ _this2.state.sceneContext.objectContainsPoints = _this2.objectContainsPoints;
1371
1387
  _this2.state.sceneContext.getMap = _this2.getMap;
1372
1388
  _this2.state.computeBoundsTree = _this2.computeBoundsTree;
1373
1389
  _this2.state.sceneContext.getTerrainHeightFromDTM = _this2.getTerrainHeightFromDTM;
@@ -1376,6 +1392,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1376
1392
  _this2.state.sceneContext.getSetting = _this2.getSetting;
1377
1393
  _this2.state.sceneContext.setSetting = _this2.setSetting;
1378
1394
  _this2.state.sceneContext.settings.fov = props.defaultFov;
1395
+ _this2.state.sceneContext.settings.pointSize = props.defaultPointSize;
1379
1396
  _this2.state.sceneContext.settings.sceneQuality = props.defaultSceneQuality;
1380
1397
  registerPermalinkDataStoreHook("map3d", _this2.store3dState);
1381
1398
  return _this2;
@@ -1454,6 +1471,15 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1454
1471
  this.instance.view.camera.fov = this.state.sceneContext.settings.fov;
1455
1472
  this.instance.notifyChange(this.instance.view.camera);
1456
1473
  }
1474
+ if (this.state.sceneContext.settings.pointSize !== prevState.sceneContext.settings.pointSize) {
1475
+ Object.values(this.state.sceneContext.objectTree).map(function (entry) {
1476
+ var obj = _this3.state.sceneContext.getSceneObject(entry.objectId);
1477
+ if (_this3.objectContainsPoints(obj)) {
1478
+ obj.pointSize = _this3.state.sceneContext.settings.pointSize;
1479
+ _this3.instance.notifyChange(obj);
1480
+ }
1481
+ });
1482
+ }
1457
1483
  if (this.state.sceneContext.settings.sceneQuality !== prevState.sceneContext.settings.sceneQuality) {
1458
1484
  var quality = Math.max(20, this.state.sceneContext.settings.sceneQuality);
1459
1485
  this.map.segments = Math.pow(2, Math.floor(quality / 20));
@@ -1500,7 +1526,9 @@ _defineProperty(Map3D, "contextType", MapContainerPortalContext);
1500
1526
  _defineProperty(Map3D, "propTypes", {
1501
1527
  controlsPosition: PropTypes.string,
1502
1528
  defaultFov: PropTypes.number,
1529
+ defaultPointSize: PropTypes.number,
1503
1530
  defaultSceneQuality: PropTypes.number,
1531
+ forceAllowInspector: PropTypes.bool,
1504
1532
  innerRef: PropTypes.func,
1505
1533
  layers: PropTypes.array,
1506
1534
  mouseButtons: PropTypes.object,
@@ -1534,6 +1562,7 @@ _defineProperty(Map3D, "defaultSceneState", {
1534
1562
  snapObjects: [],
1535
1563
  settings: {
1536
1564
  fov: 30,
1565
+ pointSize: 0,
1537
1566
  sceneQuality: 100
1538
1567
  },
1539
1568
  sceneId: null,
@@ -21,10 +21,10 @@ function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length)
21
21
  * LICENSE file in the root directory of this source tree.
22
22
  */
23
23
 
24
- import convexHull from 'quick-hull-2d';
25
24
  import { Box3, BufferGeometry, Matrix3, Matrix4, Mesh, Vector2, Vector3 } from 'three';
26
25
  import { MeshLine, MeshLineMaterial } from 'three.meshline';
27
26
  import { CSS2DObject } from "three/addons/renderers/CSS2DRenderer";
27
+ import convexHull from '../../../utils/QuickHull2D';
28
28
  export function createLabelObject(text, pos, sceneContext, zoffset) {
29
29
  var yoffset = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
30
30
  var labelEl = document.createElement("span");
@@ -98,7 +98,8 @@ var Input = /*#__PURE__*/function (_React$Component) {
98
98
  }], [{
99
99
  key: "getDerivedStateFromProps",
100
100
  value: function getDerivedStateFromProps(nextProps, state) {
101
- var strValue = String(nextProps.value || "");
101
+ var _nextProps$value;
102
+ var strValue = String((_nextProps$value = nextProps.value) !== null && _nextProps$value !== void 0 ? _nextProps$value : "");
102
103
  if (state.value !== strValue) {
103
104
  return {
104
105
  value: strValue,
@@ -3,6 +3,7 @@ div.ListInput {
3
3
  background-color: var(--list-bg-color);
4
4
  padding: 0.25em;
5
5
  width: 100%;
6
+ min-height: 2em;
6
7
  }
7
8
 
8
9
  div.list-input-value {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2026.03.18",
3
+ "version": "2026.03.24",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -32,7 +32,10 @@
32
32
  "@vtaits/react-color-picker": "^2.0.0",
33
33
  "any-date-parser": "^1.5.4",
34
34
  "axios": "^1.13.4",
35
+ "buffer": "^6.0.3",
35
36
  "chart.js": "^4.5.1",
37
+ "chartjs-adapter-dayjs-4": "^1.0.4",
38
+ "chartjs-plugin-annotation": "^3.1.0",
36
39
  "classnames": "^2.5.1",
37
40
  "clone": "^2.1.2",
38
41
  "dayjs": "^1.11.19",
@@ -71,7 +74,6 @@
71
74
  "proj4": "^2.20.2",
72
75
  "prop-types": "^15.8.1",
73
76
  "qrcode.react": "^4.2.0",
74
- "quick-hull-2d": "^0.1.0",
75
77
  "randomcolor": "^0.6.2",
76
78
  "react": "^19.2.4",
77
79
  "react-chartjs-2": "^5.3.1",
@@ -85,6 +87,7 @@
85
87
  "react-swipeable": "^7.0.2",
86
88
  "redux-logger": "^3.0.6",
87
89
  "reselect": "^5.1.1",
90
+ "robust-orientation": "^1.2.1",
88
91
  "simplepolygon": "^2.0.4",
89
92
  "sortablejs": "^1.15.6",
90
93
  "suncalc": "^1.9.0",
@@ -618,6 +618,10 @@ var Editing = /*#__PURE__*/function (_React$Component) {
618
618
  })) {
619
619
  return null;
620
620
  }
621
+ var match = LayerUtils.searchLayer(_this2.props.layers, 'wms_name', mapName, 'name', layerName);
622
+ if (!match) {
623
+ return null;
624
+ }
621
625
  return mapName + "#" + layerName;
622
626
  });
623
627
  }).flat().filter(Boolean);
@@ -45,6 +45,7 @@ import { setSnappingConfig } from '../actions/map';
45
45
  import Icon from '../components/Icon';
46
46
  import PrintSelection from '../components/PrintSelection';
47
47
  import SideBar from '../components/SideBar';
48
+ import ComboBox from '../components/widgets/ComboBox';
48
49
  import NumberInput from '../components/widgets/NumberInput';
49
50
  import Spinner from '../components/widgets/Spinner';
50
51
  import ConfigUtils from '../utils/ConfigUtils';
@@ -137,14 +138,11 @@ var MapExport = /*#__PURE__*/function (_React$Component) {
137
138
  });
138
139
  var scaleChooser = null;
139
140
  if (!isEmpty(_this.props.allowedScales)) {
140
- scaleChooser = /*#__PURE__*/React.createElement("select", {
141
+ scaleChooser = /*#__PURE__*/React.createElement(ComboBox, {
141
142
  onChange: _this.changeScale,
142
143
  value: _this.state.scale || ""
143
- }, /*#__PURE__*/React.createElement("option", {
144
- hidden: true,
145
- value: _this.state.scale || ""
146
- }, _this.state.scale || ""), _this.props.allowedScales.map(function (scale) {
147
- return /*#__PURE__*/React.createElement("option", {
144
+ }, _this.props.allowedScales.map(function (scale) {
145
+ return /*#__PURE__*/React.createElement("div", {
148
146
  key: scale,
149
147
  value: scale
150
148
  }, "1 : ", scale);
@@ -60,11 +60,13 @@ var Measure = /*#__PURE__*/function (_React$Component) {
60
60
  if (_this.props.clearMeasurementsOnExit) {
61
61
  _this.props.changeMeasurementState({
62
62
  mode: 'Reset',
63
- nextmode: null
63
+ nextmode: null,
64
+ measureId: null
64
65
  });
65
66
  } else {
66
67
  _this.props.changeMeasurementState({
67
- mode: null
68
+ mode: null,
69
+ measureId: null
68
70
  });
69
71
  }
70
72
  });
@@ -534,7 +534,7 @@ var Redlining = /*#__PURE__*/function (_React$Component) {
534
534
  text: LocaleUtils.tr("common.text")
535
535
  }
536
536
  });
537
- } else if (!_this.props.allowGeometryLabels) {
537
+ } else if (!_this.props.allowGeometryLabels || _this.props.redlining.geomType === "Text" && data.action === "Draw" && data.geomType !== "Text") {
538
538
  data = _objectSpread(_objectSpread({}, data), {}, {
539
539
  style: {
540
540
  text: ''