qwc2 2025.10.15 → 2025.10.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 (67) hide show
  1. package/actions/locate.js +3 -2
  2. package/components/AttributeTableWidget.js +24 -15
  3. package/components/FeatureAttributesWindow.js +177 -0
  4. package/components/IdentifyViewer.js +450 -319
  5. package/components/LocationRecorder.js +138 -0
  6. package/components/PickFeature.js +87 -26
  7. package/components/PluginsContainer.js +16 -3
  8. package/components/map/OlLayer.js +2 -1
  9. package/components/map3d/HeightProfile3D.js +2 -0
  10. package/components/map3d/Map3D.js +1 -1
  11. package/components/style/App.css +18 -0
  12. package/components/style/AttributeTableWidget.css +2 -1
  13. package/components/style/FeatureAttributesWindow.css +16 -0
  14. package/components/style/IdentifyViewer.css +56 -80
  15. package/components/style/LocationRecorder.css +10 -0
  16. package/components/style/NumericInputWindow.css +7 -0
  17. package/components/style/PluginsContainer.css +16 -0
  18. package/components/widgets/LayerCatalogWidget.js +40 -16
  19. package/components/widgets/NavBar.js +12 -5
  20. package/components/widgets/style/NavBar.css +0 -1
  21. package/icons/circle_full.svg +75 -0
  22. package/icons/eye-slash.svg +138 -0
  23. package/icons/pin.svg +41 -0
  24. package/icons/unpin.svg +41 -0
  25. package/package.json +1 -1
  26. package/plugins/AttributeTable.js +4 -0
  27. package/plugins/GeometryDigitizer.js +3 -0
  28. package/plugins/HeightProfile.js +2 -0
  29. package/plugins/Identify.js +12 -7
  30. package/plugins/ObjectList.js +116 -0
  31. package/plugins/Redlining.js +14 -55
  32. package/plugins/map/EditingSupport.js +22 -1
  33. package/plugins/map/LocateSupport.js +8 -1
  34. package/plugins/map/RedliningSupport.js +108 -18
  35. package/plugins/map/SnappingSupport.js +11 -6
  36. package/plugins/map/style/SnappingSupport.css +2 -13
  37. package/reducers/locate.js +4 -2
  38. package/reducers/redlining.js +2 -1
  39. package/static/translations/bg-BG.json +16 -1
  40. package/static/translations/ca-ES.json +16 -1
  41. package/static/translations/cs-CZ.json +15 -0
  42. package/static/translations/de-CH.json +17 -2
  43. package/static/translations/de-DE.json +18 -3
  44. package/static/translations/en-US.json +16 -1
  45. package/static/translations/es-ES.json +15 -0
  46. package/static/translations/fi-FI.json +15 -0
  47. package/static/translations/fr-FR.json +16 -1
  48. package/static/translations/hu-HU.json +15 -0
  49. package/static/translations/it-IT.json +16 -1
  50. package/static/translations/ja-JP.json +16 -1
  51. package/static/translations/nl-NL.json +15 -0
  52. package/static/translations/no-NO.json +15 -0
  53. package/static/translations/pl-PL.json +15 -0
  54. package/static/translations/pt-BR.json +15 -0
  55. package/static/translations/pt-PT.json +15 -0
  56. package/static/translations/ro-RO.json +15 -0
  57. package/static/translations/ru-RU.json +15 -0
  58. package/static/translations/sv-SE.json +15 -0
  59. package/static/translations/tr-TR.json +15 -0
  60. package/static/translations/tsconfig.json +12 -0
  61. package/static/translations/uk-UA.json +15 -0
  62. package/utils/EditingInterface.js +4 -1
  63. package/utils/EditingUtils.js +3 -1
  64. package/utils/MiscUtils.js +118 -0
  65. package/utils/expr_grammar/grammar.js +104 -22
  66. package/utils/expr_grammar/grammar.ne +2 -0
  67. package/utils/expr_grammar/test.js +21 -4
@@ -0,0 +1,10 @@
1
+ div.LocationRecorder {
2
+ display: inline-flex;
3
+ align-items: center;
4
+ padding: 0.25em 0.5em;
5
+ background-color: var(--container-bg-color);
6
+ box-shadow: 0px -2px 4px rgba(136, 136, 136, 0.5);
7
+ border-bottom: 1px solid rgba(136, 136, 136, 0.5);
8
+ column-gap: 0.25em;
9
+ order: 1;
10
+ }
@@ -8,4 +8,11 @@ div.numeric-input-widget-body > table input {
8
8
 
9
9
  div.numeric-input-widget-body > table tr:hover {
10
10
  background-color: var(--list-item-bg-color-hover);
11
+ }
12
+
13
+ div.numeric-input-widget-body > table td:nth-child(1),
14
+ div.numeric-input-widget-body > table td:nth-child(2),
15
+ div.numeric-input-widget-body > table td:nth-child(4),
16
+ div.numeric-input-widget-body > table td:nth-child(6) {
17
+ width: 1%;
11
18
  }
@@ -25,6 +25,22 @@ div.app-infos-container {
25
25
  row-gap: 0.25em;
26
26
  }
27
27
 
28
+ div.map-bottom-tool-container {
29
+ z-index: 3;
30
+ position: absolute;
31
+ pointer-events: none;
32
+ display: flex;
33
+ flex-direction: column-reverse;
34
+ align-items: center;
35
+ align-content: flex-start;
36
+ justify-content: flex-start;
37
+ margin-bottom: -1px;
38
+ }
39
+
40
+ div.map-bottom-tool-container > * {
41
+ pointer-events: initial;
42
+ }
43
+
28
44
  div.app-info {
29
45
  z-index: 1;
30
46
  color: var(--panel-text-color);
@@ -38,6 +38,7 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
38
38
  _this = _callSuper(this, LayerCatalogWidget, [props]);
39
39
  _defineProperty(_this, "state", {
40
40
  catalog: [],
41
+ filteredCatalog: null,
41
42
  filter: ""
42
43
  });
43
44
  _defineProperty(_this, "toggleLayerListEntry", function (path) {
@@ -64,6 +65,35 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
64
65
  };
65
66
  });
66
67
  });
68
+ _defineProperty(_this, "setFilter", function (text) {
69
+ _this.setState(function (state) {
70
+ if (!text) {
71
+ return {
72
+ filter: text,
73
+ filteredCatalog: null
74
+ };
75
+ }
76
+ var filter = new RegExp(removeDiacritics(text).replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"), "i");
77
+ var _filterCatalogEntry = function filterCatalogEntry(res, entry) {
78
+ if (entry.sublayers) {
79
+ var newEntry = _objectSpread(_objectSpread({}, entry), {}, {
80
+ sublayers: entry.sublayers.reduce(_filterCatalogEntry, []),
81
+ expanded: true
82
+ });
83
+ return newEntry.sublayers.length > 0 ? [].concat(_toConsumableArray(res), [newEntry]) : res;
84
+ } else if (removeDiacritics(entry.title).match(filter)) {
85
+ return [].concat(_toConsumableArray(res), [entry]);
86
+ } else {
87
+ return res;
88
+ }
89
+ };
90
+ var filteredCatalog = state.catalog.reduce(_filterCatalogEntry, []);
91
+ return {
92
+ filter: text,
93
+ filteredCatalog: filteredCatalog
94
+ };
95
+ });
96
+ });
67
97
  _defineProperty(_this, "addServiceLayer", function (entry) {
68
98
  var asGroup = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
69
99
  if (entry.resource) {
@@ -116,19 +146,14 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
116
146
  }
117
147
  }, {
118
148
  key: "renderCatalogEntry",
119
- value: function renderCatalogEntry(entry, filter, path) {
149
+ value: function renderCatalogEntry(entry, path) {
120
150
  var _this2 = this;
121
- var level = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
122
- var idx = arguments.length > 4 ? arguments[4] : undefined;
151
+ var level = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
152
+ var idx = arguments.length > 3 ? arguments[3] : undefined;
123
153
  var hasSublayers = !isEmpty(entry.sublayers);
124
154
  var sublayers = hasSublayers ? entry.sublayers.map(function (sublayer, i) {
125
- return _this2.renderCatalogEntry(sublayer, filter, [].concat(_toConsumableArray(path), [i]), level + 1, i);
155
+ return _this2.renderCatalogEntry(sublayer, [].concat(_toConsumableArray(path), [i]), level + 1, i);
126
156
  }) : [];
127
- if (sublayers.filter(function (item) {
128
- return item;
129
- }).length === 0 && filter && !removeDiacritics(entry.title).match(filter)) {
130
- return null;
131
- }
132
157
  var type = entry.resource ? entry.resource.slice(0, entry.resource.indexOf(":")) : entry.type;
133
158
  var key = (entry.resource || entry.type + ":" + entry.name) + ":" + idx;
134
159
  var indentSize = !this.props.levelBasedIndentSize && level > 0 ? 1.5 : level;
@@ -164,8 +189,8 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
164
189
  }, {
165
190
  key: "render",
166
191
  value: function render() {
167
- var _this3 = this;
168
- var filter = new RegExp(removeDiacritics(this.state.filter).replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"), "i");
192
+ var _this$state$filteredC,
193
+ _this3 = this;
169
194
  var emptyEntry = null;
170
195
  if (isEmpty(this.state.catalog) && this.props.pendingRequests === 0) {
171
196
  emptyEntry = /*#__PURE__*/React.createElement("div", {
@@ -177,15 +202,14 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
177
202
  }, LocaleUtils.tr("importlayer.loading"));
178
203
  }
179
204
  var filterplaceholder = LocaleUtils.tr("importlayer.filter");
205
+ var catalog = (_this$state$filteredC = this.state.filteredCatalog) !== null && _this$state$filteredC !== void 0 ? _this$state$filteredC : this.state.catalog;
180
206
  return /*#__PURE__*/React.createElement("div", {
181
207
  className: "layer-catalog-widget"
182
208
  }, /*#__PURE__*/React.createElement(InputContainer, {
183
209
  className: "layer-catalog-widget-filter"
184
210
  }, /*#__PURE__*/React.createElement("input", {
185
211
  onChange: function onChange(ev) {
186
- return _this3.setState({
187
- filter: ev.target.value
188
- });
212
+ return _this3.setFilter(ev.target.value);
189
213
  },
190
214
  placeholder: filterplaceholder,
191
215
  role: "input",
@@ -201,8 +225,8 @@ var LayerCatalogWidget = /*#__PURE__*/function (_React$PureComponent) {
201
225
  role: "suffix"
202
226
  })), /*#__PURE__*/React.createElement("div", {
203
227
  className: "layer-catalog-widget-body"
204
- }, this.state.catalog.map(function (entry, idx) {
205
- return _this3.renderCatalogEntry(entry, filter, [idx], 0, idx);
228
+ }, catalog.map(function (entry, idx) {
229
+ return _this3.renderCatalogEntry(entry, [idx], 0, idx);
206
230
  }), emptyEntry));
207
231
  }
208
232
  }]);
@@ -21,6 +21,7 @@ function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e
21
21
  */
22
22
 
23
23
  import React from 'react';
24
+ import classNames from 'classnames';
24
25
  import PropTypes from 'prop-types';
25
26
  import LocaleUtils from '../../utils/LocaleUtils';
26
27
  import Icon from '../Icon';
@@ -40,7 +41,11 @@ var NavBar = /*#__PURE__*/function (_React$Component) {
40
41
  key: idx
41
42
  }, "...");
42
43
  }
43
- var className = "button" + (page === _this.props.currentPage ? " pressed" : "");
44
+ var className = classNames({
45
+ button: true,
46
+ pressed: page === _this.props.currentPage,
47
+ selected: _this.props.selectedPages.includes(page)
48
+ });
44
49
  return /*#__PURE__*/React.createElement("button", {
45
50
  className: className,
46
51
  disabled: _this.props.disabled,
@@ -94,7 +99,7 @@ var NavBar = /*#__PURE__*/function (_React$Component) {
94
99
  }
95
100
  }, /*#__PURE__*/React.createElement(Icon, {
96
101
  icon: "chevron-right"
97
- })), /*#__PURE__*/React.createElement("select", {
102
+ })), this.props.pageSizes.length > 1 ? /*#__PURE__*/React.createElement("select", {
98
103
  disabled: this.props.disabled,
99
104
  onChange: function onChange(ev) {
100
105
  return _this2.props.pageSizeChanged(parseInt(ev.target.value, 10));
@@ -105,7 +110,7 @@ var NavBar = /*#__PURE__*/function (_React$Component) {
105
110
  key: pageSize,
106
111
  value: pageSize
107
112
  }, pageSize, " ", LocaleUtils.tr("navbar.perpage"));
108
- })));
113
+ })) : null);
109
114
  }
110
115
  }]);
111
116
  }(React.Component);
@@ -116,9 +121,11 @@ _defineProperty(NavBar, "propTypes", {
116
121
  pageChanged: PropTypes.func,
117
122
  pageSize: PropTypes.number,
118
123
  pageSizeChanged: PropTypes.func,
119
- pageSizes: PropTypes.array
124
+ pageSizes: PropTypes.array,
125
+ selectedPages: PropTypes.array
120
126
  });
121
127
  _defineProperty(NavBar, "defaultProps", {
122
- pageSizes: [10, 25, 50, 100]
128
+ pageSizes: [10, 25, 50, 100],
129
+ selectedPages: []
123
130
  });
124
131
  export { NavBar as default };
@@ -1,6 +1,5 @@
1
1
  div.navbar {
2
2
  display: inline-flex;
3
- margin-right: 1em;
4
3
  }
5
4
 
6
5
  div.navbar > select {
@@ -0,0 +1,75 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Generated by IcoMoon.io -->
3
+
4
+ <svg
5
+ version="1.1"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 24 24"
9
+ id="svg6"
10
+ sodipodi:docname="record.svg"
11
+ inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
12
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
13
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
14
+ xmlns="http://www.w3.org/2000/svg"
15
+ xmlns:svg="http://www.w3.org/2000/svg"
16
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
17
+ xmlns:cc="http://creativecommons.org/ns#"
18
+ xmlns:dc="http://purl.org/dc/elements/1.1/">
19
+ <metadata
20
+ id="metadata12">
21
+ <rdf:RDF>
22
+ <cc:Work
23
+ rdf:about="">
24
+ <dc:format>image/svg+xml</dc:format>
25
+ <dc:type
26
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
27
+ <dc:title>uniE250</dc:title>
28
+ </cc:Work>
29
+ </rdf:RDF>
30
+ </metadata>
31
+ <defs
32
+ id="defs10" />
33
+ <sodipodi:namedview
34
+ pagecolor="#ffffff"
35
+ bordercolor="#666666"
36
+ borderopacity="1"
37
+ objecttolerance="10"
38
+ gridtolerance="10"
39
+ guidetolerance="10"
40
+ inkscape:pageopacity="0"
41
+ inkscape:pageshadow="2"
42
+ inkscape:window-width="1920"
43
+ inkscape:window-height="1172"
44
+ id="namedview8"
45
+ showgrid="true"
46
+ inkscape:zoom="20.85965"
47
+ inkscape:cx="11.697224"
48
+ inkscape:cy="10.810344"
49
+ inkscape:window-x="0"
50
+ inkscape:window-y="0"
51
+ inkscape:window-maximized="1"
52
+ inkscape:current-layer="svg6"
53
+ inkscape:pagecheckerboard="0"
54
+ inkscape:showpageshadow="2"
55
+ inkscape:deskcolor="#d1d1d1">
56
+ <inkscape:grid
57
+ type="xygrid"
58
+ id="grid823"
59
+ originx="0"
60
+ originy="0"
61
+ spacingy="1"
62
+ spacingx="1"
63
+ units="px" />
64
+ </sodipodi:namedview>
65
+ <title
66
+ id="title2">uniE250</title>
67
+ <rect
68
+ style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:2;stop-color:#000000"
69
+ id="rect866"
70
+ width="18"
71
+ height="18"
72
+ x="3"
73
+ y="3"
74
+ ry="9" />
75
+ </svg>
@@ -0,0 +1,138 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+
4
+ <svg
5
+ version="1.1"
6
+ id="Capa_1"
7
+ x="0px"
8
+ y="0px"
9
+ width="24"
10
+ height="24"
11
+ viewBox="0 0 24 24"
12
+ xml:space="preserve"
13
+ sodipodi:docname="eye-slash.svg"
14
+ inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
15
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
16
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
17
+ xmlns="http://www.w3.org/2000/svg"
18
+ xmlns:svg="http://www.w3.org/2000/svg"
19
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
20
+ xmlns:cc="http://creativecommons.org/ns#"
21
+ xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata
22
+ id="metadata47"><rdf:RDF><cc:Work
23
+ rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
24
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
25
+ id="defs45" /><sodipodi:namedview
26
+ pagecolor="#ffffff"
27
+ bordercolor="#666666"
28
+ borderopacity="1"
29
+ objecttolerance="10"
30
+ gridtolerance="10"
31
+ guidetolerance="10"
32
+ inkscape:pageopacity="0"
33
+ inkscape:pageshadow="2"
34
+ inkscape:window-width="1920"
35
+ inkscape:window-height="1172"
36
+ id="namedview43"
37
+ showgrid="true"
38
+ inkscape:zoom="30.603581"
39
+ inkscape:cx="12.759945"
40
+ inkscape:cy="13.315435"
41
+ inkscape:window-x="0"
42
+ inkscape:window-y="0"
43
+ inkscape:window-maximized="1"
44
+ inkscape:current-layer="g8"
45
+ inkscape:showpageshadow="2"
46
+ inkscape:pagecheckerboard="0"
47
+ inkscape:deskcolor="#d1d1d1"><inkscape:grid
48
+ id="grid1"
49
+ units="px"
50
+ originx="0"
51
+ originy="0"
52
+ spacingx="1"
53
+ spacingy="1"
54
+ empcolor="#0099e5"
55
+ empopacity="0.30196078"
56
+ color="#0099e5"
57
+ opacity="0.14901961"
58
+ empspacing="5"
59
+ enabled="true"
60
+ visible="true" /></sodipodi:namedview>
61
+ <g
62
+ id="g10"
63
+ transform="matrix(0.04424334,0,0,0.04424334,1.1456098,0.91230727)">
64
+ <g
65
+ id="g8">
66
+ <path
67
+ d="m 264.45888,248.625 a 19.125,19.125 0 0 1 -19.125,19.125 19.125,19.125 0 0 1 -19.125,-19.125 19.125,19.125 0 0 1 19.125,-19.125 19.125,19.125 0 0 1 19.125,19.125 z"
68
+ id="circle2" />
69
+ <path
70
+ d="m 245.33388,172.125 c -42.075,0 -76.5,34.425 -76.5,76.5 0,42.075 34.425,76.5 76.5,76.5 42.075,0 76.5,-34.425 76.5,-76.5 0,-42.075 -34.425,-76.5 -76.5,-76.5 z m 0,133.875 c -32.513,0 -57.375,-24.862 -57.375,-57.375 0,-32.513 24.862,-57.375 57.375,-57.375 32.513,0 57.375,24.862 57.375,57.375 0,32.513 -24.862,57.375 -57.375,57.375 z"
71
+ id="path4"
72
+ inkscape:connector-curvature="0" />
73
+ <path
74
+ id="path6"
75
+ d="M 471.93048 1.9820549 A 22.602272 22.602272 0 0 0 466.05919 2.6000858 A 22.602272 22.602272 0 0 0 455.77339 8.2065087 L 347.30897 111.24108 C 316.97091 99.588181 282.89825 92.126272 245.33388 92.126272 C 73.208879 92.126272 -25.893384 248.62052 -25.893384 248.62052 C -25.893384 248.62052 14.167601 311.89415 87.603571 357.96784 L 3.7279526 437.64967 A 22.602272 22.602272 0 0 0 2.9333415 469.6107 A 22.602272 22.602272 0 0 0 34.894367 470.40531 L 129.67382 380.34938 C 163.32244 395.17836 202.0213 405.11476 245.33388 405.11476 C 417.45888 405.11476 516.56114 248.62052 516.56114 248.62052 C 516.56114 248.62052 472.0872 178.43975 391.01258 132.07755 L 486.9398 40.962145 A 22.602272 22.602272 0 0 0 487.73442 9.0011198 A 22.602272 22.602272 0 0 0 471.93048 1.9820549 z M 245.33388 133.8875 C 270.63714 133.8875 293.74181 136.992 314.68577 142.23092 L 115.19423 331.74567 C 50.776781 297.77898 19.66432 248.62052 19.66432 248.62052 C 19.66432 248.62052 92.333879 133.8875 245.33388 133.8875 z M 362.53902 159.13848 C 435.70043 192.92371 471.00344 248.62052 471.00344 248.62052 C 471.00344 248.62052 398.33388 363.35353 245.33388 363.35353 C 213.79639 363.35353 185.66308 358.46918 160.88437 350.72805 L 362.53902 159.13848 z " />
76
+ </g>
77
+ </g>
78
+ <g
79
+ id="g12"
80
+ transform="translate(0,-473.25)">
81
+ </g>
82
+ <g
83
+ id="g14"
84
+ transform="translate(0,-473.25)">
85
+ </g>
86
+ <g
87
+ id="g16"
88
+ transform="translate(0,-473.25)">
89
+ </g>
90
+ <g
91
+ id="g18"
92
+ transform="translate(0,-473.25)">
93
+ </g>
94
+ <g
95
+ id="g20"
96
+ transform="translate(0,-473.25)">
97
+ </g>
98
+ <g
99
+ id="g22"
100
+ transform="translate(0,-473.25)">
101
+ </g>
102
+ <g
103
+ id="g24"
104
+ transform="translate(0,-473.25)">
105
+ </g>
106
+ <g
107
+ id="g26"
108
+ transform="translate(0,-473.25)">
109
+ </g>
110
+ <g
111
+ id="g28"
112
+ transform="translate(0,-473.25)">
113
+ </g>
114
+ <g
115
+ id="g30"
116
+ transform="translate(0,-473.25)">
117
+ </g>
118
+ <g
119
+ id="g32"
120
+ transform="translate(0,-473.25)">
121
+ </g>
122
+ <g
123
+ id="g34"
124
+ transform="translate(0,-473.25)">
125
+ </g>
126
+ <g
127
+ id="g36"
128
+ transform="translate(0,-473.25)">
129
+ </g>
130
+ <g
131
+ id="g38"
132
+ transform="translate(0,-473.25)">
133
+ </g>
134
+ <g
135
+ id="g40"
136
+ transform="translate(0,-473.25)">
137
+ </g>
138
+ </svg>
package/icons/pin.svg ADDED
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+
4
+ <svg
5
+ fill="#000000"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 1.68 1.68"
9
+ version="1.1"
10
+ id="svg1"
11
+ sodipodi:docname="pin.svg"
12
+ inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
13
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
14
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ xmlns:svg="http://www.w3.org/2000/svg">
17
+ <defs
18
+ id="defs1" />
19
+ <sodipodi:namedview
20
+ id="namedview1"
21
+ pagecolor="#ffffff"
22
+ bordercolor="#666666"
23
+ borderopacity="1.0"
24
+ inkscape:showpageshadow="2"
25
+ inkscape:pageopacity="0.0"
26
+ inkscape:pagecheckerboard="0"
27
+ inkscape:deskcolor="#d1d1d1"
28
+ inkscape:zoom="5.544301"
29
+ inkscape:cx="16.593616"
30
+ inkscape:cy="45.361895"
31
+ inkscape:window-width="1600"
32
+ inkscape:window-height="842"
33
+ inkscape:window-x="0"
34
+ inkscape:window-y="0"
35
+ inkscape:window-maximized="1"
36
+ inkscape:current-layer="svg1" />
37
+ <path
38
+ d="m 0.423659,1.0813605 h 0.354229 v 0.3648746 c 0,0.1079012 0.04472,0.1987655 0.06247,0.1987655 0.01774,0 0.06247,-0.090864 0.06247,-0.1987655 V 1.0813605 h 0.35352 c 0.04898,0 0.08447,-0.031236 0.08447,-0.078797 0,-0.029815 -0.0092,-0.051111 -0.02981,-0.073115 L 1.093783,0.69376685 c -0.01491,-0.015617 -0.02414,-0.026975 -0.02059,-0.056082 l 0.0362,-0.2591025 c 0.0021,-0.014908 0.0035,-0.023428 0.01633,-0.032657 l 0.174629,-0.1263559 c 0.03904,-0.028398 0.05608,-0.063181 0.05608,-0.095126 0,-0.047562 -0.03834,-0.089444 -0.09299,-0.089444 H 0.416558 c -0.05466,0 -0.09299,0.041883 -0.09299,0.089444 0,0.031945 0.01704,0.066728 0.05537,0.095126 l 0.174629,0.1263559 c 0.01349,0.00923 0.01491,0.017749 0.01704,0.032657 l 0.03621,0.2591025 c 0.0035,0.029107 -0.0057,0.040465 -0.02059,0.056082 l -0.217224,0.2356807 c -0.02059,0.022004 -0.02981,0.0433 -0.02981,0.0731151 0,0.047562 0.03549,0.078797 0.08447,0.078797 z"
39
+ id="path1"
40
+ style="stroke-width:0.0302881" />
41
+ </svg>
@@ -0,0 +1,41 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
3
+
4
+ <svg
5
+ fill="#000000"
6
+ width="24"
7
+ height="24"
8
+ viewBox="0 0 1.68 1.68"
9
+ version="1.1"
10
+ id="svg1"
11
+ sodipodi:docname="unpin.svg"
12
+ inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
13
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
14
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
15
+ xmlns="http://www.w3.org/2000/svg"
16
+ xmlns:svg="http://www.w3.org/2000/svg">
17
+ <defs
18
+ id="defs1" />
19
+ <sodipodi:namedview
20
+ id="namedview1"
21
+ pagecolor="#ffffff"
22
+ bordercolor="#666666"
23
+ borderopacity="1.0"
24
+ inkscape:showpageshadow="2"
25
+ inkscape:pageopacity="0.0"
26
+ inkscape:pagecheckerboard="0"
27
+ inkscape:deskcolor="#d1d1d1"
28
+ inkscape:zoom="6.2020459"
29
+ inkscape:cx="11.286598"
30
+ inkscape:cy="9.4323713"
31
+ inkscape:window-width="1920"
32
+ inkscape:window-height="1052"
33
+ inkscape:window-x="0"
34
+ inkscape:window-y="0"
35
+ inkscape:window-maximized="1"
36
+ inkscape:current-layer="svg1" />
37
+ <path
38
+ d="m 1.309234,0.2833384 c 0.020817,-0.020817 0.020817,-0.053729 0,-0.073201 -0.020145,-0.020133 -0.052379,-0.020808 -0.073196,0 L 0.05010853,1.3967415 c -0.0201447,0.020148 -0.0201447,0.053723 0,0.073868 0.0194756,0.019476 0.053723,0.019476 0.0731986,0 z M 0.72835739,0.6090335 0.44698636,0.5700852 c -0.0147747,-0.00134 -0.0235045,-0.00403 -0.0322344,-0.01746 L 0.29656305,0.3894434 c -0.0268615,-0.036935 -0.0597679,-0.052379 -0.0899864,-0.052379 -0.0449932,0 -0.0846135,0.035591 -0.0846135,0.08797 V 1.2154277 Z M 1.1118045,1.2664657 V 0.90786711 h 0.3451678 c 0.1020703,0 0.1880277,-0.04298 0.1880277,-0.059096 0,-0.016788 -0.085957,-0.059768 -0.1880277,-0.059768 H 1.1118045 V 0.6003034 l -0.50499589,0.504993 0.0852854,-0.011415 c 0.0275335,-0.00403 0.0382792,0.00537 0.053051,0.018801 l 0.22295,0.2054903 c 0.020817,0.019476 0.040964,0.028202 0.06917,0.028202 0.04499,0 0.07454,-0.032903 0.07454,-0.07991 z"
39
+ id="path1"
40
+ style="stroke-width:0.0288399" />
41
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2025.10.15",
3
+ "version": "2025.10.24",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -75,6 +75,7 @@ var AttributeTable = /*#__PURE__*/function (_React$Component) {
75
75
  allowAddForGeometryLayers: this.props.allowAddForGeometryLayers,
76
76
  iface: this.props.iface,
77
77
  initialLayer: (_this$props$taskData = this.props.taskData) === null || _this$props$taskData === void 0 ? void 0 : _this$props$taskData.layer,
78
+ limitToExtent: this.props.limitToExtent,
78
79
  role: "body",
79
80
  showEditFormButton: this.props.showEditFormButton,
80
81
  showHiddenFields: this.props.showHiddenFields,
@@ -90,6 +91,8 @@ _defineProperty(AttributeTable, "propTypes", {
90
91
  allowAddForGeometryLayers: PropTypes.bool,
91
92
  blocked: PropTypes.bool,
92
93
  iface: PropTypes.object,
94
+ /** Whether to limit to the extent by default. */
95
+ limitToExtent: PropTypes.bool,
93
96
  setCurrentTask: PropTypes.func,
94
97
  /** Whether to show a button to open the edit form for selected layer. Requires the Editing plugin to be enabled. */
95
98
  showEditFormButton: PropTypes.bool,
@@ -102,6 +105,7 @@ _defineProperty(AttributeTable, "propTypes", {
102
105
  zoomLevel: PropTypes.number
103
106
  });
104
107
  _defineProperty(AttributeTable, "defaultProps", {
108
+ limitToExtent: false,
105
109
  zoomLevel: 1000,
106
110
  showEditFormButton: true,
107
111
  showHiddenFields: true,
@@ -622,6 +622,9 @@ var GeometryDigitizer = /*#__PURE__*/function (_React$Component) {
622
622
  }), this.renderOutputWindow(), this.state.pickGeomType ? /*#__PURE__*/React.createElement(PickFeature, {
623
623
  featurePicked: this.selectFeature,
624
624
  key: "FeaturePicker",
625
+ layerFilterFunc: function layerFilterFunc(layer) {
626
+ return !layer.id.startsWith("__geomdigitizer");
627
+ },
625
628
  pickGeomType: this.state.pickGeomType
626
629
  }) : null];
627
630
  }
@@ -208,6 +208,8 @@ var HeightProfilePrintDialog_ = /*#__PURE__*/function (_React$PureComponent) {
208
208
  value: function componentDidMount() {
209
209
  var templatePath = MiscUtils.resolveAssetsPath(this.props.templatePath);
210
210
  this.externalWindow = window.open(templatePath, LocaleUtils.tr("heightprofile.title"), "toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=yes, resizable=yes");
211
+ // Inherit API
212
+ this.externalWindow.qwc2 = window.qwc2;
211
213
  this.externalWindow.addEventListener('load', this.setWindowContent, false);
212
214
  this.externalWindow.addEventListener('resize', this.windowResized, false);
213
215
  window.addEventListener('beforeunload', this.closePrintWindow);
@@ -94,7 +94,7 @@ var Identify = /*#__PURE__*/function (_React$Component) {
94
94
  };
95
95
  });
96
96
  if (response) {
97
- _this.parseResult(response, l, request.params.info_format, clickPoint);
97
+ _this.parseResult(response, l, request.params.info_format, clickPoint, _this.props.click.modifiers.ctrl);
98
98
  }
99
99
  });
100
100
  });
@@ -229,6 +229,7 @@ var Identify = /*#__PURE__*/function (_React$Component) {
229
229
  });
230
230
  });
231
231
  _defineProperty(_this, "parseResult", function (response, layer, format, clickPoint) {
232
+ var ctrlPick = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : false;
232
233
  var newResults = IdentifyUtils.parseResponse(response, layer, format, clickPoint, _this.props.map.projection, _this.props.featureInfoReturnsLayerName);
233
234
  // Merge with previous
234
235
  _this.setState(function (state) {
@@ -239,10 +240,13 @@ var Identify = /*#__PURE__*/function (_React$Component) {
239
240
  features = _ref4[1];
240
241
  var key = layer.url + "#" + layername;
241
242
  identifyResults[key] = features.reduce(function (result, feature) {
242
- if (result.find(function (f) {
243
+ var idx = result.findIndex(function (f) {
243
244
  return f.id === feature.id;
244
- }) === undefined) {
245
+ });
246
+ if (idx === -1) {
245
247
  result.push(feature);
248
+ } else if (ctrlPick === true) {
249
+ result.splice(idx, 1);
246
250
  }
247
251
  return result;
248
252
  }, identifyResults[key] || []);
@@ -403,8 +407,8 @@ var Identify = /*#__PURE__*/function (_React$Component) {
403
407
  attributeCalculator: this.props.attributeCalculator,
404
408
  attributeTransform: this.props.attributeTransform,
405
409
  customExporters: this.props.customExporters,
406
- displayResultTree: this.props.displayResultTree,
407
410
  enableAggregatedReports: this.props.enableAggregatedReports,
411
+ enableCompare: true,
408
412
  enableExport: this.props.enableExport,
409
413
  exportGeometry: this.props.exportGeometry,
410
414
  highlightAllResults: this.props.highlightAllResults,
@@ -412,6 +416,7 @@ var Identify = /*#__PURE__*/function (_React$Component) {
412
416
  iframeDialogsInitiallyDocked: this.props.iframeDialogsInitiallyDocked,
413
417
  longAttributesDisplay: this.props.longAttributesDisplay,
414
418
  replaceImageUrls: this.props.replaceImageUrls,
419
+ resultDisplayMode: this.props.resultDisplayMode,
415
420
  role: "body",
416
421
  showLayerSelector: this.props.showLayerSelector
417
422
  });
@@ -469,8 +474,6 @@ _defineProperty(Identify, "propTypes", {
469
474
  currentTask: PropTypes.string,
470
475
  /** Optional list of custom exporters to offer along with the built-in exporters. See js/IdentifyExtensions.js for details. This prop can be specified in the appConfig.js cfg section. */
471
476
  customExporters: PropTypes.array,
472
- /** Whether to display a tree overview of results (as opposed to a flat list of results). */
473
- displayResultTree: PropTypes.bool,
474
477
  /** Whether to enable the aggregated report download button. */
475
478
  enableAggregatedReports: PropTypes.bool,
476
479
  /** 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. */
@@ -505,6 +508,8 @@ _defineProperty(Identify, "propTypes", {
505
508
  removeMarker: PropTypes.func,
506
509
  /** Whether to replace an attribute value containing an URL to an image with an inline image. */
507
510
  replaceImageUrls: PropTypes.bool,
511
+ /** Result display mode, one of `tree`, `flat`, `paginated`. */
512
+ resultDisplayMode: PropTypes.string,
508
513
  selection: PropTypes.object,
509
514
  setCurrentTask: PropTypes.func,
510
515
  /** Whether to show a layer selector to filter the identify results by layer. */
@@ -519,7 +524,7 @@ _defineProperty(Identify, "defaultProps", {
519
524
  clearResultsOnClose: true,
520
525
  customExporters: [],
521
526
  longAttributesDisplay: 'ellipsis',
522
- displayResultTree: true,
527
+ resultDisplayMode: 'flat',
523
528
  replaceImageUrls: true,
524
529
  featureInfoReturnsLayerName: true,
525
530
  geometry: {