qwc2 2026.2.23 → 2026.2.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.
package/actions/theme.js CHANGED
@@ -163,7 +163,7 @@ export function setCurrentTheme(theme, themes) {
163
163
  var themeLayerRestorer = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : null;
164
164
  var externalLayerRestorer = arguments.length > 8 && arguments[8] !== undefined ? arguments[8] : null;
165
165
  return function (dispatch, getState) {
166
- var _getState$layers;
166
+ var _getState$layers, _ref, _theme$mapTips;
167
167
  var curLayers = ((_getState$layers = getState().layers) === null || _getState$layers === void 0 ? void 0 : _getState$layers.flat) || [];
168
168
  var mapCrs = theme.mapCrs || themes.defaultMapCrs || "EPSG:3857";
169
169
  if (!(mapCrs in CoordinatesUtils.getAvailableCRS())) {
@@ -230,6 +230,7 @@ export function setCurrentTheme(theme, themes) {
230
230
  printGrid: theme.printGrid || themes.defaultPrintGrid || undefined,
231
231
  searchProviders: theme.searchProviders || themes.defaultSearchProviders || undefined,
232
232
  backgroundLayers: theme.backgroundLayers || themes.defaultBackgroundLayers || [],
233
+ mapTips: (_ref = (_theme$mapTips = theme.mapTips) !== null && _theme$mapTips !== void 0 ? _theme$mapTips : themes.defaultMapTips) !== null && _ref !== void 0 ? _ref : undefined,
233
234
  defaultDisplayCrs: theme.defaultDisplayCrs || themes.defaultDisplayCrs || undefined
234
235
  });
235
236
 
@@ -138,8 +138,8 @@ export default {
138
138
  }, options.sourceConfig || {}))
139
139
  }, options.layerConfig || {}));
140
140
  }
141
- layer.setVisible(queryParameters.LAYERS && options.visibility);
142
- layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 100);
141
+ layer.setVisible(queryParameters.LAYERS !== "" && options.visibility);
142
+ layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1);
143
143
  return layer;
144
144
  },
145
145
  update: function update(layer, newOptions, oldOptions) {
@@ -147,7 +147,7 @@ export default {
147
147
  if (oldOptions && layer !== null && layer !== void 0 && (_layer$getSource = layer.getSource()) !== null && _layer$getSource !== void 0 && _layer$getSource.updateParams) {
148
148
  var changed = (oldOptions.rev || 0) !== (newOptions.rev || 0);
149
149
  var oldParams = wmsToOpenlayersOptions(oldOptions);
150
- getClientSideOpacity(oldOptions, oldParams);
150
+ getClientSideOpacity(oldOptions, oldParams); // Make getClientSideOpacity transform oldParams if necessary
151
151
  var newParams = wmsToOpenlayersOptions(newOptions);
152
152
  var clientSideOpacity = getClientSideOpacity(newOptions, newParams);
153
153
  Object.keys(oldParams).forEach(function (key) {
@@ -168,18 +168,18 @@ export default {
168
168
  if (layer.get("updateTimeout")) {
169
169
  clearTimeout(layer.get("updateTimeout"));
170
170
  }
171
- if (!newOptions.visibility || !queryParameters.LAYERS) {
171
+ if (!newOptions.visibility || queryParameters.LAYERS === "") {
172
172
  layer.setVisible(false);
173
173
  }
174
- layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 100);
174
+ layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1);
175
175
  layer.set("updateTimeout", setTimeout(function () {
176
- layer.setVisible(queryParameters.LAYERS && newOptions.visibility);
176
+ layer.setVisible(queryParameters.LAYERS !== "" && newOptions.visibility);
177
177
  layer.getSource().updateParams(queryParameters);
178
178
  layer.getSource().changed();
179
179
  layer.set("updateTimeout", null);
180
180
  }, 500));
181
181
  } else {
182
- layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 100);
182
+ layer.setOpacity(clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1);
183
183
  }
184
184
  }
185
185
  }
@@ -115,7 +115,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
115
115
  addLayer: function addLayer(layer) {},
116
116
  getLayer: function getLayer(layerId) {},
117
117
  removeLayer: function removeLayer(layerId) {},
118
- updateColorLayer: function updateColorLayer(layerId, options, path) {},
118
+ updateColorLayer: function updateColorLayer(layerId, options, flags) {},
119
119
  setBaseLayer: function setBaseLayer(layer, visibility) {},
120
120
  importTiles3D: function importTiles3D(url, name) {
121
121
  var addToLayerTree = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
@@ -136,7 +136,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
136
136
  },
137
137
  getSceneObject: function getSceneObject(objectId) {},
138
138
  removeSceneObject: function removeSceneObject(objectId) {},
139
- updateSceneObject: function updateSceneObject(objectId, options) {},
139
+ updateSceneObject: function updateSceneObject(objectId, options, flags) {},
140
140
  zoomToObject: function zoomToObject(objectId) {},
141
141
  getMap: function getMap() {},
142
142
  setViewToExtent: function setViewToExtent(bounds, angle) {},
@@ -199,7 +199,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
199
199
  return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, layer.id, layer));
200
200
  }, {});
201
201
  return _this2.props.layers.reduce(function (colorLayers, layer) {
202
- var _prevOptions$visibili, _prevOptions$opacity, _prevOptions$extrusio, _prevOptions$fields;
202
+ var _prevOptions$projecti, _prevOptions$visibili, _prevOptions$opacity, _prevOptions$extrusio, _prevOptions$fields;
203
203
  if (layer.role !== LayerRole.THEME && layer.role !== LayerRole.USERLAYER) {
204
204
  return colorLayers;
205
205
  }
@@ -231,6 +231,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
231
231
  });
232
232
  };
233
233
  colorLayers[layer.id] = _objectSpread(_objectSpread({}, layer), {}, {
234
+ projection: (_prevOptions$projecti = prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.projection) !== null && _prevOptions$projecti !== void 0 ? _prevOptions$projecti : _this2.props.theme.mapCrs,
234
235
  visibility: (_prevOptions$visibili = prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.visibility) !== null && _prevOptions$visibili !== void 0 ? _prevOptions$visibili : false,
235
236
  opacity: (_prevOptions$opacity = prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.opacity) !== null && _prevOptions$opacity !== void 0 ? _prevOptions$opacity : 255,
236
237
  extrusionHeight: (_prevOptions$extrusio = prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.extrusionHeight) !== null && _prevOptions$extrusio !== void 0 ? _prevOptions$extrusio : layerCreator.createFeatureSource ? 0 : undefined,
@@ -262,14 +263,17 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
262
263
  var layerCreator = LayerRegistry[options.type];
263
264
  var mapLayer = _this2.getLayer(layerId);
264
265
  if (mapLayer) {
265
- layerCreator.update3d(mapLayer.source, options, prevOptions, _this2.state.sceneContext.mapCrs);
266
+ layerCreator.update3d(mapLayer, options, prevOptions, _this2.state.sceneContext.mapCrs);
266
267
  } else {
267
268
  mapLayer = layerCreator.create3d(options, _this2.state.sceneContext.mapCrs);
268
269
  _this2.addLayer(layerId, mapLayer);
269
270
  }
270
271
  _this2.map.insertLayerAfter(mapLayer, layerBelow);
271
- mapLayer.visible = options.visibility;
272
- mapLayer.opacity = options.opacity / 255;
272
+ // WMS layer handles visibility and opacity internally
273
+ if (options.type !== "wms") {
274
+ mapLayer.visible = options.visibility;
275
+ mapLayer.opacity = options.opacity / 255;
276
+ }
273
277
  layerBelow = mapLayer;
274
278
  if (options.extrusionHeight !== undefined && options.extrusionHeight !== 0) {
275
279
  _this2.createUpdateExtrudedLayer(layerCreator, mapLayer, options, options.features !== (prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.features));
@@ -381,16 +385,45 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
381
385
  });
382
386
  });
383
387
  _defineProperty(_this2, "updateColorLayer", function (layerId, options) {
384
- var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : [];
388
+ var flags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
385
389
  _this2.setState(function (state) {
390
+ var _flags$path;
386
391
  var entry = _objectSpread({}, state.sceneContext.colorLayers[layerId]);
387
392
  var subentry = entry;
388
- path.forEach(function (idx) {
393
+ var parent = null;
394
+ ((_flags$path = flags.path) !== null && _flags$path !== void 0 ? _flags$path : []).forEach(function (idx) {
389
395
  subentry.sublayers = _toConsumableArray(subentry.sublayers);
390
396
  subentry.sublayers[idx] = _objectSpread({}, subentry.sublayers[idx]);
397
+ parent = subentry;
391
398
  subentry = subentry.sublayers[idx];
392
399
  });
400
+ var prevOptions = _objectSpread({}, subentry);
393
401
  Object.assign(subentry, options);
402
+ if (subentry.visibility !== prevOptions.visibility) {
403
+ var _parent;
404
+ if (!isEmpty(subentry.sublayers) && flags.groupTogglesSublayers) {
405
+ // Propagate visibility to children
406
+ var _setChildVisibilities = function setChildVisibilities(child) {
407
+ child.sublayers = child.sublayers.map(function (gchild) {
408
+ var ngchild = _objectSpread(_objectSpread({}, gchild), {}, {
409
+ visibility: options.visibility
410
+ });
411
+ if (!isEmpty(ngchild.sublayers)) {
412
+ _setChildVisibilities(ngchild);
413
+ }
414
+ return ngchild;
415
+ });
416
+ };
417
+ _setChildVisibilities(subentry);
418
+ }
419
+ if ((_parent = parent) !== null && _parent !== void 0 && _parent.mutuallyExclusive) {
420
+ parent.sublayers = parent.sublayers.map(function (sublayer) {
421
+ return sublayer === subentry ? subentry : _objectSpread(_objectSpread({}, sublayer), {}, {
422
+ visibility: false
423
+ });
424
+ });
425
+ }
426
+ }
394
427
  Object.assign(entry, LayerUtils.buildWMSLayerParams(entry));
395
428
  return {
396
429
  sceneContext: _objectSpread(_objectSpread({}, state.sceneContext), {}, {
@@ -593,18 +626,19 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
593
626
  }
594
627
  });
595
628
  _defineProperty(_this2, "updateSceneObject", function (objectId, options) {
629
+ var flags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
596
630
  _this2.setState(function (state) {
597
- var objectTree = state.sceneContext.objectTree;
631
+ var objectTree = _objectSpread({}, state.sceneContext.objectTree);
598
632
  var prevOptions = objectTree[objectId] || {};
599
633
  options = _objectSpread(_objectSpread({}, prevOptions), options);
600
634
  if (options.objectId) {
601
635
  _this2.applySceneObjectState(objectId, options, prevOptions, objectTree);
602
- } else if (options.children) {
603
- _this2.setChildObjectVisibility(objectTree, options.children, options.visibility);
636
+ } else if (options.children && options.visibility !== prevOptions.visibility) {
637
+ _this2.setChildObjectVisibility(objectTree, options.children, options.visibility, flags.groupTogglesSublayers);
604
638
  }
605
639
  return {
606
640
  sceneContext: _objectSpread(_objectSpread({}, state.sceneContext), {}, {
607
- objectTree: _objectSpread(_objectSpread({}, state.sceneContext.objectTree), {}, _defineProperty({}, objectId, options))
641
+ objectTree: _objectSpread(_objectSpread({}, objectTree), {}, _defineProperty({}, objectId, options))
608
642
  })
609
643
  };
610
644
  });
@@ -613,6 +647,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
613
647
  var object = _this2.objectMap[objectId];
614
648
  var changed = false;
615
649
  if (options.visibility !== prevOptions.visibility || options.opacity !== (prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.opacity)) {
650
+ var _objectTree$options$p;
616
651
  // Visibile if object is visibile and parents also
617
652
  var isVisible = options.opacity > 0 && options.visibility;
618
653
  for (var curId = options.parent; isVisible && curId !== undefined; curId = objectTree[curId].parent) {
@@ -620,6 +655,15 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
620
655
  }
621
656
  changed |= object.visible !== isVisible;
622
657
  object.visible = isVisible;
658
+ if ((_objectTree$options$p = objectTree[options.parent]) !== null && _objectTree$options$p !== void 0 && _objectTree$options$p.mutuallyExclusive && objectTree[options.parent].children) {
659
+ objectTree[options.parent].children.forEach(function (child) {
660
+ if (child !== objectId) {
661
+ objectTree[child] = _objectSpread(_objectSpread({}, objectTree[child]), {}, {
662
+ visibility: false
663
+ });
664
+ }
665
+ });
666
+ }
623
667
  }
624
668
  if (options.opacity !== (prevOptions === null || prevOptions === void 0 ? void 0 : prevOptions.opacity)) {
625
669
  if (object.opacity !== undefined) {
@@ -643,6 +687,26 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
643
687
  _this2.loadTilesetStyle(objectId, (_options$styles = options.styles) === null || _options$styles === void 0 ? void 0 : _options$styles[options.style]);
644
688
  }
645
689
  });
690
+ _defineProperty(_this2, "setChildObjectVisibility", function (objectTree, children, visibility, groupTogglesSublayers) {
691
+ children.forEach(function (nodeId) {
692
+ if (objectTree[nodeId].children) {
693
+ _this2.setChildObjectVisibility(objectTree, objectTree[nodeId].children, visibility && objectTree[nodeId].visibility);
694
+ } else {
695
+ if (groupTogglesSublayers) {
696
+ objectTree[nodeId] = _objectSpread(_objectSpread({}, objectTree[nodeId]), {}, {
697
+ visibility: visibility
698
+ });
699
+ }
700
+ var child = objectTree[nodeId];
701
+ var newVisible = visibility && child.visibility && child.opacity > 0;
702
+ var changed = newVisible !== _this2.objectMap[child.objectId].visible;
703
+ _this2.objectMap[child.objectId].visible = newVisible;
704
+ if (changed) {
705
+ _this2.instance.notifyChange(_this2.objectMap[child.objectId]);
706
+ }
707
+ }
708
+ });
709
+ });
646
710
  _defineProperty(_this2, "loadTilesetStyle", function (objectId, url) {
647
711
  var applyTilesetStyle = function applyTilesetStyle(styleurl) {
648
712
  var tiles3d = _this2.objectMap[objectId];
@@ -668,21 +732,6 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
668
732
  applyTilesetStyle(url);
669
733
  }
670
734
  });
671
- _defineProperty(_this2, "setChildObjectVisibility", function (objectTree, children, visibility) {
672
- children.forEach(function (nodeId) {
673
- if (objectTree[nodeId].children) {
674
- _this2.setChildObjectVisibility(objectTree, objectTree[nodeId].children, visibility && objectTree[nodeId].visibility);
675
- } else {
676
- var child = objectTree[nodeId];
677
- var newVisible = visibility && child.visibility && child.opacity > 0;
678
- var changed = newVisible !== _this2.objectMap[child.objectId].visible;
679
- _this2.objectMap[child.objectId].visible = newVisible;
680
- if (changed) {
681
- _this2.instance.notifyChange(_this2.objectMap[child.objectId]);
682
- }
683
- }
684
- });
685
- });
686
735
  _defineProperty(_this2, "zoomToObject", function (objectId) {
687
736
  var margin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 20;
688
737
  var obj = _this2.state.sceneContext.getSceneObject(objectId);
@@ -704,12 +753,14 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
704
753
  if (el) {
705
754
  _this2.container = el;
706
755
  el.resizeObserver = new ResizeObserver(function (entries) {
707
- var rect = entries[0].contentRect;
708
- _this2.state.sceneContext.scene.view.dispatchEvent({
709
- type: 'view-resized',
710
- width: rect.width,
711
- height: rect.height
712
- });
756
+ if (_this2.state.sceneContext.scene) {
757
+ var rect = entries[0].contentRect;
758
+ _this2.state.sceneContext.scene.view.dispatchEvent({
759
+ type: 'view-resized',
760
+ width: rect.width,
761
+ height: rect.height
762
+ });
763
+ }
713
764
  });
714
765
  el.resizeObserver.observe(el);
715
766
  _this2.setupInstance();
@@ -841,12 +892,14 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
841
892
  var nodeIds = [];
842
893
  entries.forEach(function (entry) {
843
894
  if (entry.type === "group") {
844
- var _entry$visibility;
895
+ var _entry$visibility, _entry$mutuallyExclus, _entry$expanded;
845
896
  var groupId = uuidv4();
846
897
  objectTree[groupId] = {
847
898
  parent: parentId,
848
899
  title: entry.title,
849
- visibility: (_entry$visibility = entry.visibility) !== null && _entry$visibility !== void 0 ? _entry$visibility : true
900
+ visibility: (_entry$visibility = entry.visibility) !== null && _entry$visibility !== void 0 ? _entry$visibility : true,
901
+ mutuallyExclusive: (_entry$mutuallyExclus = entry.mutuallyExclusive) !== null && _entry$mutuallyExclus !== void 0 ? _entry$mutuallyExclus : false,
902
+ expanded: (_entry$expanded = entry.expanded) !== null && _entry$expanded !== void 0 ? _entry$expanded : true
850
903
  };
851
904
  // Need this separately to ensure object[groupId] is already assigned
852
905
  objectTree[groupId].children = _buildObjectTree(entry.items, groupId);
@@ -1198,7 +1251,8 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1198
1251
  options: {
1199
1252
  visibility: options.visibility,
1200
1253
  opacity: options.opacity,
1201
- extrusionHeight: options.extrusionHeight
1254
+ extrusionHeight: options.extrusionHeight,
1255
+ expanded: options.expanded
1202
1256
  }
1203
1257
  };
1204
1258
  });
@@ -1307,7 +1361,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1307
1361
  };
1308
1362
  });
1309
1363
  }
1310
- } else if (this.props.layers !== prevProps.layers) {
1364
+ } else if (this.props.layers !== prevProps.layers && this.instance) {
1311
1365
  this.setState(function (state) {
1312
1366
  return {
1313
1367
  sceneContext: _objectSpread(_objectSpread({}, state.sceneContext), {}, {
@@ -36,15 +36,16 @@ export default {
36
36
  });
37
37
  },
38
38
  update3d: function update3d(layer, newOptions, oldOptions, projection) {
39
+ var source = layer.source.source;
39
40
  if (newOptions.styleName !== oldOptions.styleName || newOptions.styleOptions !== oldOptions.styleOptions) {
40
- layer.source.setStyle(featureStyleFunction(newOptions));
41
+ source.setStyle(featureStyleFunction(newOptions));
41
42
  } else if (newOptions.styleFunction !== oldOptions.styleFunction) {
42
- layer.source.setStyle(newOptions.styleFunction);
43
+ source.setStyle(newOptions.styleFunction);
43
44
  }
44
45
  if (newOptions.features !== oldOptions.features) {
45
- updateFeatures(layer.source, newOptions, oldOptions, projection, true);
46
+ updateFeatures(source, newOptions, oldOptions, projection, true);
46
47
  } else if ((oldOptions.rev || 0) !== (newOptions.rev || 0)) {
47
- layer.source.update();
48
+ source.update();
48
49
  }
49
50
  },
50
51
  getFields: function getFields(options) {
@@ -15,14 +15,16 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
15
15
  import ColorLayer from '@giro3d/giro3d/core/layer/ColorLayer';
16
16
  import TiledImageSource from '@giro3d/giro3d/sources/TiledImageSource.js';
17
17
  import ol from 'openlayers';
18
- import { wmsImageLoadFunction, wmsToOpenlayersOptions } from '../../map/layers/WMSLayer';
18
+ import { wmsImageLoadFunction, wmsToOpenlayersOptions, getClientSideOpacity } from '../../map/layers/WMSLayer';
19
19
  export default {
20
20
  create3d: function create3d(options, projection) {
21
21
  var queryParameters = _objectSpread(_objectSpread({}, wmsToOpenlayersOptions(options)), {}, {
22
22
  __t: +new Date()
23
23
  });
24
- return new ColorLayer({
24
+ var clientSideOpacity = getClientSideOpacity(options, queryParameters);
25
+ var layer = new ColorLayer({
25
26
  name: options.name,
27
+ opacity: clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1,
26
28
  source: new TiledImageSource({
27
29
  source: new ol.source.TileWMS({
28
30
  url: options.url.split("?")[0],
@@ -35,13 +37,17 @@ export default {
35
37
  })
36
38
  })
37
39
  });
40
+ layer.visible = queryParameters.LAYERS !== "" && options.visibility;
41
+ return layer;
38
42
  },
39
43
  update3d: function update3d(layer, newOptions, oldOptions, projection) {
40
- var _layer$source;
41
- if (oldOptions && layer !== null && layer !== void 0 && (_layer$source = layer.source) !== null && _layer$source !== void 0 && _layer$source.updateParams) {
44
+ var source = layer.source.source;
45
+ if (oldOptions && source.updateParams) {
42
46
  var changed = (oldOptions.rev || 0) !== (newOptions.rev || 0);
43
47
  var oldParams = wmsToOpenlayersOptions(oldOptions);
48
+ getClientSideOpacity(oldOptions, oldParams); // Make getClientSideOpacity transform oldParams if necessary
44
49
  var newParams = wmsToOpenlayersOptions(newOptions);
50
+ var clientSideOpacity = getClientSideOpacity(newOptions, newParams);
45
51
  Object.keys(oldParams).forEach(function (key) {
46
52
  if (!(key in newParams)) {
47
53
  newParams[key] = undefined;
@@ -60,15 +66,18 @@ export default {
60
66
  if (layer.__updateTimeout) {
61
67
  clearTimeout(layer.__updateTimeout);
62
68
  }
63
- if (!newOptions.visibility || !queryParameters.LAYERS) {
69
+ if (!newOptions.visibility || queryParameters.LAYERS === "") {
64
70
  layer.visible = false;
65
71
  }
72
+ layer.opacity = clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1;
66
73
  layer.__updateTimeout = setTimeout(function () {
67
- layer.visible = queryParameters.LAYERS && newOptions.visibility;
68
- layer.source.updateParams(queryParameters);
69
- layer.update();
74
+ layer.visible = queryParameters.LAYERS !== "" && newOptions.visibility;
75
+ source.updateParams(queryParameters);
76
+ layer.source.update();
70
77
  layer.__updateTimeout = null;
71
78
  }, 500);
79
+ } else {
80
+ layer.opacity = clientSideOpacity !== null && clientSideOpacity !== void 0 ? clientSideOpacity : 1;
72
81
  }
73
82
  }
74
83
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2026.02.23",
3
+ "version": "2026.02.24",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -1,4 +1,6 @@
1
1
  function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
2
+ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
3
+ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
2
4
  function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
3
5
  function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
4
6
  function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
@@ -40,6 +42,7 @@ import Icon from '../../components/Icon';
40
42
  import ImportObjects3D from '../../components/map3d/ImportObjects3D';
41
43
  import SideBar from '../../components/SideBar';
42
44
  import NumberInput from '../../components/widgets/NumberInput';
45
+ import LayerUtils from '../../utils/LayerUtils';
43
46
  import LocaleUtils from '../../utils/LocaleUtils';
44
47
  import './style/LayerTree3D.css';
45
48
 
@@ -61,19 +64,25 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
61
64
  });
62
65
  _defineProperty(_this, "renderBody", function () {
63
66
  var sceneContext = _this.props.sceneContext;
67
+ var objectsHaveGroups = sceneContext.objectTree["null"].children.some(function (child) {
68
+ return !isEmpty(sceneContext.objectTree[child].children);
69
+ });
70
+ var layersHaveSublayers = Object.values(sceneContext.colorLayers).some(function (layer) {
71
+ return !isEmpty(layer.sublayers);
72
+ });
64
73
  return /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
65
74
  className: "layertree3d-layers"
66
75
  }, /*#__PURE__*/React.createElement("div", {
67
76
  className: "layertree3d-section"
68
77
  }, LocaleUtils.tr("layertree3d.objects")), sceneContext.objectTree["null"].children.map(function (objectId) {
69
- return _this.renderLayerEntry(objectId, sceneContext.objectTree[objectId], sceneContext.updateSceneObject, true);
78
+ return _this.renderLayerEntry(objectId, sceneContext.objectTree[objectId], null, _this.updateSceneObject, true, true, [], objectsHaveGroups);
70
79
  }), /*#__PURE__*/React.createElement("div", {
71
80
  className: "layertree3d-section"
72
81
  }, LocaleUtils.tr("layertree3d.layers")), Object.entries(sceneContext.colorLayers).map(function (_ref) {
73
82
  var _ref2 = _slicedToArray(_ref, 2),
74
83
  layerId = _ref2[0],
75
84
  entry = _ref2[1];
76
- return _this.renderLayerEntry(layerId, entry, sceneContext.updateColorLayer, false);
85
+ return _this.renderLayerEntry(layerId, entry, null, _this.updateColorLayer, false, true, [], layersHaveSublayers);
77
86
  }), /*#__PURE__*/React.createElement("div", {
78
87
  className: "layertree3d-option",
79
88
  onClick: function onClick() {
@@ -90,45 +99,72 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
90
99
  sceneContext: _this.props.sceneContext
91
100
  }) : null);
92
101
  });
93
- _defineProperty(_this, "renderLayerEntry", function (entryId, entry, updateCallback, isObject) {
102
+ _defineProperty(_this, "renderLayerEntry", function (entryId, entry, parent, updateCallback, isObject, parentVisible, path) {
94
103
  var _entry$title, _entry$title2;
95
- var parentVisible = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
104
+ var haveExpanders = arguments.length > 7 && arguments[7] !== undefined ? arguments[7] : true;
96
105
  if (entry.layertree === false) {
97
106
  return null;
98
107
  }
108
+ var key = [entryId].concat(_toConsumableArray(path)).join(":");
99
109
  var classes = classNames({
100
110
  "layertree3d-item": true,
101
111
  "layertree3d-item-disabled": !entry.visibility || !parentVisible
102
112
  });
103
113
  var styleMenuClasses = classNames({
104
114
  "layertree3d-item-menubutton": true,
105
- "layertree3d-item-menubutton-active": _this.state.activestylemenu === entryId
115
+ "layertree3d-item-menubutton-active": _this.state.activestylemenu === key
106
116
  });
107
117
  var optMenuClasses = classNames({
108
118
  "layertree3d-item-menubutton": true,
109
- "layertree3d-item-menubutton-active": _this.state.activemenu === entryId
119
+ "layertree3d-item-menubutton-active": _this.state.activemenu === key
110
120
  });
121
+ var expander = null;
122
+ if (haveExpanders) {
123
+ expander = !isEmpty(entry.sublayers) || !isEmpty(entry.children) ? /*#__PURE__*/React.createElement(Icon, {
124
+ className: "layertree3d-item-expander",
125
+ icon: entry.expanded === false ? "tree_plus" : "tree_minus",
126
+ onClick: function onClick() {
127
+ var _entry$expanded;
128
+ return updateCallback(entryId, {
129
+ expanded: !((_entry$expanded = entry.expanded) !== null && _entry$expanded !== void 0 ? _entry$expanded : true)
130
+ }, {
131
+ path: path
132
+ });
133
+ }
134
+ }) : /*#__PURE__*/React.createElement("span", {
135
+ className: "layertree3d-item-expander"
136
+ });
137
+ }
111
138
  return /*#__PURE__*/React.createElement("div", {
112
139
  className: "layertree3d-item-container",
113
- key: entryId
140
+ key: key
114
141
  }, /*#__PURE__*/React.createElement("div", {
115
142
  className: classes
116
- }, /*#__PURE__*/React.createElement(Icon, {
143
+ }, expander, /*#__PURE__*/React.createElement(Icon, {
117
144
  className: "layertree3d-item-checkbox",
118
- icon: entry.visibility ? "checked" : "unchecked",
145
+ icon: _this.computeVisibilityIcon(isObject, entry, parent),
119
146
  onClick: function onClick() {
120
147
  return updateCallback(entryId, {
121
148
  visibility: !entry.visibility
149
+ }, {
150
+ path: path
122
151
  });
123
152
  }
124
153
  }), /*#__PURE__*/React.createElement("span", {
125
154
  className: "layertree3d-item-title",
155
+ onClick: function onClick() {
156
+ return updateCallback(entryId, {
157
+ visibility: !entry.visibility
158
+ }, {
159
+ path: path
160
+ });
161
+ },
126
162
  title: (_entry$title = entry.title) !== null && _entry$title !== void 0 ? _entry$title : entryId
127
163
  }, (_entry$title2 = entry.title) !== null && _entry$title2 !== void 0 ? _entry$title2 : entryId), Object.keys(entry.styles || {}).length > 1 ? /*#__PURE__*/React.createElement(Icon, {
128
164
  className: styleMenuClasses,
129
165
  icon: "paint",
130
166
  onClick: function onClick() {
131
- return _this.layerStyleMenuToggled(entryId);
167
+ return _this.layerStyleMenuToggled(key);
132
168
  }
133
169
  }) : null, entry.drawGroup || entry.imported ? /*#__PURE__*/React.createElement(Icon, {
134
170
  className: "layertree3d-item-remove",
@@ -140,9 +176,9 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
140
176
  className: optMenuClasses,
141
177
  icon: "cog",
142
178
  onClick: function onClick() {
143
- return _this.layerMenuToggled(entryId);
179
+ return _this.layerMenuToggled(key);
144
180
  }
145
- })), _this.state.activemenu === entryId ? /*#__PURE__*/React.createElement("div", {
181
+ })), _this.state.activemenu === key ? /*#__PURE__*/React.createElement("div", {
146
182
  className: "layertree3d-item-optionsmenu"
147
183
  }, /*#__PURE__*/React.createElement("div", {
148
184
  className: "layertree3d-item-optionsmenu-row"
@@ -166,12 +202,25 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
166
202
  onChange: function onChange(ev) {
167
203
  return updateCallback(entryId, {
168
204
  opacity: parseInt(ev.target.value, 10)
205
+ }, {
206
+ path: path
169
207
  });
170
208
  },
171
209
  step: "1",
172
210
  type: "range",
173
211
  value: entry.opacity
174
- })), entry.extrusionHeight !== undefined ? /*#__PURE__*/React.createElement("div", {
212
+ }), (!isEmpty(entry.children) || !isEmpty(entry.sublayers)) && !_this.props.groupTogglesSublayers ? /*#__PURE__*/React.createElement(Icon, {
213
+ icon: "tree",
214
+ onClick: function onClick() {
215
+ return updateCallback(entryId, {
216
+ visibility: !entry.visibility
217
+ }, {
218
+ path: path,
219
+ groupTogglesSublayers: true
220
+ });
221
+ },
222
+ title: LocaleUtils.tr("layertree.togglegroup")
223
+ }) : null), entry.extrusionHeight !== undefined ? /*#__PURE__*/React.createElement("div", {
175
224
  className: "layertree3d-item-optionsmenu-row"
176
225
  }, /*#__PURE__*/React.createElement("span", null, "Extrude:"), /*#__PURE__*/React.createElement(React.Fragment, null, "\xA0"), /*#__PURE__*/React.createElement("select", {
177
226
  onChange: function onChange(ev) {
@@ -196,7 +245,7 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
196
245
  });
197
246
  },
198
247
  value: entry.extrusionHeight
199
- }) : null) : null) : null, _this.state.activestylemenu === entryId ? /*#__PURE__*/React.createElement("div", {
248
+ }) : null) : null) : null, _this.state.activestylemenu === key ? /*#__PURE__*/React.createElement("div", {
200
249
  className: "layertree3d-item-stylemenu"
201
250
  }, Object.keys(entry.styles).map(function (name) {
202
251
  return /*#__PURE__*/React.createElement("div", {
@@ -209,72 +258,47 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
209
258
  }, /*#__PURE__*/React.createElement(Icon, {
210
259
  icon: entry.style === name ? "radio_checked" : "radio_unchecked"
211
260
  }), /*#__PURE__*/React.createElement("div", null, name));
212
- })) : null, !isEmpty(entry.sublayers) ? /*#__PURE__*/React.createElement("div", {
261
+ })) : null, !isEmpty(entry.sublayers) && entry.expanded !== false ? /*#__PURE__*/React.createElement("div", {
213
262
  className: "layertree3d-item-sublayers"
214
263
  }, entry.sublayers.map(function (sublayer, idx) {
215
- return _this.renderSublayer(sublayer, entryId, updateCallback, [idx], entry.visibility);
216
- })) : null, !isEmpty(entry.children) ? /*#__PURE__*/React.createElement("div", {
264
+ return _this.renderLayerEntry(entryId, sublayer, entry, updateCallback, isObject, parentVisible && entry.visibility, [].concat(_toConsumableArray(path), [idx]));
265
+ })) : null, !isEmpty(entry.children) && entry.expanded !== false ? /*#__PURE__*/React.createElement("div", {
217
266
  className: "layertree3d-item-sublayers"
218
- }, entry.children.map(function (childId) {
219
- return _this.renderLayerEntry(childId, _this.props.sceneContext.objectTree[childId], updateCallback, true, parentVisible && entry.visibility);
267
+ }, entry.children.map(function (childId, idx) {
268
+ return _this.renderLayerEntry(childId, _this.props.sceneContext.objectTree[childId], entry, updateCallback, isObject, parentVisible && entry.visibility, [].concat(_toConsumableArray(path), [idx]));
220
269
  })) : null);
221
270
  });
222
- _defineProperty(_this, "renderSublayer", function (sublayer, entryId, updateCallback, path, parentVisible) {
223
- var key = entryId + ":" + path.join(":");
224
- var classes = classNames({
225
- "layertree3d-item": true,
226
- "layertree3d-item-disabled": !parentVisible || !sublayer.visibility
227
- });
228
- var optMenuClasses = classNames({
229
- "layertree3d-item-menubutton": true,
230
- "layertree3d-item-menubutton-active": _this.state.activemenu === key
231
- });
232
- return /*#__PURE__*/React.createElement("div", {
233
- className: "layertree3d-item-container",
234
- key: key
235
- }, /*#__PURE__*/React.createElement("div", {
236
- className: classes
237
- }, /*#__PURE__*/React.createElement(Icon, {
238
- className: "layertree3d-item-checkbox",
239
- icon: sublayer.visibility ? "checked" : "unchecked",
240
- onClick: function onClick() {
241
- return updateCallback(entryId, {
242
- visibility: !sublayer.visibility
243
- }, path);
244
- },
245
- sublayer: "layertree3d-item-checkbox"
246
- }), /*#__PURE__*/React.createElement("span", {
247
- className: "layertree3d-item-title",
248
- title: sublayer.title
249
- }, sublayer.title), /*#__PURE__*/React.createElement(Icon, {
250
- className: optMenuClasses,
251
- icon: "cog",
252
- onClick: function onClick() {
253
- return _this.layerMenuToggled(key);
271
+ _defineProperty(_this, "computeVisibilityIcon", function (isObject, entry, parent) {
272
+ if (parent !== null && parent !== void 0 && parent.mutuallyExclusive) {
273
+ return entry.visibility ? "radio_checked" : "radio_unchecked";
274
+ }
275
+ if (!entry.visibility) {
276
+ return "unchecked";
277
+ } else {
278
+ if (_this.props.groupTogglesSublayers) {
279
+ var subtreevisibility = isObject ? _this.computeObjectTreeVisibility(entry) : LayerUtils.computeLayerVisibility(entry);
280
+ return subtreevisibility === 1 ? "checked" : "tristate";
281
+ } else {
282
+ return "checked";
254
283
  }
255
- })), _this.state.activemenu === key ? /*#__PURE__*/React.createElement("div", {
256
- className: "layertree3d-item-optionsmenu"
257
- }, /*#__PURE__*/React.createElement("div", {
258
- className: "layertree3d-item-optionsmenu-row"
259
- }, /*#__PURE__*/React.createElement(Icon, {
260
- icon: "transparency"
261
- }), /*#__PURE__*/React.createElement("input", {
262
- className: "layertree3d-item-transparency-slider",
263
- max: "255",
264
- min: "0",
265
- onChange: function onChange(ev) {
266
- return updateCallback(entryId, {
267
- opacity: parseInt(ev.target.value, 10)
268
- }, path);
269
- },
270
- step: "1",
271
- type: "range",
272
- value: sublayer.opacity
273
- }))) : null, !isEmpty(sublayer.sublayers) ? /*#__PURE__*/React.createElement("div", {
274
- className: "layertree3d-item-sublayers"
275
- }, sublayer.sublayers.map(function (child, idx) {
276
- return _this.renderSublayer(child, entryId, updateCallback, [].concat(_toConsumableArray(path), [idx]), parentVisible && sublayer.visibility);
277
- })) : null);
284
+ }
285
+ });
286
+ _defineProperty(_this, "computeObjectTreeVisibility", function (entry) {
287
+ if (isEmpty(entry.children) || entry.visibility === false) {
288
+ return entry.visibility ? 1 : 0;
289
+ }
290
+ var visible = 0;
291
+ entry.children.map(function (childId) {
292
+ var _child$visibility;
293
+ var child = _this.props.sceneContext.objectTree[childId];
294
+ var sublayervisibility = (_child$visibility = child.visibility) !== null && _child$visibility !== void 0 ? _child$visibility : true;
295
+ if (child.children && sublayervisibility) {
296
+ visible += _this.computeObjectTreeVisibility(entry);
297
+ } else {
298
+ visible += sublayervisibility ? 1 : 0;
299
+ }
300
+ });
301
+ return visible / entry.children.length;
278
302
  });
279
303
  _defineProperty(_this, "layerStyleMenuToggled", function (entryId) {
280
304
  _this.setState(function (state) {
@@ -295,6 +319,18 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
295
319
  objectId: objectId
296
320
  });
297
321
  });
322
+ _defineProperty(_this, "updateSceneObject", function (objectId, options) {
323
+ var flags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
324
+ _this.props.sceneContext.updateSceneObject(objectId, options, _objectSpread({
325
+ groupTogglesSublayers: _this.props.groupTogglesSublayers
326
+ }, flags));
327
+ });
328
+ _defineProperty(_this, "updateColorLayer", function (objectId, options) {
329
+ var flags = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
330
+ _this.props.sceneContext.updateColorLayer(objectId, options, _objectSpread({
331
+ groupTogglesSublayers: _this.props.groupTogglesSublayers
332
+ }, flags));
333
+ });
298
334
  return _this;
299
335
  }
300
336
  _inherits(LayerTree3D, _React$Component);
@@ -316,6 +352,8 @@ var LayerTree3D = /*#__PURE__*/function (_React$Component) {
316
352
  }]);
317
353
  }(React.Component);
318
354
  _defineProperty(LayerTree3D, "propTypes", {
355
+ /** Whether toggling a group also toggles all sublayers. */
356
+ groupTogglesSublayers: PropTypes.bool,
319
357
  /** Base URL of imported tile sets. */
320
358
  importedTilesBaseUrl: PropTypes.string,
321
359
  sceneContext: PropTypes.object,
@@ -6,6 +6,11 @@
6
6
  padding: 0.25em 0 0 0.25em;
7
7
  }
8
8
 
9
+ #LayerTree3D span.layertree3d-item-expander {
10
+ margin-right: 0.25em;
11
+ flex: 0 0 1em;
12
+ }
13
+
9
14
  #LayerTree3D div.layertree3d-item-container {
10
15
  padding-left: 0.5em;
11
16
  }
@@ -31,6 +36,7 @@
31
36
  white-space: nowrap;
32
37
  overflow: hidden;
33
38
  text-overflow: ellipsis;
39
+ cursor: pointer;
34
40
  }
35
41
 
36
42
  #LayerTree3D span.layertree3d-item-transparency {
@@ -224,7 +224,6 @@ export default function layers() {
224
224
  return state;
225
225
  }
226
226
  }
227
- ;
228
227
  case ADD_LAYER_SEPARATOR:
229
228
  {
230
229
  var _newLayers4 = LayerUtils.insertSeparator(state.flat, action.title, action.afterLayerId, action.afterSublayerPath).map(function (layer) {
@@ -668,6 +668,7 @@ function genThemes(themesConfig) {
668
668
  defaultPrintResolutions: config.defaultPrintResolutions,
669
669
  defaultPrintGrid: config.defaultPrintGrid,
670
670
  defaultSearchProviders: config.defaultSearchProviders,
671
+ defaultMapTips: config.defaultMapTips,
671
672
  defaultBackgroundLayers: config.defaultBackgroundLayers || [],
672
673
  externalLayers: config.themes.externalLayers || [],
673
674
  pluginData: config.themes.pluginData,
@@ -625,6 +625,7 @@ def genThemes(themesConfig):
625
625
  "defaultPrintGrid": config["defaultPrintGrid"] if "defaultPrintGrid" in config else None,
626
626
  "defaultSearchProviders": config["defaultSearchProviders"] if "defaultSearchProviders" in config else None,
627
627
  "defaultBackgroundLayers": config["defaultBackgroundLayers"] if "defaultBackgroundLayers" in config else [],
628
+ "defaultMapTips": config["defaultMapTips"] if "defaultMapTips" in config else None,
628
629
  "pluginData": config["themes"]["pluginData"] if "pluginData" in config["themes"] else [],
629
630
  "themeInfoLinks": config["themes"]["themeInfoLinks"] if "themeInfoLinks" in config["themes"] else [],
630
631
  "externalLayers": config["themes"]["externalLayers"] if "externalLayers" in config["themes"] else [],
@@ -193,11 +193,11 @@ export function parseExpression(expr, feature, editConfig, editIface, mapPrefix,
193
193
  };
194
194
  var result = null;
195
195
  try {
196
- parser.feed(expr.replace(/\n/, ' '));
196
+ parser.feed(expr.replace(/\n/g, ' '));
197
197
  result = parser.results[0];
198
198
  } catch (_unused2) {
199
199
  /* eslint-disable-next-line */
200
- console.warn("Failed to evaluate expression " + expr.replace(/\n/, ' '));
200
+ console.warn("Failed to evaluate expression " + expr.replace(/\n/g, ' '));
201
201
  }
202
202
  delete window.qwc2ExpressionParserContext;
203
203
  if (promises.length > 0) {
@@ -244,13 +244,13 @@ export function parseExpressionsAsync(fieldExpressions, feature, editConfig, edi
244
244
  expression = _ref.expression;
245
245
  var parser = new nearley.Parser(nearley.Grammar.fromCompiled(grammar));
246
246
  try {
247
- parser.feed(expression.replace(/\n/, ' '));
247
+ parser.feed(expression.replace(/\n/g, ' '));
248
248
  // NOTE: include intermediate results in next context feature
249
249
  newfeature.properties[field] = parser.results[0];
250
250
  return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, field, parser.results[0]));
251
251
  } catch (_unused3) {
252
252
  /* eslint-disable-next-line */
253
- console.warn("Failed to evaluate expression " + expression.replace(/\n/, ' '));
253
+ console.warn("Failed to evaluate expression " + expression.replace(/\n/g, ' '));
254
254
  return res;
255
255
  }
256
256
  }, {});