qwc2
Version:
QGIS Web Client
395 lines (393 loc) • 20.1 kB
JavaScript
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
function _construct(t, e, r) { if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); var o = [null]; o.push.apply(o, e); var p = new (t.bind.apply(t, o))(); return r && _setPrototypeOf(p, r.prototype), p; }
function _toConsumableArray(r) { return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); }
function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
function _iterableToArray(r) { if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); }
function _arrayWithoutHoles(r) { if (Array.isArray(r)) return _arrayLikeToArray(r); }
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); }
function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } }
function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; }
function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); }
function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); }
function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); }
function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); }
function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
/**
* Copyright 2024 Sourcepole AG
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import { MOUSE, Vector3 } from 'three';
import ConfigUtils from '../../utils/ConfigUtils';
import { UrlParams } from '../../utils/PermaLinkUtils';
import Icon from '../Icon';
import { MapButtonPortalContext } from '../PluginsContainer';
import FirstPersonControls3D from './utils/FirstPersonControls3D';
import OrbitControls3D from './utils/OrbitControls3D';
import './style/MapControls3D.css';
// FIXME: camera.fov is 30, but in reality seems to be 50 (as would be the threejs default)
var CAMERA_FOV = 50;
var MapControls3D = /*#__PURE__*/function (_React$Component) {
function MapControls3D(props) {
var _buttonMap$_this$prop, _buttonMap$_this$prop2, _buttonMap$_this$prop3;
var _this;
_classCallCheck(this, MapControls3D);
_this = _callSuper(this, MapControls3D, [props]);
_defineProperty(_this, "state", {
pickingFirstPerson: false,
firstPerson: false
});
_defineProperty(_this, "unload", function (el) {
// componentWillUnmount is called too early, so do cleanup when the element is actually removed
if (!el) {
_this.controls.removeEventListener('change', _this.updateControlsTarget);
_this.fpcontrols.removeEventListener('change', _this.updateFpUrlParams);
if (_this.state.firstPerson) {
_this.fpcontrols.disconnect();
} else {
_this.controls.disconnect();
}
_this.props.sceneContext.scene.domElement.removeEventListener('dblclick', _this.switchToFirstPersonView);
}
});
_defineProperty(_this, "switchToFirstPersonView", function (ev) {
// Don't do anything if a task is set, may interfere
if (!_this.props.currentTask && !_this.state.firstPerson) {
_this.setupFirstPerson(ev);
}
});
_defineProperty(_this, "toggleFirstPersonControls", function () {
if (_this.state.firstPerson) {
_this.leaveFirstPerson();
} else if (_this.state.pickingFirstPerson) {
_this.props.sceneContext.scene.domElement.removeEventListener('click', _this.setupFirstPerson);
_this.props.sceneContext.scene.domElement.style.cursor = '';
_this.setState({
pickingFirstPerson: false
});
} else {
_this.props.sceneContext.scene.domElement.addEventListener('click', _this.setupFirstPerson, {
once: true
});
var cursor = ConfigUtils.getAssetsPath() + "/img/person.svg";
_this.props.sceneContext.scene.domElement.style.cursor = "url(".concat(cursor, "), pointer");
_this.setState({
pickingFirstPerson: true
});
}
});
_defineProperty(_this, "setupFirstPerson", function (ev) {
_this.props.sceneContext.scene.domElement.style.cursor = '';
var rect = ev.target.getBoundingClientRect();
var mouseX = (ev.clientX - rect.left) / rect.width * 2 - 1;
var mouseY = -((ev.clientY - rect.top) / rect.height) * 2 + 1;
var intersection = _this.props.sceneContext.getSceneIntersection(mouseX, mouseY, false);
if (!intersection) {
return;
}
var pos = intersection.point;
_this.props.sceneContext.getTerrainHeightFromDTM([pos.x, pos.y]).then(function (z) {
var camerapos = new Vector3(pos.x, pos.y, z + _this.fpcontrols.personHeight);
var targetpos = new Vector3(pos.x, pos.y + 300, z + _this.fpcontrols.personHeight);
_this.controls.animateTo(camerapos, targetpos, 0, function () {
_this.controls.disconnect();
_this.fpcontrols.connect(_this.props.sceneContext);
_this.fpcontrols.setView(camerapos, new Vector3(0, 1, 0));
_this.setState({
firstPerson: true,
pickingFirstPerson: false
});
});
});
});
_defineProperty(_this, "leaveFirstPerson", function () {
if (_this.state.firstPerson) {
_this.setState({
firstPerson: false
}, function () {
// Need to ensure this.state.firstPerson is false to avoid endless loop
var camerapos = _this.props.sceneContext.scene.view.camera.position;
_this.fpcontrols.disconnect();
_this.controls.connect(_this.props.sceneContext);
_this.controls.setView(camerapos, new Vector3().addVectors(camerapos, _this.fpcontrols.lookAt));
var bounds = [camerapos.x - 750, camerapos.y - 750, camerapos.x + 750, camerapos.y + 750];
_this.setViewToExtent(bounds);
});
}
});
_defineProperty(_this, "home", function () {
var extent = _this.props.sceneContext.map.extent;
var bounds = [extent.west, extent.south, extent.east, extent.north];
_this.setViewToExtent(bounds);
});
_defineProperty(_this, "setViewToExtent", function (bounds) {
var angle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
_this.leaveFirstPerson();
var fov = CAMERA_FOV / 180 * Math.PI;
var cameraHeight = (bounds[2] - bounds[0]) / (2 * Math.tan(fov / 2));
var center = {
x: 0.5 * (bounds[0] + bounds[2]),
y: 0.5 * (bounds[1] + bounds[3])
};
var camerapos = new Vector3(center.x, center.y, cameraHeight);
var target = new Vector3(center.x, center.y, 0);
var h = _this.props.sceneContext.getTerrainHeightFromMap([center.x, center.y]);
// Fall back to getTerrainHeightFromDTM if map is not yet loaded
if (h === undefined) {
_this.props.sceneContext.getTerrainHeightFromDTM([center.x, center.y]).then(function (h2) {
camerapos.z += h2;
target.z += h2;
_this.controls.animateTo(camerapos, target, angle);
});
} else {
camerapos.z += h;
target.z += h;
_this.controls.animateTo(camerapos, target, angle);
}
});
_defineProperty(_this, "pan", function (ev, dx, dy) {
var panInterval = setInterval(function () {
_this.props.sceneContext.scene.view.controls.panView(dx, dy);
}, 50);
ev.view.addEventListener('pointerup', function () {
clearInterval(panInterval);
}, {
once: true
});
});
_defineProperty(_this, "tilt", function (ev, azimuth, polar) {
var tiltInterval = setInterval(function () {
_this.props.sceneContext.scene.view.controls.tiltView(azimuth, polar);
}, 50);
ev.view.addEventListener('pointerup', function () {
clearInterval(tiltInterval);
}, {
once: true
});
});
_defineProperty(_this, "resetTilt", function () {
var camerapos = _this.props.sceneContext.scene.view.camera.position;
if (_this.state.firstPerson) {
var newLookAt = _this.fpcontrols.lookAt.clone();
newLookAt.z = 0;
_this.fpcontrols.setView(camerapos, newLookAt.normalize());
} else {
var target = _this.controls.target;
var newcamerapos = new Vector3(target.x, target.y, target.distanceTo(camerapos));
_this.controls.animateTo(newcamerapos, target, 0);
}
});
_defineProperty(_this, "zoom", function (ev, delta) {
var zoomInterval = setInterval(function () {
var camerapos = _this.props.sceneContext.scene.view.camera.position;
var target = _this.controls.target;
var k = Math.min(150, Math.sqrt(target.distanceTo(camerapos)));
_this.props.sceneContext.scene.view.controls.zoomView(delta * k);
}, 50);
ev.view.addEventListener('pointerup', function () {
clearInterval(zoomInterval);
}, {
once: true
});
});
_defineProperty(_this, "updateUrlParams", function () {
var cpos = _this.props.sceneContext.scene.view.camera.position;
var tpos = _this.controls.target;
UrlParams.updateParams({
v3d: [cpos.x, cpos.y, cpos.z, tpos.x, tpos.y, tpos.z, 0].map(function (v) {
return v.toFixed(1);
}).join(",")
});
_this.props.onCameraChanged([tpos.x, tpos.y, tpos.z], [cpos.x, cpos.y, cpos.z], CAMERA_FOV);
});
_defineProperty(_this, "updateFpUrlParams", function () {
var cpos = _this.fpcontrols.target;
var lkat = _this.fpcontrols.lookAt;
var h = _this.fpcontrols.personHeight;
UrlParams.updateParams({
v3d: [cpos.x, cpos.y, cpos.z, lkat.x, lkat.y, lkat.z, h].map(function (v) {
return v.toFixed(1);
}).join(",")
});
_this.props.onCameraChanged([cpos.x, cpos.y, cpos.z], null);
});
_defineProperty(_this, "restoreView", function (viewState) {
if (viewState.camera && viewState.target) {
var camera = _construct(Vector3, _toConsumableArray(viewState.camera));
var target = _construct(Vector3, _toConsumableArray(viewState.target));
if (viewState.personHeight > 0) {
_this.controls.disconnect();
_this.fpcontrols.connect(_this.props.sceneContext);
_this.fpcontrols.setView(camera, target, viewState.personHeight);
_this.setState({
firstPerson: true
});
} else {
_this.controls.setView(camera, target);
}
}
});
var sceneElement = props.sceneContext.scene.domElement;
sceneElement.tabIndex = 0;
var buttonMap = {
pan: MOUSE.PAN,
rotate: MOUSE.ROTATE,
zoom: MOUSE.DOLLY
};
var mouseButtons = {
LEFT: (_buttonMap$_this$prop = buttonMap[_this.props.mouseButtons.left]) !== null && _buttonMap$_this$prop !== void 0 ? _buttonMap$_this$prop : null,
MIDDLE: (_buttonMap$_this$prop2 = buttonMap[_this.props.mouseButtons.middle]) !== null && _buttonMap$_this$prop2 !== void 0 ? _buttonMap$_this$prop2 : null,
RIGHT: (_buttonMap$_this$prop3 = buttonMap[_this.props.mouseButtons.right]) !== null && _buttonMap$_this$prop3 !== void 0 ? _buttonMap$_this$prop3 : null
};
_this.controls = new OrbitControls3D(props.sceneContext.scene.view.camera, mouseButtons);
_this.fpcontrols = new FirstPersonControls3D(props.sceneContext.scene.view.camera, mouseButtons);
_this.controls.connect(props.sceneContext);
var targetPos = props.sceneContext.scene.view.camera.position.clone();
targetPos.z = 0;
_this.controls.target = targetPos;
_this.controls.addEventListener('change', _this.updateUrlParams);
_this.fpcontrols.addEventListener('change', _this.updateFpUrlParams);
sceneElement.addEventListener('dblclick', _this.switchToFirstPersonView);
props.onControlsSet(_this);
_this.updateUrlParams();
return _this;
}
_inherits(MapControls3D, _React$Component);
return _createClass(MapControls3D, [{
key: "render",
value: function render() {
var _this2 = this;
var firstPersonButtonClasses = classNames({
"map3d-firstperson-button": true,
"map3d-firstperson-button-active": this.state.firstPerson
});
return [this.props.children, /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
className: "map3d-nav-pan",
"data-slot": 0,
key: "MapControlsPan",
style: {
order: 1000
}
}, /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "chevron-up",
onPointerDown: function onPointerDown(ev) {
return _this2.pan(ev, 0, 1);
}
}), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "chevron-left",
onPointerDown: function onPointerDown(ev) {
return _this2.pan(ev, -1, 0);
}
}), /*#__PURE__*/React.createElement(Icon, {
icon: "home",
onClick: function onClick() {
return _this2.home();
}
}), /*#__PURE__*/React.createElement(Icon, {
icon: "chevron-right",
onPointerDown: function onPointerDown(ev) {
return _this2.pan(ev, 1, 0);
}
}), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "chevron-down",
onPointerDown: function onPointerDown(ev) {
return _this2.pan(ev, 0, -1);
}
}), /*#__PURE__*/React.createElement("span", null)), this.context), /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
className: "map3d-nav-rotate",
"data-slot": 0,
key: "MapControlsRotate",
style: {
order: 999
}
}, /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "tilt-up",
onPointerDown: function onPointerDown(ev) {
return _this2.tilt(ev, 0, 0.1);
}
}), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "tilt-left",
onPointerDown: function onPointerDown(ev) {
return _this2.tilt(ev, 0.1, 0);
}
}), /*#__PURE__*/React.createElement(Icon, {
icon: "point",
onClick: function onClick() {
return _this2.resetTilt();
}
}), /*#__PURE__*/React.createElement(Icon, {
icon: "tilt-right",
onPointerDown: function onPointerDown(ev) {
return _this2.tilt(ev, -0.1, 0);
}
}), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, {
icon: "tilt-down",
onPointerDown: function onPointerDown(ev) {
return _this2.tilt(ev, 0, -0.1);
}
}), /*#__PURE__*/React.createElement("span", null)), this.context), /*#__PURE__*/ReactDOM.createPortal(!this.state.firstPerson ? /*#__PURE__*/React.createElement("div", {
className: "map3d-nav-zoom",
"data-slot": 0,
key: "MapControlsSpacerZoom",
style: {
order: 998
}
}, /*#__PURE__*/React.createElement(Icon, {
icon: "plus",
onPointerDown: function onPointerDown(ev) {
return _this2.zoom(ev, +1);
}
}), /*#__PURE__*/React.createElement(Icon, {
icon: "minus",
onPointerDown: function onPointerDown(ev) {
return _this2.zoom(ev, -1);
}
})) : null, this.context), /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
className: firstPersonButtonClasses,
"data-slot": 0,
key: "MapControlsFirstPerson",
style: {
order: 997
}
}, /*#__PURE__*/React.createElement(Icon, {
icon: "person",
onClick: this.toggleFirstPersonControls
})), this.context), this.props.controlsPosition !== 'bottom' ? /*#__PURE__*/ReactDOM.createPortal(/*#__PURE__*/React.createElement("div", {
className: "map3d-nav-spacer",
key: "MapControlsSpacer",
style: {
order: 996
}
}), this.context) : null];
}
}]);
}(React.Component);
_defineProperty(MapControls3D, "contextType", MapButtonPortalContext);
_defineProperty(MapControls3D, "propTypes", {
children: PropTypes.oneOfType([PropTypes.node, PropTypes.func]),
controlsPosition: PropTypes.string,
currentTask: PropTypes.string,
mouseButtons: PropTypes.object,
onCameraChanged: PropTypes.func,
onControlsSet: PropTypes.func,
sceneContext: PropTypes.object
});
export default connect(function (state) {
return {
currentTask: state.task.id
};
}, {})(MapControls3D);