qwc2 2026.5.26 → 2026.5.27

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.
@@ -766,9 +766,9 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
766
766
  var center = extent.center();
767
767
  if (center.crs.isGeographic()) {
768
768
  var position = Ellipsoid.WGS84.toCartesian(center.latitude, center.longitude, center.altitude);
769
- _this2.instance.view.camera.position.set(position.x, position.y, 0.5 * (extent.east - extent.west));
769
+ _this2.instance.view.camera.position.set(position.x, position.y, 0.5 * (extent.maxX - extent.minX));
770
770
  } else {
771
- _this2.instance.view.camera.position.set(center.x, center.y, 0.5 * (extent.east - extent.west));
771
+ _this2.instance.view.camera.position.set(center.x, center.y, 0.5 * (extent.maxX - extent.minX));
772
772
  }
773
773
 
774
774
  // Skybox
@@ -98,6 +98,13 @@ var Tiles3DStyle = {
98
98
  var featureLabelCache = {};
99
99
  var labels = {};
100
100
  var idAttr = (_config$idAttr = config.idAttr) !== null && _config$idAttr !== void 0 ? _config$idAttr : "id";
101
+ if (!["tilesetStyle", "colorAttr", "alphaAttr", "labelAttr", "baseColor"].some(function (k) {
102
+ var _config$k;
103
+ return ((_config$k = config[k]) !== null && _config$k !== void 0 ? _config$k : null) !== null;
104
+ })) {
105
+ // No styling information is present
106
+ return;
107
+ }
101
108
  var context = {
102
109
  colorExpressions: [],
103
110
  featureStyles: (_config$tilesetStyle = config.tilesetStyle) === null || _config$tilesetStyle === void 0 ? void 0 : _config$tilesetStyle.featureStyles,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2026.05.26",
3
+ "version": "2026.05.27",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -39,12 +39,14 @@ import axios from 'axios';
39
39
  import isEmpty from 'lodash.isempty';
40
40
  import PropTypes from 'prop-types';
41
41
  import { v4 as uuidv4 } from 'uuid';
42
+ import { LayerRole, changeLayerProperty } from '../actions/layers';
42
43
  import Icon from '../components/Icon';
43
44
  import IdentifyViewer from '../components/IdentifyViewer';
44
45
  import SideBar from '../components/SideBar';
45
46
  import InputContainer from '../components/widgets/InputContainer';
46
47
  import Spinner from '../components/widgets/Spinner';
47
48
  import IdentifyUtils from '../utils/IdentifyUtils';
49
+ import LayerUtils from '../utils/LayerUtils';
48
50
  import LocaleUtils from '../utils/LocaleUtils';
49
51
  import { QgisSearch } from '../utils/SearchProviders';
50
52
  import "./style/FeatureSearch.css";
@@ -258,6 +260,17 @@ var FeatureSearch = /*#__PURE__*/function (_React$Component) {
258
260
  params: params
259
261
  }).then(function (response) {
260
262
  var results = IdentifyUtils.parseResponse(response.data, _this.props.theme, 'text/xml', null, _this.props.map.projection);
263
+ // Enable the layers
264
+ Object.keys(results).forEach(function (layername) {
265
+ var path = [];
266
+ var sublayer = null;
267
+ var layer = _this.props.layers.find(function (l) {
268
+ return l.role === LayerRole.THEME && (sublayer = LayerUtils.searchSubLayer(l, 'name', layername, path));
269
+ });
270
+ if (layer && sublayer) {
271
+ _this.props.changeLayerProperty(layer.id, "visibility", true, path, 'both');
272
+ }
273
+ });
261
274
  if (provider.params.resultTitle) {
262
275
  Object.entries(results).forEach(function (_ref6) {
263
276
  var _ref7 = _slicedToArray(_ref6, 2),
@@ -448,8 +461,10 @@ var FeatureSearch = /*#__PURE__*/function (_React$Component) {
448
461
  }]);
449
462
  }(React.Component);
450
463
  _defineProperty(FeatureSearch, "propTypes", {
464
+ changeLayerProperty: PropTypes.func,
451
465
  /** Whether to enable the export functionality. Either `true|false` or a list of single allowed formats (builtin formats: `json`, `geojson`, `csv`, `csvzip`, `shapefile`, `xlsx`). If a list is provided, the export formats will be sorted according to that list, and the default format will be the first format of the list. */
452
466
  enableExport: PropTypes.oneOfType([PropTypes.bool, PropTypes.array]),
467
+ layers: PropTypes.array,
453
468
  map: PropTypes.object,
454
469
  /** The side of the application on which to display the sidebar. */
455
470
  side: PropTypes.string,
@@ -461,7 +476,10 @@ _defineProperty(FeatureSearch, "defaultProps", {
461
476
  });
462
477
  export default connect(function (state) {
463
478
  return {
479
+ layers: state.layers.flat,
464
480
  map: state.map,
465
481
  theme: state.theme.current
466
482
  };
467
- }, {})(FeatureSearch);
483
+ }, {
484
+ changeLayerProperty: changeLayerProperty
485
+ })(FeatureSearch);
@@ -582,7 +582,7 @@ var MapExport = /*#__PURE__*/function (_React$Component) {
582
582
  }]);
583
583
  }(React.Component);
584
584
  _defineProperty(MapExport, "propTypes", {
585
- /** Whitelist of allowed export format mimetypes. If empty, supported formats are listed. */
585
+ /** Whitelist of allowed export format mimetypes. If empty, supported formats are listed. Formats are sorted according to this list, and the first entry becomes the default format. Supported mimetypes are: `image/jpeg`, `image/png`, `image/png; mode=16bit`, `image/png; mode=8bit`, `image/png; mode=1bit`, `image/geotiff`, `image/tiff`, `application/dxf`, `application/pdf`. */
586
586
  allowedFormats: PropTypes.arrayOf(PropTypes.string),
587
587
  /** List of scales at which to export the map. If empty, scale can be freely specified. If `false`, the map can only be exported at the current scale. */
588
588
  allowedScales: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.number), PropTypes.bool]),
package/plugins/Print.js CHANGED
@@ -132,7 +132,7 @@ var Print = /*#__PURE__*/function (_React$Component) {
132
132
  atlasFeatures: []
133
133
  });
134
134
  });
135
- _defineProperty(_this, "renderBody", function () {
135
+ _defineProperty(_this, "renderBody", function (themeLayer) {
136
136
  var _this$state$extents$a, _this$props$theme$pri2, _this$state$layout$la, _this$state$layout;
137
137
  if (!_this.state.layout) {
138
138
  return /*#__PURE__*/React.createElement("div", {
@@ -255,7 +255,7 @@ var Print = /*#__PURE__*/function (_React$Component) {
255
255
  return /*#__PURE__*/React.createElement("option", {
256
256
  key: item.name,
257
257
  value: item.name
258
- }, _this.translateLayoutName(item));
258
+ }, _this.translateLayoutName(themeLayer, item));
259
259
  })))), _this.props.formats.length > 1 ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, LocaleUtils.tr("common.format")), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("select", {
260
260
  disabled: _this.state.printSeriesEnabled,
261
261
  name: "FORMAT",
@@ -801,9 +801,9 @@ var Print = /*#__PURE__*/function (_React$Component) {
801
801
  });
802
802
  });
803
803
  });
804
- _defineProperty(_this, "translateLayoutName", function (item) {
805
- var _ref1, _ref10, _this$props$theme$tra, _this$props$theme$tra2, _this$props$theme$tra3;
806
- return (_ref1 = (_ref10 = (_this$props$theme$tra = (_this$props$theme$tra2 = _this.props.theme.translations) === null || _this$props$theme$tra2 === void 0 || (_this$props$theme$tra2 = _this$props$theme$tra2.layouts) === null || _this$props$theme$tra2 === void 0 ? void 0 : _this$props$theme$tra2[item.title]) !== null && _this$props$theme$tra !== void 0 ? _this$props$theme$tra : (_this$props$theme$tra3 = _this.props.theme.translations) === null || _this$props$theme$tra3 === void 0 || (_this$props$theme$tra3 = _this$props$theme$tra3.layouts) === null || _this$props$theme$tra3 === void 0 ? void 0 : _this$props$theme$tra3[item.name]) !== null && _ref10 !== void 0 ? _ref10 : item.title) !== null && _ref1 !== void 0 ? _ref1 : item.name;
804
+ _defineProperty(_this, "translateLayoutName", function (themeLayer, item) {
805
+ var _ref1, _ref10, _themeLayer$translati, _themeLayer$translati2, _themeLayer$translati3;
806
+ return (_ref1 = (_ref10 = (_themeLayer$translati = (_themeLayer$translati2 = themeLayer.translations) === null || _themeLayer$translati2 === void 0 || (_themeLayer$translati2 = _themeLayer$translati2.layouts) === null || _themeLayer$translati2 === void 0 ? void 0 : _themeLayer$translati2[item.title]) !== null && _themeLayer$translati !== void 0 ? _themeLayer$translati : (_themeLayer$translati3 = themeLayer.translations) === null || _themeLayer$translati3 === void 0 || (_themeLayer$translati3 = _themeLayer$translati3.layouts) === null || _themeLayer$translati3 === void 0 ? void 0 : _themeLayer$translati3[item.name]) !== null && _ref10 !== void 0 ? _ref10 : item.title) !== null && _ref1 !== void 0 ? _ref1 : item.name;
807
807
  });
808
808
  _this.printForm = null;
809
809
  _this.state.grid = props.gridInitiallyEnabled;
@@ -892,7 +892,7 @@ var Print = /*#__PURE__*/function (_React$Component) {
892
892
  width: "20em"
893
893
  }, function () {
894
894
  return {
895
- body: _this3.state.minimized ? null : _this3.renderBody(),
895
+ body: _this3.state.minimized ? null : _this3.renderBody(themeLayer),
896
896
  extra: [_this3.renderPrintSelection()]
897
897
  };
898
898
  }), this.renderPrintOutputWindow(), this.props.active && this.state.layout && this.state.layout.atlasCoverageLayer && !this.state.printSeriesEnabled ? /*#__PURE__*/React.createElement(PickFeature, {
@@ -417,7 +417,7 @@ var Redlining = /*#__PURE__*/function (_React$Component) {
417
417
  className: "redlining-control"
418
418
  }, /*#__PURE__*/React.createElement("span", null, sizeLabel, ":\xA0"), /*#__PURE__*/React.createElement(NumberInput, {
419
419
  max: 99,
420
- min: 1,
420
+ min: 0,
421
421
  mobile: true,
422
422
  onChange: function onChange(nr) {
423
423
  return _this.updateRedliningStyle({
@@ -103,9 +103,10 @@ var RedliningFeatureLabelSupport = /*#__PURE__*/function (_React$Component) {
103
103
  role: LayerRole.USERLAYER
104
104
  };
105
105
  var featureId = "label::".concat(cur.map, "::").concat(cur.layer, "::").concat(cur.feature.id);
106
- var expression = cur.profiles.find(function (profile) {
107
- return profile.name === _this.state.currentProfile;
108
- }).expression;
106
+ var profile = cur.profiles.find(function (entry) {
107
+ return entry.name === _this.state.currentProfile;
108
+ });
109
+ var expression = profile.expression;
109
110
  if (!expression) {
110
111
  _this.props.removeLayerFeatures(layer, [featureId]);
111
112
  return;
@@ -126,12 +127,17 @@ var RedliningFeatureLabelSupport = /*#__PURE__*/function (_React$Component) {
126
127
  return f.id === featureId;
127
128
  });
128
129
  if (feature) {
130
+ var _profile$size;
129
131
  feature = _objectSpread(_objectSpread({}, feature), {}, {
130
132
  properties: {
131
133
  label: text
132
- }
134
+ },
135
+ styleOptions: _objectSpread(_objectSpread({}, feature.styleOptions), {}, {
136
+ strokeWidth: ((_profile$size = profile.size) !== null && _profile$size !== void 0 ? _profile$size : null) !== null ? 1 + 0.5 * profile.size : feature.styleOptions.stokeWidth
137
+ })
133
138
  });
134
139
  } else {
140
+ var _profile$size2;
135
141
  feature = {
136
142
  id: featureId,
137
143
  type: "Feature",
@@ -145,7 +151,7 @@ var RedliningFeatureLabelSupport = /*#__PURE__*/function (_React$Component) {
145
151
  shape: "Text",
146
152
  styleName: "textlabel",
147
153
  styleOptions: {
148
- strokeWidth: 2,
154
+ strokeWidth: 1 + 0.5 * ((_profile$size2 = profile.size) !== null && _profile$size2 !== void 0 ? _profile$size2 : 1),
149
155
  fillColor: [255, 255, 255, 1],
150
156
  textFillColor: [0, 0, 0, 1],
151
157
  strokeColor: [0, 0, 0, 1],
@@ -17,6 +17,7 @@ import deepmerge from 'deepmerge';
17
17
  import { flatten } from 'flat';
18
18
  import { CHANGE_LOCALE, ADD_TRANSLATIONS } from '../actions/locale';
19
19
  var defaultState = {
20
+ messagesTree: {},
20
21
  messages: {},
21
22
  fallbackMessages: {},
22
23
  current: null
@@ -39,7 +40,7 @@ export default function locale() {
39
40
  var _action$translations$, _action$translations;
40
41
  var newMessages = (_action$translations$ = (_action$translations = action.translations) === null || _action$translations === void 0 ? void 0 : _action$translations[state.current]) !== null && _action$translations$ !== void 0 ? _action$translations$ : {};
41
42
  return _objectSpread(_objectSpread({}, state), {}, {
42
- messagesTree: deepmerge(state.messages, newMessages),
43
+ messagesTree: deepmerge(state.messagesTree, newMessages),
43
44
  messages: _objectSpread(_objectSpread({}, state.messages), flatten(newMessages))
44
45
  });
45
46
  }
@@ -671,6 +671,7 @@ function genThemes(themesConfig) {
671
671
  defaultSearchProviders: config.defaultSearchProviders,
672
672
  defaultMapTips: config.defaultMapTips,
673
673
  defaultBackgroundLayers: config.defaultBackgroundLayers || [],
674
+ defaultLabelProfiles: config.defaultLabelProfiles,
674
675
  externalLayers: config.themes.externalLayers || [],
675
676
  pluginData: config.themes.pluginData,
676
677
  themeInfoLinks: config.themes.themeInfoLinks,
@@ -628,6 +628,7 @@ def genThemes(themesConfig):
628
628
  "defaultSearchProviders": config["defaultSearchProviders"] if "defaultSearchProviders" in config else None,
629
629
  "defaultBackgroundLayers": config["defaultBackgroundLayers"] if "defaultBackgroundLayers" in config else [],
630
630
  "defaultMapTips": config["defaultMapTips"] if "defaultMapTips" in config else None,
631
+ "defaultLabelProfiles": config["defaultLabelProfiles"] if "defaultLabelProfiles" in config else None,
631
632
  "pluginData": config["themes"]["pluginData"] if "pluginData" in config["themes"] else [],
632
633
  "themeInfoLinks": config["themes"]["themeInfoLinks"] if "themeInfoLinks" in config["themes"] else [],
633
634
  "externalLayers": config["themes"]["externalLayers"] if "externalLayers" in config["themes"] else [],
@@ -19,6 +19,7 @@ import ConfigUtils from './ConfigUtils';
19
19
  import arrowhead from './img/arrowhead.svg';
20
20
  import markerIcon from './img/marker-icon.png';
21
21
  import measurehead from './img/measurehead.svg';
22
+ import MiscUtils from './MiscUtils';
22
23
  import ResourceRegistry from './ResourceRegistry';
23
24
  ResourceRegistry.addResource('arrowhead', arrowhead);
24
25
  ResourceRegistry.addResource('measurehead', measurehead);
@@ -466,15 +467,15 @@ export default {
466
467
  return [new ol.style.Style({
467
468
  text: new ol.style.Text({
468
469
  font: '10pt sans-serif',
469
- padding: [6, 10, 6, 10],
470
+ padding: [3, 5, 3, 5],
470
471
  text: feature.getProperties().label || "",
471
472
  rotation: feature.getProperties().rotation || 0,
472
473
  scale: options.strokeWidth,
473
474
  fill: new ol.style.Fill({
474
- color: '#000'
475
+ color: options.strokeColor
475
476
  }),
476
477
  stroke: new ol.style.Stroke({
477
- color: '#FFF',
478
+ color: MiscUtils.isBrightColor(options.strokeColor) ? '#333' : '#FFF',
478
479
  width: 2
479
480
  }),
480
481
  backgroundFill: new ol.style.Fill({
@@ -185,11 +185,21 @@ var MiscUtils = {
185
185
  capitalizeFirst: function capitalizeFirst(text) {
186
186
  return text.slice(0, 1).toUpperCase() + text.slice(1);
187
187
  },
188
- isBrightColor: function isBrightColor(hex) {
189
- var color = +("0x" + hex.slice(1).replace(hex.length < 5 && /./g, '$&$&'));
190
- var r = color >> 16;
191
- var g = color >> 8 & 255;
192
- var b = color & 255;
188
+ isBrightColor: function isBrightColor(color) {
189
+ var r;
190
+ var g;
191
+ var b;
192
+ if (Array.isArray(color)) {
193
+ var _color = _slicedToArray(color, 3);
194
+ r = _color[0];
195
+ g = _color[1];
196
+ b = _color[2];
197
+ } else {
198
+ var intcolor = +("0x" + color.slice(1).replace(color.length < 5 && /./g, '$&$&'));
199
+ r = intcolor >> 16;
200
+ g = intcolor >> 8 & 255;
201
+ b = intcolor & 255;
202
+ }
193
203
  var hsp = Math.sqrt(0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b));
194
204
  return hsp > 127.5;
195
205
  },
@@ -184,7 +184,7 @@ var ThemeUtils = {
184
184
  editConfig: theme.editConfig,
185
185
  editConfigUrl: theme.editConfigUrl,
186
186
  filter: theme.filter,
187
- labelProfiles: theme.labelProfiles,
187
+ labelProfiles: _objectSpread(_objectSpread({}, themes.defaultLabelProfiles), theme.labelProfiles),
188
188
  wms_name: theme.wms_name
189
189
  };
190
190
  if (layer.editConfigUrl) {