qwc2 2025.12.4 → 2025.12.17

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 (57) hide show
  1. package/components/IdentifyViewer.js +89 -35
  2. package/components/ResizeableWindow.js +8 -0
  3. package/components/SearchBox.js +12 -5
  4. package/components/SideBar.js +4 -0
  5. package/components/style/IdentifyViewer.css +7 -5
  6. package/components/widgets/EditableSelect.js +2 -1
  7. package/components/widgets/LayerCatalogWidget.js +25 -14
  8. package/components/widgets/MenuButton.js +7 -2
  9. package/components/widgets/NavBar.js +4 -2
  10. package/components/widgets/PopupMenu.js +44 -13
  11. package/components/widgets/SearchWidget.js +39 -50
  12. package/components/widgets/style/SearchWidget.css +3 -19
  13. package/icons/oblique.svg +53 -0
  14. package/package.json +3 -2
  15. package/plugins/Cyclomedia.js +2 -1
  16. package/plugins/HeightProfile.js +4 -1
  17. package/plugins/Identify.js +4 -0
  18. package/plugins/LayerTree.js +5 -1
  19. package/plugins/Measure.js +5 -1
  20. package/plugins/ObliqueView.js +410 -0
  21. package/plugins/Print.js +1 -1
  22. package/plugins/TopBar.js +2 -0
  23. package/plugins/map/MeasurementSupport.js +1 -0
  24. package/plugins/map/RedliningSupport.js +7 -5
  25. package/plugins/style/Map.css +4 -2
  26. package/plugins/style/ObliqueView.css +112 -0
  27. package/reducers/layers.js +18 -36
  28. package/reducers/measurement.js +2 -1
  29. package/scripts/themesConfig.js +1 -0
  30. package/scripts/themesConfig.py +2 -0
  31. package/scripts/wmts_config_generator.py +1 -1
  32. package/static/translations/bg-BG.json +11 -1
  33. package/static/translations/ca-ES.json +11 -1
  34. package/static/translations/cs-CZ.json +11 -1
  35. package/static/translations/de-CH.json +11 -1
  36. package/static/translations/de-DE.json +11 -1
  37. package/static/translations/en-US.json +11 -1
  38. package/static/translations/es-ES.json +11 -1
  39. package/static/translations/fi-FI.json +11 -1
  40. package/static/translations/fr-FR.json +12 -2
  41. package/static/translations/hu-HU.json +11 -1
  42. package/static/translations/it-IT.json +11 -1
  43. package/static/translations/ja-JP.json +11 -1
  44. package/static/translations/nl-NL.json +11 -1
  45. package/static/translations/no-NO.json +11 -1
  46. package/static/translations/pl-PL.json +11 -1
  47. package/static/translations/pt-BR.json +11 -1
  48. package/static/translations/pt-PT.json +11 -1
  49. package/static/translations/ro-RO.json +11 -1
  50. package/static/translations/ru-RU.json +11 -1
  51. package/static/translations/sv-SE.json +11 -1
  52. package/static/translations/tr-TR.json +11 -1
  53. package/static/translations/tsconfig.json +8 -0
  54. package/static/translations/uk-UA.json +11 -1
  55. package/utils/FeatureStyles.js +5 -2
  56. package/utils/MiscUtils.js +2 -1
  57. package/utils/VectorLayerUtils.js +14 -5
@@ -34,11 +34,11 @@ import isEmpty from 'lodash.isempty';
34
34
  import PropTypes from 'prop-types';
35
35
  import { v4 as uuidv4 } from 'uuid';
36
36
  import LocaleUtils from '../../utils/LocaleUtils';
37
- import MiscUtils from '../../utils/MiscUtils';
38
37
  import { SearchResultType } from '../../utils/SearchProviders';
39
38
  import VectorLayerUtils from '../../utils/VectorLayerUtils';
40
39
  import Icon from '../Icon';
41
40
  import InputContainer from './InputContainer';
41
+ import PopupMenu from './PopupMenu';
42
42
  import Spinner from './Spinner';
43
43
  import './style/SearchWidget.css';
44
44
  var SearchWidget = /*#__PURE__*/function (_React$Component) {
@@ -51,42 +51,40 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
51
51
  reqId: null,
52
52
  results: [],
53
53
  pending: 0,
54
- active: false
54
+ resultsVisible: false
55
55
  });
56
56
  _defineProperty(_this, "renderResults", function () {
57
- return /*#__PURE__*/React.createElement("div", {
57
+ return /*#__PURE__*/React.createElement(PopupMenu, {
58
+ anchor: _this.input,
58
59
  className: "search-widget-results",
59
- onMouseDown: _this.setPreventBlur
60
+ onClose: function onClose() {
61
+ return _this.setState({
62
+ resultsVisible: false
63
+ });
64
+ },
65
+ setMaxWidth: true,
66
+ spaceKeyActivation: false
60
67
  }, _this.state.results.filter(function (group) {
61
68
  var _group$type;
62
69
  return _this.props.resultTypeFilter.includes((_group$type = group.type) !== null && _group$type !== void 0 ? _group$type : SearchResultType.PLACE);
63
70
  }).map(function (group) {
64
71
  var _group$title;
65
- return /*#__PURE__*/React.createElement("div", {
66
- className: "search-widget-results-group",
67
- key: group.id,
68
- onMouseDown: MiscUtils.killEvent
69
- }, /*#__PURE__*/React.createElement("div", {
70
- className: "search-widget-results-group-title"
72
+ return [/*#__PURE__*/React.createElement("div", {
73
+ className: "search-widget-results-group-title",
74
+ disabled: true,
75
+ key: group.id
71
76
  }, /*#__PURE__*/React.createElement("span", null, (_group$title = group.title) !== null && _group$title !== void 0 ? _group$title : LocaleUtils.tr(group.titlemsgid))), group.items.map(function (item) {
72
77
  item.text = (item.label !== undefined ? item.label : item.text || '').replace(/<\/?\w+\s*\/?>/g, '');
73
78
  return /*#__PURE__*/React.createElement("div", {
74
- className: "search-widget-results-group-item",
75
- key: item.id,
79
+ className: "search-widget-results-item",
80
+ key: group.id + ":" + item.id,
76
81
  onClick: function onClick() {
77
82
  return _this.resultSelected(group, item);
78
83
  },
79
84
  title: item.text
80
85
  }, item.text);
81
- }));
82
- }));
83
- });
84
- _defineProperty(_this, "setPreventBlur", function () {
85
- _this.preventBlur = true;
86
- setTimeout(function () {
87
- _this.preventBlur = false;
88
- return false;
89
- }, 100);
86
+ })];
87
+ }).flat());
90
88
  });
91
89
  _defineProperty(_this, "textChanged", function (ev) {
92
90
  _this.setState({
@@ -102,27 +100,19 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
102
100
  _this.searchTimeout = setTimeout(_this.startSearch, 250);
103
101
  }
104
102
  });
105
- _defineProperty(_this, "onBlur", function () {
106
- if (!_this.preventBlur) {
107
- clearTimeout(_this.searchTimeout);
108
- _this.props.onBlur();
109
- _this.setState({
110
- active: false
111
- });
112
- }
113
- });
114
103
  _defineProperty(_this, "onFocus", function (ev) {
115
- ev.target.select();
116
- _this.props.onFocus();
117
- _this.setState({
118
- active: true
119
- });
104
+ if (_this.input && !_this.state.resultsVisible) {
105
+ ev.target.select();
106
+ }
120
107
  });
121
108
  _defineProperty(_this, "onKeyDown", function (ev) {
122
109
  if (ev.key === 'Enter') {
123
110
  _this.startSearch();
124
- } else if (ev.key === 'Escape') {
125
- ev.target.blur();
111
+ } else if (ev.key === 'ArrowDown' || ev.key === 'ArrowUp') {
112
+ ev.preventDefault();
113
+ _this.setState({
114
+ resultsVisible: true
115
+ });
126
116
  }
127
117
  });
128
118
  _defineProperty(_this, "startSearch", function () {
@@ -149,7 +139,8 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
149
139
  provider: provider
150
140
  });
151
141
  }))),
152
- pending: state.pending - 1
142
+ pending: state.pending - 1,
143
+ resultsVisible: true
153
144
  };
154
145
  });
155
146
  }, axios);
@@ -174,14 +165,12 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
174
165
  } : null
175
166
  }));
176
167
  }
177
- if (_this.input) {
178
- _this.input.blur();
179
- }
180
168
  });
181
169
  _defineProperty(_this, "clear", function () {
182
170
  _this.setState({
183
171
  results: [],
184
- text: ""
172
+ text: "",
173
+ resultsVisible: false
185
174
  });
186
175
  _this.props.resultSelected(null);
187
176
  });
@@ -209,13 +198,17 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
209
198
  }, {
210
199
  key: "render",
211
200
  value: function render() {
212
- var _this$props$placehold,
213
- _this2 = this;
201
+ var _this2 = this,
202
+ _this$props$placehold;
214
203
  return /*#__PURE__*/React.createElement("div", {
215
204
  className: "search-widget-container"
216
205
  }, /*#__PURE__*/React.createElement(InputContainer, null, /*#__PURE__*/React.createElement("input", {
217
- onBlur: this.onBlur,
218
206
  onChange: this.textChanged,
207
+ onClick: function onClick() {
208
+ return _this2.setState({
209
+ resultsVisible: true
210
+ });
211
+ },
219
212
  onFocus: this.onFocus,
220
213
  onKeyDown: this.onKeyDown,
221
214
  placeholder: (_this$props$placehold = this.props.placeholder) !== null && _this$props$placehold !== void 0 ? _this$props$placehold : LocaleUtils.tr("search.search"),
@@ -231,14 +224,12 @@ var SearchWidget = /*#__PURE__*/function (_React$Component) {
231
224
  icon: "clear",
232
225
  onClick: this.clear,
233
226
  role: "suffix"
234
- })), (!isEmpty(this.state.results) || this.state.pending > 0) && this.state.active ? this.renderResults() : null);
227
+ })), (!isEmpty(this.state.results) || this.state.pending > 0) && this.state.resultsVisible ? this.renderResults() : null);
235
228
  }
236
229
  }]);
237
230
  }(React.Component);
238
231
  _defineProperty(SearchWidget, "propTypes", {
239
232
  className: PropTypes.string,
240
- onBlur: PropTypes.func,
241
- onFocus: PropTypes.func,
242
233
  placeholder: PropTypes.string,
243
234
  queryGeometries: PropTypes.bool,
244
235
  resultSelected: PropTypes.func.isRequired,
@@ -251,8 +242,6 @@ _defineProperty(SearchWidget, "propTypes", {
251
242
  value: PropTypes.string
252
243
  });
253
244
  _defineProperty(SearchWidget, "defaultProps", {
254
- onBlur: function onBlur() {},
255
- onFocus: function onFocus() {},
256
245
  resultTypeFilter: [SearchResultType.PLACE],
257
246
  searchParams: {},
258
247
  searchProviders: []
@@ -11,31 +11,15 @@ div.search-widget-container > div.input-container > div.spinner {
11
11
  height: 2em;
12
12
  }
13
13
 
14
- div.search-widget-results {
15
- position: absolute;
16
- left: 0;
17
- right: 0;
18
- top: 100%;
19
- border: 1px solid var(--border-color);
20
- background-color: var(--list-bg-color);
21
- max-height: 10em;
22
- overflow-y: auto;
23
- z-index: 1;
24
- box-shadow: 0px 2px 4px rgba(136, 136, 136, 0.5);
25
- }
26
14
 
27
15
  div.search-widget-results-group-title {
28
16
  background-color: var(--list-section-bg-color);
29
17
  color: var(--list-section-text-color);
30
- font-weight: bold;;
31
- }
32
-
33
- div.search-widget-results-group-item:hover {
34
- background-color: var(--list-item-bg-color-hover);
35
- color: var(--list-item-text-color-hover);
18
+ font-weight: bold;
19
+ padding: 0.25em;
36
20
  }
37
21
 
38
- div.search-widget-results-group div {
22
+ div.search-widget-results-item {
39
23
  padding: 0.25em;
40
24
  white-space: nowrap;
41
25
  overflow: hidden;
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?>
2
+ <svg
3
+ width="24"
4
+ height="24"
5
+ version="1.1"
6
+ id="svg1"
7
+ sodipodi:docname="oblique.svg"
8
+ inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
9
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
10
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11
+ xmlns="http://www.w3.org/2000/svg"
12
+ xmlns:svg="http://www.w3.org/2000/svg">
13
+ <defs
14
+ id="defs1">
15
+ <inkscape:path-effect
16
+ effect="perspective-envelope"
17
+ up_left_point="31.84,8.235"
18
+ up_right_point="118.21,8.235"
19
+ down_left_point="6.84,117.332"
20
+ down_right_point="143.209,117.332"
21
+ id="path-effect1"
22
+ is_visible="true"
23
+ lpeversion="1"
24
+ deform_type="perspective"
25
+ horizontal_mirror="false"
26
+ vertical_mirror="false"
27
+ overflow_perspective="false" />
28
+ </defs>
29
+ <sodipodi:namedview
30
+ id="namedview1"
31
+ pagecolor="#ffffff"
32
+ bordercolor="#666666"
33
+ borderopacity="1.0"
34
+ inkscape:showpageshadow="2"
35
+ inkscape:pageopacity="0.0"
36
+ inkscape:pagecheckerboard="0"
37
+ inkscape:deskcolor="#d1d1d1"
38
+ inkscape:zoom="3.59479"
39
+ inkscape:cx="50.211556"
40
+ inkscape:cy="55.775164"
41
+ inkscape:window-width="1600"
42
+ inkscape:window-height="842"
43
+ inkscape:window-x="0"
44
+ inkscape:window-y="0"
45
+ inkscape:window-maximized="1"
46
+ inkscape:current-layer="svg1" />
47
+ <path
48
+ d="m 139.33138,100.40986 c 2.07628,9.061 -1.23538,16.92214 -7.48538,16.92214 H 18.203 c -6.25,0 -9.5619324,-7.85964 -7.485227,-16.92214 L 30.125343,15.717557 C 31.084946,11.529963 35.078345,8.235 39.036814,8.235 h 71.976376 c 3.95847,0 7.95183,3.294963 8.9114,7.482557 z M 37.902612,14.17429 c -0.788552,0 -1.627148,0.722405 -1.790034,1.542582 L 19.292985,100.40846 c -0.338692,1.70541 0.375285,3.22606 1.525768,3.22606 H 129.23037 c 1.14762,0 1.86732,-1.52065 1.52863,-3.22606 L 113.93933,15.716872 c -0.16289,-0.820177 -1.00606,-1.542582 -1.792,-1.542582 z m 9.9385,28.041591 c -5.628345,0 -9.313113,-5.26189 -8.279299,-11.413497 0.976861,-5.812713 5.854252,-10.301547 10.933143,-10.301547 5.076868,0 8.852895,4.488834 8.40881,10.301547 -0.469976,6.151607 -5.435804,11.413497 -11.062654,11.413497 z M 120.62564,91.150498 H 29.419956 L 32.243929,74.346727 53.872491,50.541182 62.079678,61.858035 89.407567,29.014214 114.54115,54.940506 Z"
49
+ id="path1"
50
+ inkscape:path-effect="#path-effect1"
51
+ inkscape:original-d="m 143.209,105.968 c 0,6.25 -5.113,11.364 -11.363,11.364 H 18.203 c -6.25,0 -11.363,-5.113 -11.363,-11.364 v -86.37 c 0,-6.25 5.113,-11.363 11.363,-11.363 h 113.643 c 6.25,0 11.363,5.113 11.363,11.363 z M 18.203,17.326 c -1.207,0 -2.271,1.068 -2.271,2.271 v 86.37 c 0,1.207 1.065,2.271 2.271,2.271 h 113.643 c 1.203,0 2.274,-1.064 2.274,-2.271 v -86.37 c 0,-1.203 -1.071,-2.271 -2.274,-2.271 H 18.203 Z m 20.458,36.365 c -7.529,0 -13.641,-6.108 -13.641,-13.635 0,-7.527 6.112,-13.638 13.641,-13.638 7.526,0 13.632,6.111 13.632,13.638 0,7.527 -6.105,13.635 -13.632,13.635 z M 125.025,99.15 H 25.02 V 85.51 l 22.73,-22.724 11.363,11.36 36.365,-36.361 29.547,29.547 z"
52
+ transform="matrix(0.16942427,0,0,0.16942427,-0.71096897,1.3629513)" />
53
+ </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qwc2",
3
- "version": "2025.12.04",
3
+ "version": "2025.12.17",
4
4
  "description": "QGIS Web Client",
5
5
  "author": "Sourcepole AG",
6
6
  "license": "BSD-2-Clause",
@@ -17,7 +17,6 @@
17
17
  ],
18
18
  "dependencies": {
19
19
  "@furkot/webfonts-generator": "^2.0.2",
20
- "@sourcepole/qwc-giro3d": "^0.44.0-dev",
21
20
  "@kayahr/text-encoding": "^2.0.0",
22
21
  "@loaders.gl/core": "^4.3.3",
23
22
  "@loaders.gl/shapefile": "^4.3.3",
@@ -26,7 +25,9 @@
26
25
  "@norbulcz/num-parse": "^0.1.0",
27
26
  "@panoramax/web-viewer": "^4.0.1",
28
27
  "@reduxjs/toolkit": "^2.4.0",
28
+ "@sourcepole/qwc-giro3d": "^0.44.0-dev",
29
29
  "@turf/buffer": "^6.5.0",
30
+ "@turf/clean-coords": "^7.3.1",
30
31
  "@turf/helpers": "^6.5.0",
31
32
  "@vtaits/react-color-picker": "^2.0.0",
32
33
  "any-date-parser": "^1.5.4",
@@ -173,6 +173,7 @@ var Cyclomedia = /*#__PURE__*/function (_React$Component) {
173
173
  }
174
174
  });
175
175
  _defineProperty(_this, "cyclomediaIndexHtml", function () {
176
+ var _window$__CSP_NONCE__, _window$__CSP_NONCE__2, _window$__CSP_NONCE__3, _window$__CSP_NONCE__4;
176
177
  var supportedLang = ["de", "en-GB", "en-US", "fi", "fr", "nl", "tr", "pl"];
177
178
  var lang = LocaleUtils.lang();
178
179
  if (supportedLang.indexOf(lang) < 0) {
@@ -182,7 +183,7 @@ var Cyclomedia = /*#__PURE__*/function (_React$Component) {
182
183
  }
183
184
  }
184
185
  var loginOauth = !!_this.props.clientId && !_this.state.loginFailed;
185
- return "\n <!DOCTYPE html>\n <html>\n <head>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react@18.3.1/umd/react.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js\"></script>\n <script type=\"text/javascript\" src=\"https://streetsmart.cyclomedia.com/api/v".concat(_this.props.cyclomediaVersion, "/StreetSmartApi.js\"></script>\n <script type=\"text/javascript\">\n let apiInitialized = false;\n let initCallback = null;\n let posCallback = null;\n let measureCallback = null;\n\n function initApi() {\n StreetSmartApi.init({\n targetElement: document.getElementById(\"streetsmartApi\"),\n username: \"").concat(_this.state.username || undefined, "\",\n password: \"").concat(_this.state.password || undefined, "\",\n apiKey: \"").concat(_this.props.apikey, "\",\n clientId: \"").concat(_this.props.clientId, "\",\n loginOauth: ").concat(loginOauth, ",\n loginRedirectUri: \"").concat(_this.props.loginRedirectUri, "\",\n logoutRedirectUri: \"").concat(_this.props.logoutRedirectUri, "\",\n srs: \"").concat(_this.props.projection, "\",\n locale: \"").concat(lang, "\",\n configurationUrl: 'https://atlas.cyclomedia.com/configuration',\n addressSettings: {\n locale: \"us\",\n database: \"Nokia\"\n }\n }).then(() => {\n apiInitialized = true;\n if (initCallback) {\n initCallback(true);\n }\n }, (e) => {\n apiInitialized = false;\n if (initCallback) {\n initCallback(false, e.message);\n }\n });\n }\n function openImage(posStr, crs) {\n if (!apiInitialized) {\n return;\n }\n StreetSmartApi.open(posStr, {\n viewerType: StreetSmartApi.ViewerType.PANORAMA,\n srs: crs,\n panoramaViewer: {\n closable: false,\n maximizable: true,\n replace: true,\n recordingsVisible: true,\n navbarVisible: true,\n timeTravelVisible: true,\n measureTypeButtonVisible: true,\n measureTypeButtonStart: true,\n measureTypeButtonToggle: true,\n },\n }).then((result) => {\n if (result && result[0]){\n window.panoramaViewer = result[0];\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.IMAGE_CHANGE, changeView);\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.VIEW_CHANGE, changeView);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_CHANGED, changeMeasurement);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_STOPPED, stopMeasurement);\n }\n }).catch((reason) => {\n console.log('Failed to create component(s) through API: ' + reason);\n });\n }\n function changeView() {\n if (posCallback) {\n const recording = window.panoramaViewer.getRecording();\n const orientation = window.panoramaViewer.getOrientation();\n const pos = recording.xyz;\n const posData = {\n pos: [pos[0], pos[1]],\n crs: recording.srs,\n yaw: orientation.yaw * Math.PI / 180,\n hFov: orientation.hFov * Math.PI / 180.0\n }\n posCallback(posData);\n }\n }\n function changeMeasurement(e) {\n measureCallback(e.detail.activeMeasurement);\n }\n function stopMeasurement() {\n measureCallback(null);\n }\n function registerCallbacks(_initCallback, _posCallback, _measureCallback) {\n initCallback = _initCallback;\n posCallback = _posCallback;\n measureCallback = _measureCallback;\n }\n </script>\n <style>\n html, body, #streetsmartApi {height: 100%;}\n </style>\n </head>\n <body style=\"margin: 0\">\n <div id=\"streetsmartApi\">\n </div>\n </body>\n </html>\n ");
186
+ return "\n <!DOCTYPE html>\n <html>\n <head>\n <script nonce=\"".concat((_window$__CSP_NONCE__ = window.__CSP_NONCE__) !== null && _window$__CSP_NONCE__ !== void 0 ? _window$__CSP_NONCE__ : '', "\" type=\"text/javascript\" src=\"https://unpkg.com/react@18.3.1/umd/react.production.min.js\"></script>\n <script nonce=\"").concat((_window$__CSP_NONCE__2 = window.__CSP_NONCE__) !== null && _window$__CSP_NONCE__2 !== void 0 ? _window$__CSP_NONCE__2 : '', "\" type=\"text/javascript\" src=\"https://unpkg.com/react-dom@18.3.1/umd/react-dom.production.min.js\"></script>\n <script nonce=\"").concat((_window$__CSP_NONCE__3 = window.__CSP_NONCE__) !== null && _window$__CSP_NONCE__3 !== void 0 ? _window$__CSP_NONCE__3 : '', "\" type=\"text/javascript\" src=\"https://streetsmart.cyclomedia.com/api/v").concat(_this.props.cyclomediaVersion, "/StreetSmartApi.js\"></script>\n <script nonce=\"").concat((_window$__CSP_NONCE__4 = window.__CSP_NONCE__) !== null && _window$__CSP_NONCE__4 !== void 0 ? _window$__CSP_NONCE__4 : '', "\" type=\"text/javascript\">\n let apiInitialized = false;\n let initCallback = null;\n let posCallback = null;\n let measureCallback = null;\n\n function initApi() {\n StreetSmartApi.init({\n targetElement: document.getElementById(\"streetsmartApi\"),\n username: \"").concat(_this.state.username || undefined, "\",\n password: \"").concat(_this.state.password || undefined, "\",\n apiKey: \"").concat(_this.props.apikey, "\",\n clientId: \"").concat(_this.props.clientId, "\",\n loginOauth: ").concat(loginOauth, ",\n loginRedirectUri: \"").concat(_this.props.loginRedirectUri, "\",\n logoutRedirectUri: \"").concat(_this.props.logoutRedirectUri, "\",\n srs: \"").concat(_this.props.projection, "\",\n locale: \"").concat(lang, "\",\n configurationUrl: 'https://atlas.cyclomedia.com/configuration',\n addressSettings: {\n locale: \"us\",\n database: \"Nokia\"\n }\n }).then(() => {\n apiInitialized = true;\n if (initCallback) {\n initCallback(true);\n }\n }, (e) => {\n apiInitialized = false;\n if (initCallback) {\n initCallback(false, e.message);\n }\n });\n }\n function openImage(posStr, crs) {\n if (!apiInitialized) {\n return;\n }\n StreetSmartApi.open(posStr, {\n viewerType: StreetSmartApi.ViewerType.PANORAMA,\n srs: crs,\n panoramaViewer: {\n closable: false,\n maximizable: true,\n replace: true,\n recordingsVisible: true,\n navbarVisible: true,\n timeTravelVisible: true,\n measureTypeButtonVisible: true,\n measureTypeButtonStart: true,\n measureTypeButtonToggle: true,\n },\n }).then((result) => {\n if (result && result[0]){\n window.panoramaViewer = result[0];\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.IMAGE_CHANGE, changeView);\n window.panoramaViewer.on(StreetSmartApi.Events.panoramaViewer.VIEW_CHANGE, changeView);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_CHANGED, changeMeasurement);\n StreetSmartApi.on(StreetSmartApi.Events.measurement.MEASUREMENT_STOPPED, stopMeasurement);\n }\n }).catch((reason) => {\n console.log('Failed to create component(s) through API: ' + reason);\n });\n }\n function changeView() {\n if (posCallback) {\n const recording = window.panoramaViewer.getRecording();\n const orientation = window.panoramaViewer.getOrientation();\n const pos = recording.xyz;\n const posData = {\n pos: [pos[0], pos[1]],\n crs: recording.srs,\n yaw: orientation.yaw * Math.PI / 180,\n hFov: orientation.hFov * Math.PI / 180.0\n }\n posCallback(posData);\n }\n }\n function changeMeasurement(e) {\n measureCallback(e.detail.activeMeasurement);\n }\n function stopMeasurement() {\n measureCallback(null);\n }\n function registerCallbacks(_initCallback, _posCallback, _measureCallback) {\n initCallback = _initCallback;\n posCallback = _posCallback;\n measureCallback = _measureCallback;\n }\n </script>\n <style>\n html, body, #streetsmartApi {height: 100%;}\n </style>\n </head>\n <body style=\"margin: 0\">\n <div id=\"streetsmartApi\">\n </div>\n </body>\n </html>\n ");
186
187
  });
187
188
  _defineProperty(_this, "addRecordingsWFS", function () {
188
189
  var layer = {
@@ -124,7 +124,8 @@ var HeightProfilePrintDialog_ = /*#__PURE__*/function (_React$PureComponent) {
124
124
  strokeWidth: 4,
125
125
  strokeDash: [],
126
126
  headmarker: _this.props.measurement.lineHeadMarker,
127
- tailmarker: _this.props.measurement.lineTailMarker
127
+ tailmarker: _this.props.measurement.lineTailMarker,
128
+ markerscale: _this.props.measurement.markerScale
128
129
  },
129
130
  properties: {
130
131
  segment_labels: measurement.segment_lengths.map(function (length) {
@@ -147,6 +148,8 @@ var HeightProfilePrintDialog_ = /*#__PURE__*/function (_React$PureComponent) {
147
148
  REQUEST: 'GetMap',
148
149
  TRANSPARENT: 'true',
149
150
  TILED: 'false',
151
+ filename: 'heightprofile.png',
152
+ // To make the ogc-service treat this as a raster export
150
153
  CRS: _this.props.map.projection,
151
154
  BBOX: bounds,
152
155
  WIDTH: _this.props.map.size.width,
@@ -415,6 +415,7 @@ var Identify = /*#__PURE__*/function (_React$Component) {
415
415
  longAttributesDisplay: this.props.longAttributesDisplay,
416
416
  replaceImageUrls: this.props.replaceImageUrls,
417
417
  resultDisplayMode: this.props.resultDisplayMode,
418
+ resultGridSize: this.props.resultGridSize,
418
419
  showLayerSelector: this.props.showLayerSelector
419
420
  });
420
421
  }
@@ -507,6 +508,8 @@ _defineProperty(Identify, "propTypes", {
507
508
  replaceImageUrls: PropTypes.bool,
508
509
  /** Result display mode, one of `tree`, `flat`, `paginated`. */
509
510
  resultDisplayMode: PropTypes.string,
511
+ /** Target cell size of the result grid in comparison mode. */
512
+ resultGridSize: PropTypes.number,
510
513
  selection: PropTypes.object,
511
514
  setCurrentTask: PropTypes.func,
512
515
  /** Whether to show a layer selector to filter the identify results by layer. */
@@ -522,6 +525,7 @@ _defineProperty(Identify, "defaultProps", {
522
525
  customExporters: [],
523
526
  longAttributesDisplay: 'ellipsis',
524
527
  resultDisplayMode: 'flat',
528
+ resultGridSize: 200,
525
529
  replaceImageUrls: true,
526
530
  featureInfoReturnsLayerName: true,
527
531
  geometry: {
@@ -441,7 +441,8 @@ var LayerTree = /*#__PURE__*/function (_React$Component) {
441
441
  icon: "tree",
442
442
  onClick: function onClick() {
443
443
  return _this.props.changeLayerProperty(layer.id, "visibility", subtreevisibility !== 1, path, "children");
444
- }
444
+ },
445
+ title: LocaleUtils.tr("layertree.togglegroup")
445
446
  });
446
447
  }
447
448
  var infoButton = null;
@@ -689,18 +690,21 @@ var LayerTree = /*#__PURE__*/function (_React$Component) {
689
690
  }), _this.state.visibilityMenu ? /*#__PURE__*/React.createElement(PopupMenu, {
690
691
  anchor: _this.visibilityButton,
691
692
  className: "layertree-visibility-menu",
693
+ keepMenuOpen: true,
692
694
  onClose: function onClose() {
693
695
  return _this.setState({
694
696
  visibilityMenu: false
695
697
  });
696
698
  }
697
699
  }, _this.props.showToggleAllLayersCheckbox ? /*#__PURE__*/React.createElement("div", {
700
+ key: "hidealllayers",
698
701
  onClick: function onClick() {
699
702
  return _this.toggleLayerTreeVisibility(vis === 0);
700
703
  }
701
704
  }, /*#__PURE__*/React.createElement(Icon, {
702
705
  icon: vis === 0 ? "checked" : "unchecked"
703
706
  }), " ", LocaleUtils.tr("layertree.hidealllayers")) : null, _this.props.enableVisibleFilter ? /*#__PURE__*/React.createElement("div", {
707
+ key: "filtervisible",
704
708
  onClick: function onClick() {
705
709
  return _this.setState(function (state) {
706
710
  return {
@@ -52,7 +52,8 @@ var Measure = /*#__PURE__*/function (_React$Component) {
52
52
  bearingHeadMarker: _this.props.bearingHeadMarker,
53
53
  bearingTailMarker: _this.props.bearingTailMarker,
54
54
  lineHeadMarker: _this.props.lineHeadMarker,
55
- lineTailMarker: _this.props.lineTailMarker
55
+ lineTailMarker: _this.props.lineTailMarker,
56
+ markerScale: _this.props.markerScale
56
57
  });
57
58
  _this.props.setSnappingConfig(_this.props.snapping, _this.props.snappingActive);
58
59
  });
@@ -199,6 +200,8 @@ _defineProperty(Measure, "propTypes", {
199
200
  /** Tail marker of distance line measurement geometry. Can be one of `OUTARROW`, `INARROW`, `LINE`. */
200
201
  lineTailMarker: PropTypes.string,
201
202
  mapCrs: PropTypes.string,
203
+ /** Scale factor for all heade/tail markers. */
204
+ markerScale: PropTypes.number,
202
205
  measureState: PropTypes.object,
203
206
  setSnappingConfig: PropTypes.func,
204
207
  /** Whether to show the widget to switch between measure modes. */
@@ -210,6 +213,7 @@ _defineProperty(Measure, "propTypes", {
210
213
  snappingActive: PropTypes.oneOfType([PropTypes.bool, PropTypes.string])
211
214
  });
212
215
  _defineProperty(Measure, "defaultProps", {
216
+ markerScale: 1,
213
217
  showMeasureModeSwitcher: true,
214
218
  snapping: true,
215
219
  snappingActive: true