qwc2 2025.12.18 → 2025.12.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 (71) hide show
  1. package/actions/display.js +6 -6
  2. package/actions/theme.js +4 -19
  3. package/components/AppMenu.js +1 -1
  4. package/components/AttributeForm.js +7 -7
  5. package/components/AttributeTableWidget.js +2 -2
  6. package/components/AutoEditForm.js +6 -3
  7. package/components/EditComboField.js +1 -4
  8. package/components/IdentifyViewer.js +33 -34
  9. package/components/ImportLayer.js +78 -79
  10. package/components/OverviewMapButton.js +147 -0
  11. package/components/PluginsContainer.js +2 -2
  12. package/components/ResizeableWindow.js +8 -1
  13. package/components/SearchBox.js +2 -2
  14. package/components/SideBar.js +1 -0
  15. package/components/StandardApp.js +1 -2
  16. package/components/ThemeLayersListWindow.js +10 -1
  17. package/components/{map3d/View3DSwitcher.js → ViewSwitcher.js} +74 -29
  18. package/components/map/OlMap.js +1 -1
  19. package/components/map3d/Map3D.js +50 -48
  20. package/components/map3d/MapControls3D.js +4 -1
  21. package/components/map3d/drawtool/EditTool3D.js +1 -1
  22. package/components/map3d/layers/WFSLayer3D.js +1 -1
  23. package/components/share/ShareQRCode.js +1 -1
  24. package/{plugins/map3d/style/OverviewMap3D.css → components/style/OverviewMapButton.css} +4 -4
  25. package/components/style/ViewSwitcher.css +36 -0
  26. package/components/widgets/ColorButton.js +2 -2
  27. package/components/widgets/CopyButton.js +1 -1
  28. package/components/widgets/LayerCatalogWidget.js +4 -4
  29. package/libs/openlayers.js +11 -11
  30. package/package.json +54 -55
  31. package/plugins/API.js +4 -4
  32. package/plugins/FeatureForm.js +2 -2
  33. package/plugins/FeatureSearch.js +12 -12
  34. package/plugins/GeometryDigitizer.js +12 -13
  35. package/plugins/Map.js +11 -4
  36. package/plugins/MapFilter.js +12 -12
  37. package/plugins/MapTip.js +1 -1
  38. package/plugins/ObliqueView.js +115 -51
  39. package/plugins/Print.js +79 -91
  40. package/plugins/Routing.js +1 -1
  41. package/plugins/Share.js +5 -5
  42. package/plugins/TimeManager.js +1 -2
  43. package/plugins/View3D.js +135 -123
  44. package/plugins/map/RedliningSupport.js +3 -3
  45. package/plugins/map3d/Draw3D.js +4 -4
  46. package/plugins/map3d/ExportObjects3D.js +1 -1
  47. package/plugins/map3d/HideObjects3D.js +7 -7
  48. package/plugins/map3d/Identify3D.js +1 -1
  49. package/plugins/map3d/LayerTree3D.js +1 -1
  50. package/plugins/map3d/MapExport3D.js +25 -25
  51. package/plugins/map3d/Measure3D.js +1 -1
  52. package/plugins/map3d/OverviewMap3D.js +27 -102
  53. package/plugins/map3d/TopBar3D.js +7 -7
  54. package/plugins/style/ObliqueView.css +27 -11
  55. package/reducers/display.js +2 -2
  56. package/reducers/layers.js +11 -11
  57. package/scripts/gen-plugin-docs.js +11 -4
  58. package/scripts/makeIconkit.js +2 -2
  59. package/scripts/themesConfig.js +5 -5
  60. package/scripts/updateTranslations.js +2 -2
  61. package/utils/CoordinatesUtils.js +1 -1
  62. package/utils/EditingUtils.js +6 -6
  63. package/utils/FeatureStyles.js +1 -1
  64. package/utils/LayerUtils.js +73 -74
  65. package/utils/MiscUtils.js +10 -3
  66. package/utils/PermaLinkUtils.js +68 -71
  67. package/utils/SearchProviders.js +2 -2
  68. package/utils/ServiceLayerUtils.js +12 -12
  69. package/utils/ThemeUtils.js +1 -1
  70. package/utils/VectorLayerUtils.js +3 -3
  71. package/components/map3d/style/View3DSwitcher.css +0 -19
@@ -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 _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
3
5
  function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
4
6
  function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
@@ -26,15 +28,16 @@ import axios from 'axios';
26
28
  import isEqual from 'lodash.isequal';
27
29
  import ol from 'openlayers';
28
30
  import PropTypes from 'prop-types';
31
+ import { setViewMode, ViewMode } from '../actions/display';
29
32
  import { zoomToExtent } from '../actions/map';
30
33
  import { setCurrentTask } from '../actions/task';
31
34
  import Icon from '../components/Icon';
35
+ import OlLayer from '../components/map/OlLayer';
36
+ import OverviewMapButton from '../components/OverviewMapButton';
32
37
  import ResizeableWindow from '../components/ResizeableWindow';
33
- import LayerRegistry from '../components/map/layers/index';
34
38
  import InputContainer from '../components/widgets/InputContainer';
35
39
  import ConfigUtils from '../utils/ConfigUtils';
36
40
  import CoordinatesUtils from '../utils/CoordinatesUtils';
37
- import LayerUtils from '../utils/LayerUtils';
38
41
  import LocaleUtils from '../utils/LocaleUtils';
39
42
  import MapUtils from '../utils/MapUtils';
40
43
  import MiscUtils from '../utils/MiscUtils';
@@ -42,6 +45,27 @@ import './style/ObliqueView.css';
42
45
 
43
46
  /**
44
47
  * Display oblique satellite imagery.
48
+ *
49
+ * Requires `obliqueImageryServiceUrl` in `config.json` to point to a `qwc-oblique-imagery-service`.
50
+ *
51
+ * You can configure oblique imagery datasets in the `obliqueDatasets` entry in a QWC theme configuration as follows:
52
+ * ```
53
+ * {
54
+ * ...
55
+ * "obliqueDatasets": [{
56
+ * {
57
+ * "name": "<dataset_name>",
58
+ * "default": <false|true>,
59
+ * "backgroundLayer": "<background_layer_name>",
60
+ * "backgroundOpacity": <0-255>,
61
+ * "title": "<dataset_title>",
62
+ * "titleMsgId": "<dataset_title_msgid>"
63
+ * },
64
+ * ...
65
+ * ]
66
+ * }
67
+ * ```
68
+ * where `dataset_name` is the the name of a dataset configured in the `qwc-oblique-imagery-service`.
45
69
  */
46
70
  var ObliqueView = /*#__PURE__*/function (_React$Component) {
47
71
  function ObliqueView(props) {
@@ -49,6 +73,7 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
49
73
  _classCallCheck(this, ObliqueView);
50
74
  _this = _callSuper(this, ObliqueView, [props]);
51
75
  _defineProperty(_this, "onClose", function () {
76
+ _this.props.setViewMode(ViewMode._2D);
52
77
  _this.setState(ObliqueView.defaultState);
53
78
  });
54
79
  _defineProperty(_this, "renderScaleChooser", function () {
@@ -93,6 +118,9 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
93
118
  /* eslint-disable-next-line */
94
119
  console.warn("Failed to load dataset config");
95
120
  });
121
+ } else {
122
+ _this.obliqueImageryLayer.setSource(null);
123
+ _this.closestImage = null;
96
124
  }
97
125
  });
98
126
  _defineProperty(_this, "setupLayer", function () {
@@ -108,7 +136,6 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
108
136
  }, 0);
109
137
  _this.map.setView(new ol.View({
110
138
  projection: projection,
111
- // extent: datasetConfig.extent,
112
139
  center: ol.extent.getCenter(datasetConfig.extent),
113
140
  rotation: _this.getRotation() / 180 * Math.PI,
114
141
  zoom: zoom,
@@ -119,42 +146,25 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
119
146
  _this.setState({
120
147
  currentZoom: zoom
121
148
  });
122
- var layers = [];
123
- var themeConfig = _this.props.theme.obliqueDatasets.find(function (entry) {
124
- return entry.name === _this.state.selectedDataset;
125
- });
126
- if (themeConfig.backgroundLayer) {
127
- var _themeConfig$backgrou;
128
- var layerConfig = LayerUtils.splitLayerUrlParam(themeConfig.backgroundLayer);
129
- layerConfig.version = _this.props.themes.defaultWMSVersion || "1.3.0";
130
- layerConfig.opacity = (_themeConfig$backgrou = themeConfig.backgroundOpacity) !== null && _themeConfig$backgrou !== void 0 ? _themeConfig$backgrou : 127;
131
- var layerCreator = LayerRegistry[layerConfig.type];
132
- if (layerCreator) {
133
- layers.push(layerCreator.create(layerConfig, _this.map));
134
- }
135
- }
136
- _this.obliqueImageryLayer = new ol.layer.Tile({
137
- source: new ol.source.XYZ({
138
- projection: projection,
139
- tileGrid: new ol.tilegrid.TileGrid({
140
- extent: datasetConfig.extent,
141
- resolutions: datasetConfig.resolutions,
142
- tileSize: datasetConfig.tileSize,
143
- origin: datasetConfig.origin
144
- }),
145
- url: datasetConfig.url,
146
- crossOrigin: "anonymous",
147
- tileLoadFunction: function tileLoadFunction(tile, src) {
148
- var _this$closestImage;
149
- if (((_this$closestImage = _this.closestImage) !== null && _this$closestImage !== void 0 ? _this$closestImage : null) !== null) {
150
- src += "?img=" + _this.closestImage;
151
- }
152
- tile.getImage().src = src.replace('{direction}', _this.state.currentDirection);
149
+ _this.obliqueImageryLayer.setSource(new ol.source.XYZ({
150
+ projection: projection,
151
+ tileGrid: new ol.tilegrid.TileGrid({
152
+ extent: datasetConfig.extent,
153
+ resolutions: datasetConfig.resolutions,
154
+ tileSize: datasetConfig.tileSize,
155
+ origin: datasetConfig.origin
156
+ }),
157
+ url: datasetConfig.url,
158
+ crossOrigin: "anonymous",
159
+ tileLoadFunction: function tileLoadFunction(tile, src) {
160
+ var _this$closestImage;
161
+ if (((_this$closestImage = _this.closestImage) !== null && _this$closestImage !== void 0 ? _this$closestImage : null) !== null) {
162
+ src += "?img=" + _this.closestImage;
153
163
  }
154
- })
155
- });
156
- layers.push(_this.obliqueImageryLayer);
157
- _this.map.setLayers(layers);
164
+ tile.getImage().src = src.replace('{direction}', _this.state.currentDirection);
165
+ }
166
+ }));
167
+ _this.searchClosestImage();
158
168
  });
159
169
  _defineProperty(_this, "searchClosestImage", function () {
160
170
  var _this$state$datasetCo;
@@ -244,8 +254,9 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
244
254
  return null;
245
255
  });
246
256
  });
257
+ _this.obliqueImageryLayer = new ol.layer.Tile();
258
+ _this.map.addLayer(_this.obliqueImageryLayer);
247
259
  _this.closestImage = null;
248
- _this.obliqueImageryLayer = null;
249
260
  _this.state = ObliqueView.defaultState;
250
261
  _this.focusedMap = null;
251
262
  return _this;
@@ -255,6 +266,9 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
255
266
  key: "componentDidMount",
256
267
  value: function componentDidMount() {
257
268
  window.addEventListener('focus', this.trackFocus, true);
269
+ if (this.props.startupParams.v === "oblique") {
270
+ this.props.setViewMode(ViewMode._Oblique);
271
+ }
258
272
  }
259
273
  }, {
260
274
  key: "componentWillUnmount",
@@ -265,12 +279,14 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
265
279
  key: "componentDidUpdate",
266
280
  value: function componentDidUpdate(prevProps, prevState) {
267
281
  if (this.props.active && !prevProps.active) {
268
- this.setState({
269
- active: true
270
- });
282
+ this.props.setViewMode(ViewMode._Oblique);
271
283
  this.props.setCurrentTask(null);
272
284
  }
273
- if (this.props.active && this.props.theme && this.props.theme !== prevProps.theme || this.props.active && !prevProps.active) {
285
+ // Honour theme startupView on theme change unless first loaded theme and startupParams.v is set
286
+ if (this.props.theme !== prevProps.theme && this.props.theme.startupView === "oblique" && (prevProps.theme !== null || !this.props.startupParams.v)) {
287
+ this.props.setViewMode(ViewMode._Oblique);
288
+ }
289
+ if (this.props.viewMode === ViewMode._Oblique && this.props.theme && (this.props.theme !== prevProps.theme || prevProps.viewMode !== ViewMode._Oblique)) {
274
290
  var _datasets$find$name, _datasets$find, _datasets$;
275
291
  var datasets = this.props.theme.obliqueDatasets || [];
276
292
  var defaultDataset = (_datasets$find$name = (_datasets$find = datasets.find(function (entry) {
@@ -283,8 +299,6 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
283
299
  });
284
300
  }
285
301
  if (this.state.selectedDataset !== prevState.selectedDataset) {
286
- this.closestImage = null;
287
- this.obliqueImageryLayer = null;
288
302
  this.queryDatasetConfig();
289
303
  }
290
304
  if (this.state.datasetConfig && this.state.datasetConfig !== prevState.datasetConfig) {
@@ -310,8 +324,15 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
310
324
  }, {
311
325
  key: "render",
312
326
  value: function render() {
313
- var _this2 = this;
314
- if (!this.state.active) {
327
+ var _this2 = this,
328
+ _this$props$theme,
329
+ _this$props$theme$fin,
330
+ _this$props$themes,
331
+ _this$props$themes$fi,
332
+ _obliqueConfig$backgr,
333
+ _this$state$selectedD,
334
+ _this$props$theme2;
335
+ if (this.props.viewMode !== ViewMode._Oblique || !this.props.themes) {
315
336
  return null;
316
337
  }
317
338
  var rot = this.getRotation();
@@ -331,6 +352,12 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
331
352
  title: LocaleUtils.tr("common.lock2dview"),
332
353
  active: this.state.viewsLocked
333
354
  }];
355
+ var obliqueConfig = (_this$props$theme = this.props.theme) === null || _this$props$theme === void 0 || (_this$props$theme = _this$props$theme.obliqueDatasets) === null || _this$props$theme === void 0 || (_this$props$theme$fin = _this$props$theme.find) === null || _this$props$theme$fin === void 0 ? void 0 : _this$props$theme$fin.call(_this$props$theme, function (entry) {
356
+ return entry.name === _this2.state.selectedDataset;
357
+ });
358
+ var basemap = (_this$props$themes = this.props.themes) === null || _this$props$themes === void 0 || (_this$props$themes = _this$props$themes.backgroundLayers) === null || _this$props$themes === void 0 || (_this$props$themes$fi = _this$props$themes.find) === null || _this$props$themes$fi === void 0 ? void 0 : _this$props$themes$fi.call(_this$props$themes, function (entry) {
359
+ return entry.name === (obliqueConfig === null || obliqueConfig === void 0 ? void 0 : obliqueConfig.backgroundLayer);
360
+ });
334
361
  return /*#__PURE__*/React.createElement(ResizeableWindow, {
335
362
  dockable: this.props.geometry.side,
336
363
  extraControls: extraControls,
@@ -407,7 +434,9 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
407
434
  transform: "rotate(".concat(-rot, "deg)")
408
435
  },
409
436
  tabIndex: 0
410
- }, "S"), /*#__PURE__*/React.createElement("span", null)), /*#__PURE__*/React.createElement("div", {
437
+ }, "S"), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement("div", {
438
+ className: "obliqueview-nav-circle"
439
+ })), /*#__PURE__*/React.createElement("div", {
411
440
  className: "obliqueview-nav-zoom"
412
441
  }, /*#__PURE__*/React.createElement(Icon, {
413
442
  icon: "plus",
@@ -419,9 +448,39 @@ var ObliqueView = /*#__PURE__*/function (_React$Component) {
419
448
  onClick: function onClick() {
420
449
  return _this2.changeZoom(-1);
421
450
  }
422
- })), /*#__PURE__*/React.createElement("div", {
451
+ })), basemap && this.state.datasetConfig ? /*#__PURE__*/React.createElement(OlLayer, {
452
+ map: this.map,
453
+ options: _objectSpread(_objectSpread({}, basemap), {}, {
454
+ opacity: (_obliqueConfig$backgr = obliqueConfig.backgroundOpacity) !== null && _obliqueConfig$backgr !== void 0 ? _obliqueConfig$backgr : 127
455
+ }),
456
+ projection: this.state.datasetConfig.crs,
457
+ zIndex: -1
458
+ }) : null, /*#__PURE__*/React.createElement("div", {
423
459
  className: "obliqueview-bottombar"
424
- }, this.renderScaleChooser())));
460
+ }, /*#__PURE__*/React.createElement("select", {
461
+ onChange: function onChange(ev) {
462
+ return _this2.setState({
463
+ selectedDataset: ev.target.value
464
+ });
465
+ },
466
+ value: (_this$state$selectedD = this.state.selectedDataset) !== null && _this$state$selectedD !== void 0 ? _this$state$selectedD : ""
467
+ }, (((_this$props$theme2 = this.props.theme) === null || _this$props$theme2 === void 0 ? void 0 : _this$props$theme2.obliqueDatasets) || []).map(function (entry) {
468
+ var _entry$title;
469
+ return /*#__PURE__*/React.createElement("option", {
470
+ key: entry.name,
471
+ value: entry.name
472
+ }, LocaleUtils.trWithFallback(entry.titleMsgId, (_entry$title = entry.title) !== null && _entry$title !== void 0 ? _entry$title : entry.name));
473
+ })), /*#__PURE__*/React.createElement("span", {
474
+ className: "obliqueview-bottombar-spacer"
475
+ }), this.renderScaleChooser(), /*#__PURE__*/React.createElement("span", {
476
+ className: "obliqueview-bottombar-spacer"
477
+ }), basemap && this.state.datasetConfig ? /*#__PURE__*/React.createElement(OverviewMapButton, {
478
+ center: this.state.currentCenter,
479
+ coneRotation: this.getRotation() / 180 * Math.PI,
480
+ layer: basemap,
481
+ projection: this.state.datasetConfig.crs,
482
+ resolution: MapUtils.computeForZoom(this.state.datasetConfig.resolutions, this.state.currentZoom) * 0.25
483
+ }) : null)));
425
484
  }
426
485
  }]);
427
486
  }(React.Component);
@@ -443,8 +502,11 @@ _defineProperty(ObliqueView, "propTypes", {
443
502
  /** A list of allowed map scales, in decreasing order. */
444
503
  scales: PropTypes.arrayOf(PropTypes.number),
445
504
  setCurrentTask: PropTypes.func,
505
+ setViewMode: PropTypes.func,
506
+ startupParams: PropTypes.object,
446
507
  theme: PropTypes.object,
447
508
  themes: PropTypes.object,
509
+ viewMode: PropTypes.number,
448
510
  zoomToExtent: PropTypes.func
449
511
  });
450
512
  _defineProperty(ObliqueView, "defaultProps", {
@@ -460,7 +522,6 @@ _defineProperty(ObliqueView, "defaultProps", {
460
522
  scales: [20000, 10000, 5000, 2500, 1000, 500, 250]
461
523
  });
462
524
  _defineProperty(ObliqueView, "defaultState", {
463
- active: false,
464
525
  selectedDataset: null,
465
526
  datasetConfig: null,
466
527
  currentDirection: null,
@@ -472,10 +533,13 @@ export default connect(function (state) {
472
533
  return {
473
534
  active: state.task.id === "ObliqueView",
474
535
  map: state.map,
536
+ startupParams: state.localConfig.startupParams,
537
+ viewMode: state.display.viewMode,
475
538
  theme: state.theme.current,
476
539
  themes: state.theme.themes
477
540
  };
478
541
  }, {
479
542
  setCurrentTask: setCurrentTask,
543
+ setViewMode: setViewMode,
480
544
  zoomToExtent: zoomToExtent
481
545
  })(ObliqueView);