UNPKG

qwc2-lts

Version:
447 lines (444 loc) 22.5 kB
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 _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 Coordinates from '@giro3d/giro3d/core/geographic/Coordinates'; import classNames from 'classnames'; import PropTypes from 'prop-types'; import { Raycaster, Vector2, Vector3 } from 'three'; import { MapControls } from 'three/addons/controls/MapControls'; import Icon from '../Icon'; import './style/MapControls3D.css'; var MapControls3D = /*#__PURE__*/function (_React$Component) { function MapControls3D(props) { var _this; _classCallCheck(this, MapControls3D); _this = _callSuper(this, MapControls3D, [props]); _defineProperty(_this, "state", { firstPerson: false }); _defineProperty(_this, "keyHandler", function (event) { if (event.repeat) { return; } else if (event.key === "ArrowUp") { if (event.ctrlKey) _this.tilt(event, 0, 1, true);else _this.pan(event, 0, 1, true); } else if (event.key === "ArrowDown") { if (event.ctrlKey) _this.tilt(event, 0, -1, true);else _this.pan(event, 0, -1, true); } else if (event.key === "ArrowLeft") { if (event.ctrlKey) _this.tilt(event, 1, 0, true);else _this.pan(event, 1, 0, true); } else if (event.key === "ArrowRight") { if (event.ctrlKey) _this.tilt(event, -1, 0, true);else _this.pan(event, -1, 0, true); } }); _defineProperty(_this, "home", function () { _this.leaveFirstPerson(); var extent = _this.props.sceneContext.map.extent; var bounds = [extent.west, extent.south, extent.east, extent.north]; _this.setViewToExtent(bounds); }); _defineProperty(_this, "pan", function (ev, dx, dy) { var keyboard = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; // Pan faster the heigher one is above the terrain var d = _this.state.firstPerson ? 200000 : 100 + (_this.props.sceneContext.scene.view.camera.position.z - _this.props.sceneContext.scene.view.controls.target.z) / 250; var delta = new Vector2(dx, dy).multiplyScalar(d); _this.animationInterrupted = false; var lastTimestamp = new Date() / 1000; var _animate = function animate() { if (_this.animationInterrupted) { return; } // Pan <delta> distance per second var timestamp = new Date() / 1000; var k = timestamp - lastTimestamp; lastTimestamp = timestamp; _this.props.sceneContext.scene.view.controls._pan(delta.x * k, delta.y * k); _this.props.sceneContext.scene.notifyChange(); requestAnimationFrame(_animate); }; requestAnimationFrame(_animate); var element = keyboard ? ev.target : ev.view; var event = keyboard ? "keyup" : "mouseup"; element.addEventListener(event, function () { _this.animationInterrupted = true; }, { once: true }); }); _defineProperty(_this, "tilt", function (ev, yaw, az) { var keyboard = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false; if (_this.state.firstPerson) { az *= -1; yaw *= -1; } // Pan faster the heigher one is above the terrain _this.animationInterrupted = false; var lastTimestamp = new Date() / 1000; var _animate2 = function animate() { if (_this.animationInterrupted) { return; } // Pan <delta> distance per second var timestamp = new Date() / 1000; var k = timestamp - lastTimestamp; lastTimestamp = timestamp; if (az) { _this.props.sceneContext.scene.view.controls._rotateUp(az * k); } if (yaw) { _this.props.sceneContext.scene.view.controls._rotateLeft(yaw * k); } _this.props.sceneContext.scene.notifyChange(); requestAnimationFrame(_animate2); }; requestAnimationFrame(_animate2); var element = keyboard ? ev.target : ev.view; var event = keyboard ? "keyup" : "mouseup"; element.addEventListener(event, function () { _this.animationInterrupted = true; }, { once: true }); }); _defineProperty(_this, "resetTilt", function () { if (_this.state.firstPerson) { var target = _this.props.sceneContext.scene.view.controls.target; var camerapos = _this.props.sceneContext.scene.view.camera.position; _this.props.sceneContext.scene.view.controls.target.set(target.x, target.y, camerapos.z); } else { // Animate from old to new position var _target = _this.props.sceneContext.scene.view.controls.target; var oldPosition = _this.props.sceneContext.scene.view.camera.position.clone(); var oldYaw = _this.props.sceneContext.scene.view.controls.getAzimuthalAngle(); var newPosition = new Vector3(_target.x, _target.y, _target.distanceTo(oldPosition)); var startTime = new Date() / 1000; _this.animationInterrupted = false; var _animate3 = function animate() { if (!_this.props.sceneContext.scene || _this.animationInterrupted) { return; } var duration = 2; var elapsed = new Date() / 1000 - startTime; var x = elapsed / duration; var k = 0.5 * (1 - Math.cos(x * Math.PI)); var currentPosition = new Vector3().lerpVectors(oldPosition, newPosition, k); currentPosition.x -= _target.x; currentPosition.y -= _target.y; currentPosition.applyAxisAngle(new Vector3(0, 0, 1), -oldYaw * k); currentPosition.x += _target.x; currentPosition.y += _target.y; _this.props.sceneContext.scene.view.camera.position.copy(currentPosition); _this.props.sceneContext.scene.notifyChange(); if (elapsed < duration) { requestAnimationFrame(_animate3); } else { _this.props.sceneContext.scene.view.camera.position.copy(newPosition); _this.props.sceneContext.scene.notifyChange(); } }; requestAnimationFrame(_animate3); } }); _defineProperty(_this, "setViewToExtent", function (bounds) { var _elevationResult$samp; var angle = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; if (_this.state.firstPerson) { _this.leaveFirstPerson(); } var center = { x: 0.5 * (bounds[0] + bounds[2]), y: 0.5 * (bounds[1] + bounds[3]) }; var elevationResult = _this.props.sceneContext.map.getElevation({ coordinates: new Coordinates(_this.props.sceneContext.mapCrs, center.x, center.y) }); elevationResult.samples.sort(function (a, b) { return a.resolution > b.resolution; }); center.z = ((_elevationResult$samp = elevationResult.samples[0]) === null || _elevationResult$samp === void 0 ? void 0 : _elevationResult$samp.elevation) || 0; // Camera height to width bbox width var fov = 35 / 180 * Math.PI; var cameraHeight = (bounds[2] - bounds[0]) / (2 * Math.tan(fov / 2)); // Animate from old to new position/target var oldPosition = _this.props.sceneContext.scene.view.camera.position.clone(); var oldTarget = _this.props.sceneContext.scene.view.controls.target.clone(); var oldYaw = _this.props.sceneContext.scene.view.controls.getAzimuthalAngle(); var newPosition = new Vector3(center.x, center.y, center.z + cameraHeight); var newTarget = new Vector3(center.x, center.y, center.z); var rotateAngle = -oldYaw + angle; while (rotateAngle > Math.PI) { rotateAngle -= 2 * Math.PI; } while (rotateAngle < -Math.PI) { rotateAngle += 2 * Math.PI; } var startTime = new Date() / 1000; _this.animationInterrupted = false; var _animate4 = function animate() { if (!_this.props.sceneContext.scene || _this.animationInterrupted) { return; } var duration = 2; var elapsed = new Date() / 1000 - startTime; var x = elapsed / duration; var k = 0.5 * (1 - Math.cos(x * Math.PI)); var currentPosition = new Vector3().lerpVectors(oldPosition, newPosition, k); var currentTarget = new Vector3().lerpVectors(oldTarget, newTarget, k); currentPosition.x -= currentTarget.x; currentPosition.y -= currentTarget.y; currentPosition.applyAxisAngle(new Vector3(0, 0, 1), rotateAngle * k); currentPosition.x += currentTarget.x; currentPosition.y += currentTarget.y; _this.props.sceneContext.scene.view.camera.position.copy(currentPosition); _this.props.sceneContext.scene.view.controls.target.copy(currentTarget); _this.props.sceneContext.scene.notifyChange(); if (elapsed < duration) { requestAnimationFrame(_animate4); } else { _this.props.sceneContext.scene.view.camera.position.copy(newPosition); _this.props.sceneContext.scene.view.controls.target.copy(newTarget); _this.props.sceneContext.scene.view.controls._rotateLeft(-angle); _this.props.sceneContext.scene.notifyChange(); } }; requestAnimationFrame(_animate4); }); _defineProperty(_this, "updateControlsTarget", function () { var _terrInter$point$z, _terrInter$point; var target = _this.props.sceneContext.scene.view.controls.target; if (_this.prevTarget && _this.prevTarget.distanceToSquared(target) < 50) { return; } _this.prevTarget = target.clone(); var x = target.x; var y = target.y; var raycaster = new Raycaster(); // Query highest resolution terrain tile (i.e. tile with no children) raycaster.set(new Vector3(x, y, target.z + 100000), new Vector3(0, 0, -1)); var terrInter = raycaster.intersectObjects([_this.props.sceneContext.map.object3d]).filter(function (result) { return result.object.children.length === 0; })[0]; var terrainHeight = (_terrInter$point$z = terrInter === null || terrInter === void 0 || (_terrInter$point = terrInter.point) === null || _terrInter$point === void 0 ? void 0 : _terrInter$point.z) !== null && _terrInter$point$z !== void 0 ? _terrInter$point$z : 0; // FIXME: Why does raycaster.intersectObjects on terrain return 0-ish even when above terrain? if (terrainHeight <= 1) { return; } if (_this.state.firstPerson) { var delta = terrainHeight + _this.personHeight - _this.props.sceneContext.scene.view.camera.position.z; _this.props.sceneContext.scene.view.camera.position.z += delta; _this.props.sceneContext.scene.view.controls.target.z += delta; return; } var cameraHeight = _this.props.sceneContext.scene.view.camera.position.z; // If camera height is at terrain height, target height should be at terrain height // If camera height is at twice the terrain height or further, target height should be zero var targetHeight = terrainHeight > 0 ? terrainHeight * Math.max(0, 1 - (cameraHeight - terrainHeight) / terrainHeight) : 0; _this.props.sceneContext.scene.view.controls.target.z = targetHeight; }); _defineProperty(_this, "stopAnimations", function () { _this.animationInterrupted = true; }); _defineProperty(_this, "switchToFirstPersonView", function (ev) { if (!_this.state.firstPerson) { _this.setupFirstPerson(ev); } }); _defineProperty(_this, "toggleFirstPersonControls", function () { if (_this.state.firstPerson) { _this.leaveFirstPerson(); } else { _this.props.sceneContext.scene.domElement.addEventListener('click', _this.setupFirstPerson, { once: true }); } }); _defineProperty(_this, "setupFirstPerson", function (ev) { 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.getTerrainHeight([pos.x, pos.y]).then(function (z) { // Animate from old to new position/target var oldPosition = _this.props.sceneContext.scene.view.camera.position.clone(); var oldTarget = _this.props.sceneContext.scene.view.controls.target.clone(); var newPosition = new Vector3(pos.x, pos.y, z + _this.personHeight); var newTarget = new Vector3(pos.x, pos.y + 300, z + _this.personHeight); var startTime = new Date() / 1000; _this.animationInterrupted = false; var _animate5 = function animate() { if (!_this.props.sceneContext.scene || _this.animationInterrupted) { return; } var duration = 2; var elapsed = new Date() / 1000 - startTime; var x = elapsed / duration; var k = 0.5 * (1 - Math.cos(x * Math.PI)); var currentPosition = new Vector3().lerpVectors(oldPosition, newPosition, k); var currentTarget = new Vector3().lerpVectors(oldTarget, newTarget, k); _this.props.sceneContext.scene.view.camera.position.copy(currentPosition); _this.props.sceneContext.scene.view.controls.target.copy(currentTarget); _this.props.sceneContext.scene.notifyChange(); if (elapsed < duration) { requestAnimationFrame(_animate5); } else { _this.props.sceneContext.scene.view.camera.position.copy(newPosition); _this.props.sceneContext.scene.view.controls.target.set(pos.x, pos.y + 0.1, z + _this.personHeight); _this.props.sceneContext.scene.notifyChange(); _this.controls.maxPolarAngle = 0.8 * Math.PI; _this.controls.panSpeed = 600; _this.controls.enableZoom = false; _this.setState({ firstPerson: true }); } }; requestAnimationFrame(_animate5); }); }); _defineProperty(_this, "leaveFirstPerson", function () { _this.controls.maxPolarAngle = Math.PI * 0.5; _this.controls.panSpeed = 1; _this.controls.enableZoom = true; _this.setState({ firstPerson: false }, function () { var cameraPos = _this.props.sceneContext.scene.view.camera.position; var bounds = [cameraPos.x - 1000, cameraPos.y - 1000, cameraPos.x + 1000, cameraPos.y + 1000]; _this.setViewToExtent(bounds); }); }); _this.animationInterrupted = false; _this.personHeight = 2; _this.prevTarget = null; return _this; } _inherits(MapControls3D, _React$Component); return _createClass(MapControls3D, [{ key: "componentDidMount", value: function componentDidMount() { var sceneElement = this.props.sceneContext.scene.domElement; sceneElement.tabIndex = 0; this.controls = new MapControls(this.props.sceneContext.scene.view.camera, sceneElement); this.controls.zoomToCursor = true; this.controls.enableDamping = true; this.controls.dampingFactor = 0.2; this.controls.maxPolarAngle = Math.PI * 0.5; sceneElement.addEventListener('keydown', this.keyHandler); this.props.sceneContext.scene.view.setControls(this.controls); var targetPos = this.props.sceneContext.scene.view.camera.position.clone(); targetPos.z = 0; this.controls.target = targetPos; this.controls.addEventListener('change', this.updateControlsTarget); sceneElement.addEventListener('dblclick', this.switchToFirstPersonView); } }, { key: "componentWillUnmount", value: function componentWillUnmount() { this.animationInterrupted = true; var sceneElement = this.props.sceneContext.scene.domElement; sceneElement.addEventListener('keydown', this.keyHandler); this.controls.removeEventListener('change', this.updateControlsTarget); this.props.sceneContext.scene.view.setControls(null); this.controls.dispose(); this.controls = null; this.props.sceneContext.scene.domElement.removeEventListener('dblclick', this.switchToFirstPersonView); } }, { key: "render", value: function render() { var _this2 = this; var firstPersonButtonClasses = classNames({ "map3d-firstperson-button": true, "map3d-firstperson-button-active": this.state.firstPerson }); return [/*#__PURE__*/React.createElement("div", { className: "map3d-nav-pan", key: "MapPanWidget" }, /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "chevron-up", onMouseDown: function onMouseDown(ev) { return _this2.pan(ev, 0, 1); } }), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "chevron-left", onMouseDown: function onMouseDown(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", onMouseDown: function onMouseDown(ev) { return _this2.pan(ev, -1, 0); } }), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "chevron-down", onMouseDown: function onMouseDown(ev) { return _this2.pan(ev, 0, -1); } }), /*#__PURE__*/React.createElement("span", null)), /*#__PURE__*/React.createElement("div", { className: "map3d-nav-rotate", key: "MapRotateWidget" }, /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "tilt-up", onMouseDown: function onMouseDown(ev) { return _this2.tilt(ev, 0, 1); } }), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "tilt-left", onMouseDown: function onMouseDown(ev) { return _this2.tilt(ev, 1, 0); } }), /*#__PURE__*/React.createElement(Icon, { icon: "point", onClick: function onClick() { return _this2.resetTilt(); } }), /*#__PURE__*/React.createElement(Icon, { icon: "tilt-right", onMouseDown: function onMouseDown(ev) { return _this2.tilt(ev, -1, 0); } }), /*#__PURE__*/React.createElement("span", null), /*#__PURE__*/React.createElement(Icon, { icon: "tilt-down", onMouseDown: function onMouseDown(ev) { return _this2.tilt(ev, 0, -1); } }), /*#__PURE__*/React.createElement("span", null)), /*#__PURE__*/React.createElement("div", { className: firstPersonButtonClasses, key: "FirstPersonButton", onClick: this.toggleFirstPersonControls }, /*#__PURE__*/React.createElement(Icon, { icon: "person" }))]; } }]); }(React.Component); _defineProperty(MapControls3D, "propTypes", { sceneContext: PropTypes.object }); export { MapControls3D as default };