qwc2 2026.2.27 → 2026.3.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 (99) hide show
  1. package/components/AppMenu.js +13 -8
  2. package/components/AttributeTableWidget.js +16 -3
  3. package/components/CoordinateDisplayer.js +5 -3
  4. package/components/IdentifyViewer.js +11 -5
  5. package/components/QtDesignerForm.js +1 -1
  6. package/components/SideBar.js +2 -2
  7. package/components/map/OlMap.js +7 -1
  8. package/components/map3d/Map3D.js +113 -39
  9. package/components/map3d/MapControls3D.js +2 -3
  10. package/components/map3d/drawtool/CreateTool3D.js +2 -1
  11. package/components/map3d/drawtool/EditTool3D.js +224 -13
  12. package/components/map3d/utils/FirstPersonControls3D.js +1 -0
  13. package/components/map3d/utils/MiscUtils3D.js +187 -24
  14. package/components/style/App.css +1 -0
  15. package/components/style/AppMenu.css +6 -0
  16. package/components/style/DefaultStyleScheme.css +4 -0
  17. package/components/style/MessageBar.css +1 -0
  18. package/components/style/PluginsContainer.css +3 -0
  19. package/components/style/QtDesignerForm.css +5 -0
  20. package/components/style/ResizeableWindow.css +4 -1
  21. package/components/style/SearchBox.css +2 -0
  22. package/components/style/SideBar.css +1 -0
  23. package/components/style/ThemeList.css +3 -1
  24. package/components/widgets/ColorButton.js +6 -1
  25. package/components/widgets/Input.js +2 -1
  26. package/components/widgets/style/InputContainer.css +2 -0
  27. package/components/widgets/style/ListInput.css +5 -1
  28. package/icons/measureobj.svg +58 -0
  29. package/icons/snap_3d.svg +105 -0
  30. package/package.json +7 -1
  31. package/plugins/API.js +9 -1
  32. package/plugins/Editing.js +29 -2
  33. package/plugins/FeatureSearch.js +42 -41
  34. package/plugins/LayerTree.js +113 -59
  35. package/plugins/LocateButton.js +1 -1
  36. package/plugins/Map.js +1 -0
  37. package/plugins/MapExport.js +4 -6
  38. package/plugins/MapFilter.js +10 -45
  39. package/plugins/MapLegend.js +10 -2
  40. package/plugins/Measure.js +4 -2
  41. package/plugins/ObliqueView.js +4 -4
  42. package/plugins/Panoramax.js +5 -5
  43. package/plugins/Redlining.js +1 -1
  44. package/plugins/SensorThingsTool.js +3022 -0
  45. package/plugins/TopBar.js +3 -0
  46. package/plugins/View3D.js +8 -0
  47. package/plugins/map/MeasurementSupport.js +26 -4
  48. package/plugins/map3d/Draw3D.js +32 -4
  49. package/plugins/map3d/ExportObjects3D.js +24 -21
  50. package/plugins/map3d/HideObjects3D.js +45 -35
  51. package/plugins/map3d/Identify3D.js +9 -14
  52. package/plugins/map3d/MeasureObjects3D.js +362 -0
  53. package/plugins/map3d/Settings3D.js +20 -2
  54. package/plugins/map3d/TopBar3D.js +1 -0
  55. package/plugins/map3d/style/MeasureObjects3D.css +34 -0
  56. package/plugins/style/BottomBar.css +5 -0
  57. package/plugins/style/FeatureSearch.css +0 -5
  58. package/plugins/style/LayerTree.css +2 -1
  59. package/plugins/style/ObliqueView.css +2 -2
  60. package/plugins/style/SensorThingsTool.css +193 -0
  61. package/plugins/style/TopBar.css +1 -0
  62. package/resources/panoramax-cursor.svg +20 -0
  63. package/resources/target.svg +6 -0
  64. package/scripts/themesConfig.js +1 -0
  65. package/scripts/themesConfig.py +2 -0
  66. package/static/translations/bg-BG.json +118 -2
  67. package/static/translations/ca-ES.json +174 -58
  68. package/static/translations/cs-CZ.json +118 -2
  69. package/static/translations/de-CH.json +119 -3
  70. package/static/translations/de-DE.json +119 -3
  71. package/static/translations/en-US.json +120 -4
  72. package/static/translations/es-ES.json +158 -42
  73. package/static/translations/fi-FI.json +118 -2
  74. package/static/translations/fr-FR.json +143 -27
  75. package/static/translations/hu-HU.json +118 -2
  76. package/static/translations/it-IT.json +142 -26
  77. package/static/translations/ja-JP.json +118 -2
  78. package/static/translations/nl-NL.json +118 -2
  79. package/static/translations/no-NO.json +118 -2
  80. package/static/translations/pl-PL.json +118 -2
  81. package/static/translations/pt-BR.json +118 -2
  82. package/static/translations/pt-PT.json +118 -2
  83. package/static/translations/ro-RO.json +118 -2
  84. package/static/translations/ru-RU.json +118 -2
  85. package/static/translations/sv-SE.json +118 -2
  86. package/static/translations/tr-TR.json +118 -2
  87. package/static/translations/tsconfig.json +97 -1
  88. package/static/translations/uk-UA.json +118 -2
  89. package/utils/DataServiceExprUtils.js +167 -0
  90. package/utils/FeatureSnapIndex.js +293 -0
  91. package/utils/FeatureStyles.js +4 -0
  92. package/utils/IdentifyUtils.js +2 -0
  93. package/utils/LayerUtils.js +36 -37
  94. package/utils/QuickHull2D.js +135 -0
  95. package/utils/SearchProviders.js +55 -21
  96. package/utils/ServiceLayerUtils.js +4 -1
  97. package/utils/ThemeUtils.js +2 -0
  98. package/utils/expr_grammar/grammar.js +15 -15
  99. package/utils/expr_grammar/grammar.ne +1 -1
@@ -123,6 +123,7 @@ var AppMenu = /*#__PURE__*/function (_React$Component) {
123
123
  "appmenu-submenu": true,
124
124
  "appmenu-submenu-expanded": expanded
125
125
  });
126
+ var label = item.title ? LocaleUtils.tr(item.title) : LocaleUtils.tr("appmenu.items." + item.key);
126
127
  return [/*#__PURE__*/React.createElement("div", {
127
128
  className: className,
128
129
  key: (_item$key = item.key) !== null && _item$key !== void 0 ? _item$key : item.title,
@@ -136,13 +137,14 @@ var AppMenu = /*#__PURE__*/function (_React$Component) {
136
137
  tabIndex: 0
137
138
  }, /*#__PURE__*/React.createElement(Icon, {
138
139
  icon: item.icon,
139
- size: "xlarge"
140
- }), item.title ? LocaleUtils.tr(item.title) : LocaleUtils.tr("appmenu.items." + item.key)), subitems];
140
+ size: "xlarge",
141
+ title: _this.props.menuIconOnly ? label : null
142
+ }), !_this.props.menuIconOnly ? label : null), subitems];
141
143
  } else {
142
144
  var trargs = item.trargs || [];
143
- var label = item.title ? LocaleUtils.tr.apply(LocaleUtils, [item.title].concat(_toConsumableArray(trargs))) : LocaleUtils.tr.apply(LocaleUtils, ["appmenu.items." + item.key + (item.mode || "")].concat(_toConsumableArray(trargs)));
145
+ var _label = item.title ? LocaleUtils.tr.apply(LocaleUtils, [item.title].concat(_toConsumableArray(trargs))) : LocaleUtils.tr.apply(LocaleUtils, ["appmenu.items." + item.key + (item.mode || "")].concat(_toConsumableArray(trargs)));
144
146
  var comment = item.comment ? LocaleUtils.tr.apply(LocaleUtils, ["appmenu.items." + item.key + (item.mode || "") + "_comment"].concat(_toConsumableArray(trargs))) : "";
145
- if (!filter || removeDiacritics(label.toLowerCase()).match(filter) || comment && removeDiacritics(comment.toLowerCase()).match(filter)) {
147
+ if (!filter || removeDiacritics(_label.toLowerCase()).match(filter) || comment && removeDiacritics(comment.toLowerCase()).match(filter)) {
146
148
  var _className = classnames({
147
149
  "appmenu-menu-item": true,
148
150
  "appmenu-menu-item-nested": submenu
@@ -160,12 +162,13 @@ var AppMenu = /*#__PURE__*/function (_React$Component) {
160
162
  tabIndex: 0
161
163
  }, /*#__PURE__*/React.createElement(Icon, {
162
164
  icon: item.icon,
163
- size: "xlarge"
164
- }), /*#__PURE__*/React.createElement("span", {
165
+ size: "xlarge",
166
+ title: _this.props.menuIconOnly ? _label : null
167
+ }), !_this.props.menuIconOnly ? /*#__PURE__*/React.createElement("span", {
165
168
  className: "appmenu-menu-item-label"
166
- }, label, comment ? /*#__PURE__*/React.createElement("div", {
169
+ }, _label, comment ? /*#__PURE__*/React.createElement("div", {
167
170
  className: "appmenu-menu-item-comment"
168
- }, comment) : null));
171
+ }, comment) : null) : null);
169
172
  }
170
173
  return null;
171
174
  }
@@ -284,6 +287,7 @@ var AppMenu = /*#__PURE__*/function (_React$Component) {
284
287
  "appmenu-blocked": this.props.currentTaskBlocked,
285
288
  "appmenu-visible": visible,
286
289
  "appmenu-compact": this.props.menuCompact,
290
+ "appmenu-icononly": this.props.menuIconOnly,
287
291
  "appmenu-nolabel": !showLabel
288
292
  });
289
293
  var filter = this.state.filter ? new RegExp(removeDiacritics(this.state.filter).replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&"), "i") : null;
@@ -360,6 +364,7 @@ _defineProperty(AppMenu, "propTypes", {
360
364
  currentTaskBlocked: PropTypes.bool,
361
365
  keepMenuOpen: PropTypes.bool,
362
366
  menuCompact: PropTypes.bool,
367
+ menuIconOnly: PropTypes.bool,
363
368
  menuItems: PropTypes.array,
364
369
  onMenuToggled: PropTypes.func,
365
370
  openExternalUrl: PropTypes.func,
@@ -38,7 +38,7 @@ import { connect } from 'react-redux';
38
38
  import FileSaver from 'file-saver';
39
39
  import isEmpty from 'lodash.isempty';
40
40
  import PropTypes from 'prop-types';
41
- import { LayerRole, addLayerFeatures, removeLayer } from '../actions/layers';
41
+ import { LayerRole, addLayerFeatures, removeLayer, refreshLayer } from '../actions/layers';
42
42
  import { zoomToExtent, zoomToPoint } from '../actions/map';
43
43
  import { setCurrentTask, setCurrentTaskBlocked } from '../actions/task';
44
44
  import EditComboField from '../components/EditComboField';
@@ -485,6 +485,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
485
485
  return newState;
486
486
  }, function () {
487
487
  if (reload) {
488
+ var mapPrefix = _this.state.curEditConfig.editDataset.split(".")[0];
489
+ _this.props.refreshLayer(function (layer) {
490
+ return layer.wms_name === mapPrefix;
491
+ });
488
492
  _this.reload(_this.state.loadedLayer, true);
489
493
  }
490
494
  });
@@ -566,6 +570,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
566
570
  alert(result);
567
571
  } else {
568
572
  _this.changedFiles = {};
573
+ var mapPrefix = _this.state.curEditConfig.editDataset.split(".")[0];
574
+ _this.props.refreshLayer(function (layer) {
575
+ return layer.wms_name === mapPrefix;
576
+ });
569
577
  _this.reload(_this.state.loadedLayer, true, {
570
578
  changedFeatureIdx: null,
571
579
  originalFeatureProps: null,
@@ -1029,7 +1037,10 @@ var AttributeTableWidget = /*#__PURE__*/function (_React$Component) {
1029
1037
  return _this2.updateFilter("filterField", ev.target.value);
1030
1038
  },
1031
1039
  value: this.state.filterField
1032
- }, showIdColumn ? /*#__PURE__*/React.createElement("option", {
1040
+ }, /*#__PURE__*/React.createElement("option", {
1041
+ disabled: true,
1042
+ value: ""
1043
+ }, LocaleUtils.tr("common.select")), showIdColumn ? /*#__PURE__*/React.createElement("option", {
1033
1044
  value: "<id>"
1034
1045
  }, this.translateFieldName(primaryKey)) : null, fields.map(function (field) {
1035
1046
  return /*#__PURE__*/React.createElement("option", {
@@ -1239,6 +1250,7 @@ _defineProperty(AttributeTableWidget, "propTypes", {
1239
1250
  mapCrs: PropTypes.string,
1240
1251
  mapScales: PropTypes.array,
1241
1252
  readOnly: PropTypes.bool,
1253
+ refreshLayer: PropTypes.func,
1242
1254
  removeLayer: PropTypes.func,
1243
1255
  setCurrentTask: PropTypes.func,
1244
1256
  setCurrentTaskBlocked: PropTypes.func,
@@ -1279,7 +1291,7 @@ _defineProperty(AttributeTableWidget, "defaultState", {
1279
1291
  originalFeatureProps: null,
1280
1292
  pageSize: 50,
1281
1293
  currentPage: 0,
1282
- filterField: "id",
1294
+ filterField: "",
1283
1295
  filterOp: "~",
1284
1296
  filterVal: "",
1285
1297
  sortField: null,
@@ -1302,6 +1314,7 @@ export default connect(function (state) {
1302
1314
  };
1303
1315
  }, {
1304
1316
  addLayerFeatures: addLayerFeatures,
1317
+ refreshLayer: refreshLayer,
1305
1318
  removeLayer: removeLayer,
1306
1319
  setCurrentTask: setCurrentTask,
1307
1320
  setCurrentTaskBlocked: setCurrentTaskBlocked,
@@ -37,9 +37,11 @@ var CoordinateDisplayer = /*#__PURE__*/function (_React$Component) {
37
37
  mousePos: null
38
38
  });
39
39
  _defineProperty(_this, "getMapMousePos", function (ev) {
40
- _this.setState({
41
- mousePos: ev.coordinate
42
- });
40
+ if (ev.coordinate) {
41
+ _this.setState({
42
+ mousePos: ev.coordinate
43
+ });
44
+ }
43
45
  });
44
46
  return _this;
45
47
  }
@@ -620,9 +620,11 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
620
620
  var inlineExtaAttribs = false;
621
621
  var featureReports = _this.state.reports[layerid] || [];
622
622
  if (feature.featurereport) {
623
+ var parts = feature.featurereport.split(".");
623
624
  featureReports.push({
624
625
  title: null,
625
- template: feature.featurereport
626
+ template: parts.shift(),
627
+ format: parts.join(".")
626
628
  });
627
629
  }
628
630
  if (feature.type === "text") {
@@ -768,7 +770,7 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
768
770
  }) : null, /*#__PURE__*/React.createElement("span", null, [_this.props.showLayerTitles ? feature.layertitle : "", feature.displayname].filter(Boolean).join(": ")), zoomToFeatureButton, /*#__PURE__*/React.createElement(Icon, {
769
771
  icon: "info-sign",
770
772
  onClick: function onClick() {
771
- return _this.showLayerInfo(layerid);
773
+ return _this.showLayerInfo(layerid, feature.layerinfo);
772
774
  }
773
775
  }), /*#__PURE__*/React.createElement(Icon, {
774
776
  icon: "trash",
@@ -1122,12 +1124,15 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
1122
1124
  });
1123
1125
  });
1124
1126
  });
1125
- _defineProperty(_this, "showLayerInfo", function (layer) {
1127
+ _defineProperty(_this, "showLayerInfo", function (layer, infolayer) {
1126
1128
  var _layer$split = layer.split('#'),
1127
1129
  _layer$split2 = _slicedToArray(_layer$split, 2),
1128
1130
  layerUrl = _layer$split2[0],
1129
1131
  layerName = _layer$split2[1];
1130
- var match = LayerUtils.searchLayer(_this.props.layers, 'url', layerUrl, 'name', layerName);
1132
+ if (!infolayer) {
1133
+ infolayer = layerName;
1134
+ }
1135
+ var match = LayerUtils.searchLayer(_this.props.layers, 'url', layerUrl, 'name', infolayer);
1131
1136
  if (match) {
1132
1137
  _this.props.setActiveLayerInfo(match.layer, match.sublayer);
1133
1138
  }
@@ -1154,7 +1159,8 @@ var IdentifyViewer = /*#__PURE__*/function (_React$Component) {
1154
1159
  text = DOMPurify.sanitize(text, {
1155
1160
  ADD_ATTR: ['target'],
1156
1161
  ADD_TAGS: ["iframe"],
1157
- FORCE_BODY: true
1162
+ FORCE_BODY: true,
1163
+ ALLOW_UNKNOWN_PROTOCOLS: true
1158
1164
  }).replace('&#10;', '<br />');
1159
1165
  var options = {
1160
1166
  replace: function replace(node) {
@@ -572,7 +572,7 @@ var QtDesignerForm = /*#__PURE__*/function (_React$Component) {
572
572
  }, inputConstraints, {
573
573
  style: fontStyle,
574
574
  type: "date",
575
- value: value
575
+ value: value.split("T")[0]
576
576
  }));
577
577
  } else if (widget["class"] === "QTimeEdit") {
578
578
  return /*#__PURE__*/React.createElement("input", _extends({
@@ -157,11 +157,11 @@ var SideBar = /*#__PURE__*/function (_React$Component) {
157
157
  onPointerDown: this.startSidebarResize
158
158
  }), /*#__PURE__*/React.createElement("div", {
159
159
  className: "sidebar-titlebar"
160
- }, this.state.render ? this.props.extraBeforeContent : null, /*#__PURE__*/React.createElement(Icon, {
160
+ }, this.state.render ? this.props.extraBeforeContent : null, this.props.icon ? /*#__PURE__*/React.createElement(Icon, {
161
161
  className: "sidebar-titlebar-icon",
162
162
  icon: this.props.icon,
163
163
  size: "large"
164
- }), /*#__PURE__*/React.createElement("span", {
164
+ }) : null, /*#__PURE__*/React.createElement("span", {
165
165
  className: "sidebar-titlebar-title"
166
166
  }, this.props.title), this.state.render ? this.props.extraTitlebarContent : null, /*#__PURE__*/React.createElement("span", {
167
167
  className: "sidebar-titlebar-spacer"
@@ -185,7 +185,8 @@ var OlMap = /*#__PURE__*/function (_React$Component) {
185
185
  constrainRotation: false,
186
186
  enableRotation: enableRotation !== false,
187
187
  rotation: MapUtils.degreesToRadians(rotation) || 0,
188
- extent: extent
188
+ extent: extent,
189
+ constrainOnlyCenter: _this.props.mapOptions.constrainOnlyCenter || false
189
190
  };
190
191
  return new ol.View(viewOptions);
191
192
  });
@@ -320,6 +321,11 @@ var OlMap = /*#__PURE__*/function (_React$Component) {
320
321
  if (prevProps.bbox.rotation !== this.props.bbox.rotation) {
321
322
  view.setRotation(this.props.bbox.rotation);
322
323
  }
324
+ if (prevProps.fullExtent !== this.props.fullExtent) {
325
+ this.setState({
326
+ rebuildView: true
327
+ });
328
+ }
323
329
  }
324
330
  if (this.state.rebuildView) {
325
331
  this.setState({
@@ -51,7 +51,9 @@ import axios from 'axios';
51
51
  import { fromUrl } from "geotiff";
52
52
  import isEmpty from 'lodash.isempty';
53
53
  import PropTypes from 'prop-types';
54
- import { Vector2, CubeTextureLoader, Group, Raycaster, Mesh, Box3, Vector3, Matrix4 } from 'three';
54
+ import * as THREE from 'three';
55
+ import { Vector2, CubeTextureLoader, Group, Raycaster, Mesh, Box3, Vector3, Matrix4, EventDispatcher, Points } from 'three';
56
+ import { computeBoundsTree, disposeBoundsTree, computeBatchedBoundsTree, disposeBatchedBoundsTree, acceleratedRaycast } from 'three-mesh-bvh';
55
57
  import { GLTFExporter } from 'three/addons/exporters/GLTFExporter.js';
56
58
  import { GLTFLoader } from 'three/addons/loaders/GLTFLoader';
57
59
  import { v4 as uuidv4 } from 'uuid';
@@ -73,6 +75,12 @@ import MapControls3D from './MapControls3D';
73
75
  import { updateObjectLabel } from './utils/MiscUtils3D';
74
76
  import Tiles3DStyle from './utils/Tiles3DStyle';
75
77
  import './style/Map3D.css';
78
+ THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
79
+ THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;
80
+ THREE.Mesh.prototype.raycast = acceleratedRaycast;
81
+ THREE.BatchedMesh.prototype.computeBoundsTree = computeBatchedBoundsTree;
82
+ THREE.BatchedMesh.prototype.disposeBoundsTree = disposeBatchedBoundsTree;
83
+ THREE.BatchedMesh.prototype.raycast = acceleratedRaycast;
76
84
 
77
85
  // Ensures onUnload is called *after* all other children have unmounted
78
86
  var UnloadWrapper = /*#__PURE__*/function (_React$Component) {
@@ -138,7 +146,10 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
138
146
  removeSceneObject: function removeSceneObject(objectId) {},
139
147
  updateSceneObject: function updateSceneObject(objectId, options, flags) {},
140
148
  zoomToObject: function zoomToObject(objectId) {},
149
+ objectIsVisible: function objectIsVisible(objectId) {},
150
+ objectContainsPoints: function objectContainsPoints(object) {},
141
151
  getMap: function getMap() {},
152
+ computeBoundsTree: function computeBoundsTree(object) {},
142
153
  setViewToExtent: function setViewToExtent(bounds, angle) {},
143
154
  getTerrainHeightFromDTM: function getTerrainHeightFromDTM(scenePos) {},
144
155
  getTerrainHeightFromMap: function getTerrainHeightFromMap(scenePos) {},
@@ -175,9 +186,11 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
175
186
  // Nothing changed
176
187
  return;
177
188
  }
178
- var dtm = _this2.getLayer("__dtm");
179
- if (visibility !== dtm.visible) {
180
- dtm.visible = visibility;
189
+ if (visibility !== !!_this2.map.backgroundOpacity) {
190
+ var dtm = _this2.getLayer("__dtm");
191
+ if (dtm) {
192
+ dtm.visible = visibility;
193
+ }
181
194
  _this2.map.receiveShadow = visibility;
182
195
  _this2.map.backgroundOpacity = visibility ? 1 : 0;
183
196
  _this2.instance.notifyChange(_this2.map);
@@ -477,6 +490,10 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
477
490
  scene.userData.tilesetName = objectId;
478
491
  scene.userData.featureIdAttr = "id";
479
492
  Tiles3DStyle.applyTileStyle(scene, tiles.userData, _this2.state.sceneContext);
493
+ if (_this2.objectContainsPoints(tiles)) {
494
+ tiles.pointSize = _this2.state.sceneContext.settings.pointSize;
495
+ }
496
+ _this2.computeBoundsTree(scene);
480
497
  _this2.instance.notifyChange(tiles);
481
498
  });
482
499
  // Show/hide labels when tile visibility changes
@@ -500,12 +517,16 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
500
517
  var callback = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : null;
501
518
  var loader = new GLTFLoader();
502
519
  var processor = function processor(gltf) {
520
+ var root = gltf.scene;
521
+ if (root.children.length === 1 && root.children[0].isGroup) {
522
+ root = root.children[0];
523
+ }
503
524
  // GLTF is Y-UP, we need Z-UP
504
- gltf.scene.rotation.x = Math.PI / 2;
505
- gltf.scene.updateMatrixWorld(true);
506
- gltf.scene.castShadow = true;
507
- gltf.scene.receiveShadow = true;
508
- gltf.scene.traverse(function (c) {
525
+ root.rotation.x += Math.PI / 2;
526
+ root.updateMatrixWorld(true);
527
+ root.castShadow = true;
528
+ root.receiveShadow = true;
529
+ root.traverse(function (c) {
509
530
  if (c.geometry) {
510
531
  c.castShadow = true;
511
532
  c.receiveShadow = true;
@@ -514,19 +535,18 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
514
535
  });
515
536
 
516
537
  // Shift root position to center of object
517
- gltf.scene.updateMatrixWorld(true);
518
- var box = new Box3().setFromObject(gltf.scene);
538
+ var box = new Box3().setFromObject(root);
519
539
  var centerWorld = box.getCenter(new Vector3());
520
540
  centerWorld.z = box.min.z;
521
- var centerLocal = gltf.scene.worldToLocal(centerWorld.clone());
522
- gltf.scene.position.add(centerWorld);
541
+ var centerLocal = root.worldToLocal(centerWorld.clone());
542
+ root.position.add(centerWorld);
523
543
 
524
544
  // Offset children back so the world positions remain unchanged
525
- gltf.scene.children.forEach(function (child) {
545
+ root.children.forEach(function (child) {
526
546
  child.position.sub(centerLocal);
527
547
  });
528
- gltf.scene.updateMatrixWorld(true);
529
- _this2.addSceneObject(objectId, gltf.scene, addToLayerTree, treeOptions, showEditTool);
548
+ root.updateMatrixWorld(true);
549
+ _this2.addSceneObject(objectId, root, addToLayerTree, treeOptions, showEditTool);
530
550
  callback === null || callback === void 0 || callback();
531
551
  };
532
552
  if (typeof dataOrUrl === 'string') {
@@ -541,6 +561,12 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
541
561
  });
542
562
  }
543
563
  });
564
+ _defineProperty(_this2, "computeBoundsTree", function (group) {
565
+ group.traverse(function (c) {
566
+ var _c$geometry, _c$geometry$computeBo;
567
+ (_c$geometry = c.geometry) === null || _c$geometry === void 0 || (_c$geometry$computeBo = _c$geometry.computeBoundsTree) === null || _c$geometry$computeBo === void 0 || _c$geometry$computeBo.call(_c$geometry);
568
+ });
569
+ });
544
570
  _defineProperty(_this2, "addSceneObject", function (objectId, object) {
545
571
  var addToLayerTree = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
546
572
  var treeOptions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
@@ -553,6 +579,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
553
579
  _this2.objectMap[objectId] = object;
554
580
  _this2.instance.notifyChange(object);
555
581
  if (addToLayerTree) {
582
+ _this2.computeBoundsTree(object);
556
583
  _this2.setState(function (state) {
557
584
  var newObjectTree = _objectSpread({}, state.sceneContext.objectTree);
558
585
  newObjectTree["null"] = _objectSpread(_objectSpread({}, newObjectTree["null"]), {}, {
@@ -562,7 +589,8 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
562
589
  objectId: objectId,
563
590
  parent: null,
564
591
  visibility: true,
565
- opacity: 255
592
+ opacity: 255,
593
+ snap: true
566
594
  }, treeOptions);
567
595
  return {
568
596
  sceneContext: _objectSpread(_objectSpread({}, state.sceneContext), {}, {
@@ -749,6 +777,26 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
749
777
  _this2.state.sceneContext.setViewToExtent(bounds, 0);
750
778
  }
751
779
  });
780
+ _defineProperty(_this2, "objectIsVisible", function (objectId) {
781
+ var objectTree = _this2.state.sceneContext.objectTree;
782
+ var options = objectTree[objectId];
783
+ var isVisible = options.opacity > 0 && options.visibility;
784
+ for (var curId = options.parent; isVisible && curId !== undefined; curId = objectTree[curId].parent) {
785
+ isVisible && (isVisible = objectTree[curId].visibility);
786
+ }
787
+ return isVisible;
788
+ });
789
+ _defineProperty(_this2, "objectContainsPoints", function (object) {
790
+ var containsPoints = false;
791
+ if (object !== null && object !== void 0 && object.tiles) {
792
+ object.traverse(function (child) {
793
+ if (child instanceof Points) {
794
+ containsPoints = true;
795
+ }
796
+ });
797
+ }
798
+ return containsPoints;
799
+ });
752
800
  _defineProperty(_this2, "getMap", function () {
753
801
  return _this2.map;
754
802
  });
@@ -770,7 +818,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
770
818
  }
771
819
  });
772
820
  _defineProperty(_this2, "setupInstance", function () {
773
- var _this2$props$theme$ma, _this2$props$theme$ma2, _this2$props$theme$ma3, _this2$props$theme$ma4, _this2$props$theme$ma5, _this2$props$theme$ma8, _this2$props$theme$ma9, _this2$props$theme$ma0, _this2$props$theme$ma1;
821
+ var _this2$props$theme$ma, _this2$props$theme$ma2, _this2$props$theme$ma3, _this2$props$theme$ma4, _this2$props$theme$ma7;
774
822
  if (_this2.instance) {
775
823
  _this2.disposeInstance();
776
824
  }
@@ -794,7 +842,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
794
842
  _this2.instance.add(_this2.sceneObjectGroup);
795
843
 
796
844
  // Setup map
797
- var initialBbox = (_this2$props$theme$ma = (_this2$props$theme$ma2 = _this2.props.theme.map3d) === null || _this2$props$theme$ma2 === void 0 ? void 0 : _this2$props$theme$ma2.extent) !== null && _this2$props$theme$ma !== void 0 ? _this2$props$theme$ma : _this2.props.theme.initialBbox;
845
+ var initialBbox = (_this2$props$theme$ma = _this2.props.theme.map3d.extent) !== null && _this2$props$theme$ma !== void 0 ? _this2$props$theme$ma : _this2.props.theme.initialBbox;
798
846
  var bounds = CoordinatesUtils.reprojectBbox(initialBbox.bounds, initialBbox.crs, projection);
799
847
  var extent = new Extent(crs, bounds[0], bounds[2], bounds[1], bounds[3]);
800
848
  _this2.map = new Map({
@@ -819,16 +867,16 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
819
867
  _this2.instance.scene.background = cubeTexture;
820
868
 
821
869
  // Setup elevation
822
- var demUrl = MiscUtils.resolveAssetsPath((_this2$props$theme$ma3 = (_this2$props$theme$ma4 = _this2.props.theme.map3d) === null || _this2$props$theme$ma4 === void 0 || (_this2$props$theme$ma4 = _this2$props$theme$ma4.dtm) === null || _this2$props$theme$ma4 === void 0 ? void 0 : _this2$props$theme$ma4.url) !== null && _this2$props$theme$ma3 !== void 0 ? _this2$props$theme$ma3 : "");
823
- var demCrs = CoordinateSystem.fromSrid(((_this2$props$theme$ma5 = _this2.props.theme.map3d) === null || _this2$props$theme$ma5 === void 0 || (_this2$props$theme$ma5 = _this2$props$theme$ma5.dtm) === null || _this2$props$theme$ma5 === void 0 ? void 0 : _this2$props$theme$ma5.crs) || "EPSG:3857");
870
+ var demUrl = MiscUtils.resolveAssetsPath((_this2$props$theme$ma2 = (_this2$props$theme$ma3 = _this2.props.theme.map3d.dtm) === null || _this2$props$theme$ma3 === void 0 ? void 0 : _this2$props$theme$ma3.url) !== null && _this2$props$theme$ma2 !== void 0 ? _this2$props$theme$ma2 : "");
871
+ var demCrs = CoordinateSystem.fromSrid(((_this2$props$theme$ma4 = _this2.props.theme.map3d.dtm) === null || _this2$props$theme$ma4 === void 0 ? void 0 : _this2$props$theme$ma4.crs) || "EPSG:3857");
824
872
  if (demUrl) {
825
- var _this2$props$theme$ma6, _this2$props$theme$ma7;
873
+ var _this2$props$theme$ma5, _this2$props$theme$ma6;
826
874
  var demSource = new GeoTIFFSource({
827
875
  url: demUrl,
828
876
  crs: demCrs
829
877
  });
830
- var demMin = (_this2$props$theme$ma6 = _this2.props.theme.map3d.dtm.min) !== null && _this2$props$theme$ma6 !== void 0 ? _this2$props$theme$ma6 : undefined;
831
- var demMax = (_this2$props$theme$ma7 = _this2.props.theme.map3d.dtm.max) !== null && _this2$props$theme$ma7 !== void 0 ? _this2$props$theme$ma7 : undefined;
878
+ var demMin = (_this2$props$theme$ma5 = _this2.props.theme.map3d.dtm.min) !== null && _this2$props$theme$ma5 !== void 0 ? _this2$props$theme$ma5 : undefined;
879
+ var demMax = (_this2$props$theme$ma6 = _this2.props.theme.map3d.dtm.max) !== null && _this2$props$theme$ma6 !== void 0 ? _this2$props$theme$ma6 : undefined;
832
880
  var elevationLayer = new ElevationLayer({
833
881
  name: 'dem',
834
882
  extent: extent,
@@ -843,7 +891,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
843
891
 
844
892
  // Collect baselayers
845
893
  var externalLayers = {};
846
- var baseLayers = ThemeUtils.createThemeBackgroundLayers(((_this2$props$theme$ma8 = _this2.props.theme.map3d) === null || _this2$props$theme$ma8 === void 0 ? void 0 : _this2$props$theme$ma8.basemaps) || [], _this2.props.themes, null, externalLayers);
894
+ var baseLayers = ThemeUtils.createThemeBackgroundLayers(_this2.props.theme.map3d.basemaps || [], _this2.props.themes, null, externalLayers);
847
895
  baseLayers.push({
848
896
  type: "blank",
849
897
  name: "",
@@ -872,14 +920,14 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
872
920
  // Collect color layers
873
921
  var colorLayers = _this2.collectColorLayers([], []);
874
922
  _this2.objectMap = {};
875
- var objects = ((_this2$props$theme$ma9 = _this2.props.theme.map3d) === null || _this2$props$theme$ma9 === void 0 ? void 0 : _this2$props$theme$ma9.objects) || [];
923
+ var objects = _toConsumableArray((_this2$props$theme$ma7 = _this2.props.theme.map3d.objects) !== null && _this2$props$theme$ma7 !== void 0 ? _this2$props$theme$ma7 : []);
876
924
  // Convert legacy flat lists to tree
877
- (((_this2$props$theme$ma0 = _this2.props.theme.map3d) === null || _this2$props$theme$ma0 === void 0 ? void 0 : _this2$props$theme$ma0.tiles3d) || []).forEach(function (entry) {
925
+ (_this2.props.theme.map3d.tiles3d || []).forEach(function (entry) {
878
926
  objects.push(_objectSpread(_objectSpread({}, entry), {}, {
879
927
  type: "tiles3d"
880
928
  }));
881
929
  });
882
- (((_this2$props$theme$ma1 = _this2.props.theme.map3d) === null || _this2$props$theme$ma1 === void 0 ? void 0 : _this2$props$theme$ma1.objects3d) || []).forEach(function (entry) {
930
+ (_this2.props.theme.map3d.objects3d || []).forEach(function (entry) {
883
931
  objects.push(_objectSpread(_objectSpread({}, entry), {}, {
884
932
  type: "object3d"
885
933
  }));
@@ -908,12 +956,13 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
908
956
  objectTree[groupId].children = _buildObjectTree(entry.items, groupId);
909
957
  nodeIds.push(groupId);
910
958
  } else if (entry.type === "tiles3d") {
911
- var _entry$title, _entry$visibility2;
959
+ var _entry$title, _entry$visibility2, _entry$snap;
912
960
  objectTree[entry.name] = {
913
961
  objectId: entry.name,
914
962
  parent: parentId,
915
963
  title: (_entry$title = entry.title) !== null && _entry$title !== void 0 ? _entry$title : entry.name,
916
964
  visibility: (_entry$visibility2 = entry.visibility) !== null && _entry$visibility2 !== void 0 ? _entry$visibility2 : true,
965
+ snap: (_entry$snap = entry.snap) !== null && _entry$snap !== void 0 ? _entry$snap : true,
917
966
  opacity: 255,
918
967
  styles: entry.styles || {},
919
968
  style: entry.style || Object.keys(entry.styles || {})[0] || null
@@ -930,12 +979,13 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
930
979
  _this2.applySceneObjectState(entry.name, objectTree[entry.name], {}, objectTree);
931
980
  nodeIds.push(entry.name);
932
981
  } else if (entry.type === "object3d") {
933
- var _entry$title2, _entry$visibility3;
982
+ var _entry$title2, _entry$visibility3, _entry$snap2;
934
983
  objectTree[entry.name] = {
935
984
  objectId: entry.name,
936
985
  parent: parentId,
937
986
  title: (_entry$title2 = entry.title) !== null && _entry$title2 !== void 0 ? _entry$title2 : entry.name,
938
987
  visibility: (_entry$visibility3 = entry.visibility) !== null && _entry$visibility3 !== void 0 ? _entry$visibility3 : true,
988
+ snap: (_entry$snap2 = entry.snap) !== null && _entry$snap2 !== void 0 ? _entry$snap2 : true,
939
989
  opacity: 255
940
990
  };
941
991
  _this2.importObject3D(MiscUtils.resolveAssetsPath(entry.url), entry.name, false, {}, false, function () {
@@ -964,7 +1014,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
964
1014
  });
965
1015
 
966
1016
  // Inspector
967
- if (["1", "true"].includes((UrlParams.getParam("inspector") || "").toLowerCase())) {
1017
+ if ((process.env.NODE_ENV !== "production" || _this2.props.forceAllowInspector) && ["1", "true"].includes((UrlParams.getParam("inspector") || "").toLowerCase())) {
968
1018
  var inspectorContainer = document.createElement("div");
969
1019
  inspectorContainer.className = 'map3d-inspector';
970
1020
  _this2.container.appendChild(inspectorContainer);
@@ -1319,6 +1369,7 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1319
1369
  _this2.sceneObjectGroup = null;
1320
1370
  _this2.objectMap = {};
1321
1371
  _this2.tilesetStyles = {};
1372
+ _this2.state.sceneContext.eventDispatcher = new EventDispatcher();
1322
1373
  _this2.state.sceneContext.addLayer = _this2.addLayer;
1323
1374
  _this2.state.sceneContext.getLayer = _this2.getLayer;
1324
1375
  _this2.state.sceneContext.removeLayer = _this2.removeLayer;
@@ -1331,13 +1382,17 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1331
1382
  _this2.state.sceneContext.removeSceneObject = _this2.removeSceneObject;
1332
1383
  _this2.state.sceneContext.updateSceneObject = _this2.updateSceneObject;
1333
1384
  _this2.state.sceneContext.zoomToObject = _this2.zoomToObject;
1385
+ _this2.state.sceneContext.objectIsVisible = _this2.objectIsVisible;
1386
+ _this2.state.sceneContext.objectContainsPoints = _this2.objectContainsPoints;
1334
1387
  _this2.state.sceneContext.getMap = _this2.getMap;
1388
+ _this2.state.computeBoundsTree = _this2.computeBoundsTree;
1335
1389
  _this2.state.sceneContext.getTerrainHeightFromDTM = _this2.getTerrainHeightFromDTM;
1336
1390
  _this2.state.sceneContext.getTerrainHeightFromMap = _this2.getTerrainHeightFromMap;
1337
1391
  _this2.state.sceneContext.getSceneIntersection = _this2.getSceneIntersection;
1338
1392
  _this2.state.sceneContext.getSetting = _this2.getSetting;
1339
1393
  _this2.state.sceneContext.setSetting = _this2.setSetting;
1340
1394
  _this2.state.sceneContext.settings.fov = props.defaultFov;
1395
+ _this2.state.sceneContext.settings.pointSize = props.defaultPointSize;
1341
1396
  _this2.state.sceneContext.settings.sceneQuality = props.defaultSceneQuality;
1342
1397
  registerPermalinkDataStoreHook("map3d", _this2.store3dState);
1343
1398
  return _this2;
@@ -1389,16 +1444,22 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1389
1444
  this.setState(function (state) {
1390
1445
  return {
1391
1446
  sceneContext: _objectSpread(_objectSpread({}, state.sceneContext), {}, {
1392
- collisionObjects: Object.entries(state.sceneContext.objectTree).reduce(function (res, _ref12) {
1447
+ collisionObjects: Object.keys(state.sceneContext.objectTree).reduce(function (res, objectId) {
1448
+ var obj = _this3.objectMap[objectId];
1449
+ if (obj !== null && obj !== void 0 && obj.visible) {
1450
+ var _obj$tiles$group, _obj$tiles;
1451
+ res.push((_obj$tiles$group = (_obj$tiles = obj.tiles) === null || _obj$tiles === void 0 ? void 0 : _obj$tiles.group) !== null && _obj$tiles$group !== void 0 ? _obj$tiles$group : obj);
1452
+ }
1453
+ return res;
1454
+ }, []),
1455
+ snapObjects: Object.entries(state.sceneContext.objectTree).reduce(function (res, _ref12) {
1393
1456
  var _ref13 = _slicedToArray(_ref12, 2),
1394
- objectid = _ref13[0],
1457
+ objectId = _ref13[0],
1395
1458
  entry = _ref13[1];
1396
- if (entry.visibility && entry.opacity > 0) {
1397
- var obj = _this3.objectMap[entry.objectId];
1398
- if (obj) {
1399
- var _obj$tiles$group, _obj$tiles;
1400
- res.push((_obj$tiles$group = (_obj$tiles = obj.tiles) === null || _obj$tiles === void 0 ? void 0 : _obj$tiles.group) !== null && _obj$tiles$group !== void 0 ? _obj$tiles$group : obj);
1401
- }
1459
+ var obj = _this3.objectMap[objectId];
1460
+ if (obj !== null && obj !== void 0 && obj.visible && entry.snap) {
1461
+ var _obj$tiles$group2, _obj$tiles2;
1462
+ res.push((_obj$tiles$group2 = (_obj$tiles2 = obj.tiles) === null || _obj$tiles2 === void 0 ? void 0 : _obj$tiles2.group) !== null && _obj$tiles$group2 !== void 0 ? _obj$tiles$group2 : obj);
1402
1463
  }
1403
1464
  return res;
1404
1465
  }, [])
@@ -1410,6 +1471,15 @@ var Map3D = /*#__PURE__*/function (_React$Component2) {
1410
1471
  this.instance.view.camera.fov = this.state.sceneContext.settings.fov;
1411
1472
  this.instance.notifyChange(this.instance.view.camera);
1412
1473
  }
1474
+ if (this.state.sceneContext.settings.pointSize !== prevState.sceneContext.settings.pointSize) {
1475
+ Object.values(this.state.sceneContext.objectTree).map(function (entry) {
1476
+ var obj = _this3.state.sceneContext.getSceneObject(entry.objectId);
1477
+ if (_this3.objectContainsPoints(obj)) {
1478
+ obj.pointSize = _this3.state.sceneContext.settings.pointSize;
1479
+ _this3.instance.notifyChange(obj);
1480
+ }
1481
+ });
1482
+ }
1413
1483
  if (this.state.sceneContext.settings.sceneQuality !== prevState.sceneContext.settings.sceneQuality) {
1414
1484
  var quality = Math.max(20, this.state.sceneContext.settings.sceneQuality);
1415
1485
  this.map.segments = Math.pow(2, Math.floor(quality / 20));
@@ -1456,7 +1526,9 @@ _defineProperty(Map3D, "contextType", MapContainerPortalContext);
1456
1526
  _defineProperty(Map3D, "propTypes", {
1457
1527
  controlsPosition: PropTypes.string,
1458
1528
  defaultFov: PropTypes.number,
1529
+ defaultPointSize: PropTypes.number,
1459
1530
  defaultSceneQuality: PropTypes.number,
1531
+ forceAllowInspector: PropTypes.bool,
1460
1532
  innerRef: PropTypes.func,
1461
1533
  layers: PropTypes.array,
1462
1534
  mouseButtons: PropTypes.object,
@@ -1487,8 +1559,10 @@ _defineProperty(Map3D, "defaultSceneState", {
1487
1559
  colorLayers: {},
1488
1560
  objectTree: {},
1489
1561
  collisionObjects: [],
1562
+ snapObjects: [],
1490
1563
  settings: {
1491
1564
  fov: 30,
1565
+ pointSize: 0,
1492
1566
  sceneQuality: 100
1493
1567
  },
1494
1568
  sceneId: null,
@@ -33,7 +33,7 @@ import { connect } from 'react-redux';
33
33
  import classNames from 'classnames';
34
34
  import PropTypes from 'prop-types';
35
35
  import { MOUSE, Vector3 } from 'three';
36
- import ConfigUtils from '../../utils/ConfigUtils';
36
+ import personSvg from '../../resources/person.svg';
37
37
  import { UrlParams } from '../../utils/PermaLinkUtils';
38
38
  import Icon from '../Icon';
39
39
  import { MapButtonPortalContext } from '../PluginsContainer';
@@ -85,8 +85,7 @@ var MapControls3D = /*#__PURE__*/function (_React$Component) {
85
85
  _this.props.sceneContext.scene.domElement.addEventListener('click', _this.setupFirstPerson, {
86
86
  once: true
87
87
  });
88
- var cursor = ConfigUtils.getAssetsPath() + "/img/person.svg";
89
- _this.props.sceneContext.scene.domElement.style.cursor = "url(".concat(cursor, "), pointer");
88
+ _this.props.sceneContext.scene.domElement.style.cursor = "url(".concat(personSvg, "), pointer");
90
89
  _this.setState({
91
90
  pickingFirstPerson: true
92
91
  });
@@ -108,12 +108,13 @@ var CreateTool3D = /*#__PURE__*/function (_React$Component) {
108
108
  var mesh = new Mesh(geometry, material);
109
109
  mesh.castShadow = true;
110
110
  mesh.receiveShadow = true;
111
- drawGroup.add(mesh);
112
111
  var pos = _this.drawCursor.points[0];
113
112
  mesh.position.copy(new Vector3(Math.round(pos.x), Math.round(pos.y), Math.floor(pos.z + 0.5 * s)));
114
113
  mesh.updateMatrixWorld();
114
+ drawGroup.attach(mesh);
115
115
  _this.props.sceneContext.scene.notifyChange();
116
116
  _this.props.objectCreated(mesh);
117
+ _this.props.sceneContext.computeBoundsTree(mesh);
117
118
  }
118
119
  });
119
120
  return _this;