qwc2
Version:
QGIS Web Client
526 lines (522 loc) • 23.7 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 ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
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 { connect, Provider } from 'react-redux';
import isEmpty from 'lodash.isempty';
import PropTypes from 'prop-types';
import * as displayExports from '../actions/display';
import { setView3dMode, View3DMode } from '../actions/display';
import * as layersExports from '../actions/layers';
import { LayerRole, addLayerFeatures, removeLayer } from '../actions/layers';
import { panTo, zoomToPoint } from '../actions/map';
import * as mapExports from '../actions/map';
import * as themeExports from '../actions/theme';
import PluginsContainer from '../components/PluginsContainer';
import ResizeableWindow from '../components/ResizeableWindow';
import StandardApp from '../components/StandardApp';
import View3DSwitcher from '../components/map3d/View3DSwitcher';
import Spinner from '../components/widgets/Spinner';
import ReducerIndex from '../reducers/index';
import personIcon from '../resources/person.png';
import { createStore } from '../stores/StandardStore';
import ConfigUtils from '../utils/ConfigUtils';
import LocaleUtils from '../utils/LocaleUtils';
import MapUtils from '../utils/MapUtils';
import { UrlParams } from '../utils/PermaLinkUtils';
import './style/View3D.css';
/**
* Displays a 3D map view.
*
* See [3D View](../../topics/View3D).
*/
var View3D = /*#__PURE__*/function (_React$Component) {
function View3D(props) {
var _this;
_classCallCheck(this, View3D);
_this = _callSuper(this, View3D, [props]);
_defineProperty(_this, "state", {
componentLoaded: false,
windowDetached: false,
viewsLocked: false,
storedState: null
});
_defineProperty(_this, "render3DWindow", function () {
if (_this.props.view3dMode > View3DMode.DISABLED) {
var extraControls = [{
icon: "sync",
callback: _this.sync2DExtent,
title: LocaleUtils.tr("map3d.syncview")
}, {
icon: "lock",
callback: _this.setLockViews,
title: LocaleUtils.tr("map3d.lockview"),
active: _this.state.viewsLocked
}];
if (!_this.state.windowDetached) {
extraControls.push({
icon: "maximize",
callback: function callback() {
return _this.props.setView3dMode(View3DMode.FULLSCREEN);
},
title: LocaleUtils.tr("window.maximize")
});
}
var Map3D = _this.map3dComponent;
var device = ConfigUtils.isMobile() ? 'mobile' : 'desktop';
var pluginsConfig = _this.props.view3dMode === View3DMode.FULLSCREEN ? _this.props.localConfig.plugins[device].filter(function (entry) {
return entry.availableIn3D;
}) : [];
return /*#__PURE__*/React.createElement(ResizeableWindow, {
extraControls: extraControls,
fullscreen: _this.props.view3dMode === View3DMode.FULLSCREEN,
icon: "map3d",
initialHeight: _this.props.geometry.initialHeight,
initialWidth: _this.props.geometry.initialWidth,
initialX: _this.props.geometry.initialX,
initialY: _this.props.geometry.initialY,
initiallyDocked: _this.props.geometry.initiallyDocked,
key: "View3DWindow",
maximizeable: false,
onClose: _this.onClose,
onExternalWindowResized: _this.redrawScene,
onFocusChanged: _this.windowFocusChanged,
onGeometryChanged: _this.onGeometryChanged,
splitScreenWhenDocked: true,
splitTopAndBottomBar: true,
title: LocaleUtils.tr("map3d.title")
}, _this.state.componentLoaded ? /*#__PURE__*/React.createElement(Provider, {
store: _this.store
}, /*#__PURE__*/React.createElement(PluginsContainer, {
pluginsConfig: pluginsConfig
}, /*#__PURE__*/React.createElement(Map3D, {
controlsPosition: _this.props.controlsPosition,
defaultSceneQuality: _this.props.defaultSceneQuality,
innerRef: _this.setRef,
mouseButtons: _this.props.mouseButtons,
onCameraChanged: _this.onCameraChanged,
onMapInitialized: _this.setupMap,
pluginOptions: _this.props.pluginOptions,
plugins3d: _this.props.plugins3d,
searchProviders: _this.props.searchProviders,
theme: _this.props.theme
}), _this.props.view3dMode === View3DMode.DISABLING ? /*#__PURE__*/React.createElement("div", {
className: "view3d-busy-overlay"
}, /*#__PURE__*/React.createElement(Spinner, null), /*#__PURE__*/React.createElement("span", null, LocaleUtils.tr("view3d.storingstate"))) : null)) : null);
}
return null;
});
_defineProperty(_this, "onClose", function () {
_this.props.setView3dMode(View3DMode.DISABLING);
});
_defineProperty(_this, "onGeometryChanged", function (geometry) {
if (geometry.maximized && _this.props.view3dMode !== View3DMode.FULLSCREEN) {
_this.props.setView3dMode(View3DMode.FULLSCREEN);
}
_this.setState({
windowDetached: geometry.detached
});
});
_defineProperty(_this, "onCameraChanged", function (center, camera, fov) {
// Note: If camera pos is NULL, we are in first-person-view
if (_this.state.viewsLocked && _this.focusedMap === "map3d") {
var rotation = undefined;
if (camera) {
rotation = Math.atan2(center[1] - camera[1], center[0] - camera[0]) - 0.5 * Math.PI;
var distance = Math.sqrt((camera[0] - center[0]) * (camera[0] - center[0]) + (camera[1] - center[1]) * (camera[1] - center[1]) + (camera[2] - center[2]) * (camera[2] - center[2]));
var fovrad = fov / 180 * Math.PI;
var bboxWidth = distance * (2 * Math.tan(fovrad / 2));
var bbox = [-0.5 * bboxWidth, 0, 0.5 * bboxWidth, 0];
var zoom = MapUtils.getZoomForExtent(bbox, _this.props.map.resolutions, _this.props.map.size, 0, _this.props.map.scales.length - 1);
_this.props.zoomToPoint(center.slice(0, 2), zoom, _this.props.theme.mapCrs, rotation);
if (_this.firstPersonMarker) {
_this.props.removeLayer("view3d-firstperson-cone");
_this.firstPersonMarker = false;
}
} else {
_this.props.panTo(center.slice(0, 2), _this.props.theme.mapCrs, rotation);
var feature = {
geometry: {
type: 'Point',
coordinates: center.slice(0, 2)
},
crs: _this.props.theme.mapCrs,
styleName: 'marker',
styleOptions: {
iconSrc: personIcon
}
};
var layer = {
id: "view3d-firstperson-marker",
role: LayerRole.MARKER
};
_this.props.addLayerFeatures(layer, [feature], true);
_this.firstPersonMarker = true;
}
} else if (_this.firstPersonMarker) {
_this.props.removeLayer("view3d-firstperson-marker");
_this.firstPersonMarker = false;
}
});
_defineProperty(_this, "setRef", function (ref) {
_this.map3dComponentRef = ref;
});
_defineProperty(_this, "sync2DExtent", function () {
if (_this.map3dComponentRef) {
_this.map3dComponentRef.setViewToExtent(_this.props.map.bbox.bounds, _this.props.map.bbox.rotation);
}
});
_defineProperty(_this, "setLockViews", function () {
_this.setState(function (state) {
return {
viewsLocked: !state.viewsLocked
};
});
if (_this.firstPersonMarker) {
_this.props.removeLayer("view3d-firstperson-marker");
_this.firstPersonMarker = false;
}
});
_defineProperty(_this, "setupMap", function () {
if (_this.map3dComponentRef) {
if (!isEmpty(_this.state.storedState)) {
_this.map3dComponentRef.restore3dState(_this.state.storedState);
} else if (_this.props.theme.current.map3d.initialView) {
_this.map3dComponentRef.restore3dState(_this.props.theme.current.map3d.initialView);
} else {
_this.sync2DExtent();
}
}
});
_defineProperty(_this, "redrawScene", function (ev) {
if (_this.map3dComponentRef) {
_this.map3dComponentRef.redrawScene(ev);
}
});
_defineProperty(_this, "trackFocus", function (ev) {
var _mapEl$contains, _map3dEl$contains;
var mapEl = document.getElementById("map");
var map3dEl = document.getElementById("map3d");
if (mapEl !== null && mapEl !== void 0 && (_mapEl$contains = mapEl.contains) !== null && _mapEl$contains !== void 0 && _mapEl$contains.call(mapEl, document.activeElement)) {
_this.focusedMap = "map";
} else if (map3dEl !== null && map3dEl !== void 0 && (_map3dEl$contains = map3dEl.contains) !== null && _map3dEl$contains !== void 0 && _map3dEl$contains.call(map3dEl, document.activeElement)) {
_this.focusedMap = "map3d";
} else {
_this.focusedMap = null;
}
});
_this.map3dComponent = null;
_this.map3dComponentRef = null;
_this.focusedMap = null;
_this.firstPersonMarker = true;
// Subset of 2d reducers
var _ReducerIndex$reducer = ReducerIndex.reducers,
processNotifications = _ReducerIndex$reducer.processNotifications,
task = _ReducerIndex$reducer.task,
windows = _ReducerIndex$reducer.windows;
// Reducer for syncronization with parent store
var forwardReducer = function forwardReducer(key, forwardActions, syncAction) {
return function () {
var state = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var action = arguments.length > 1 ? arguments[1] : undefined;
if (forwardActions.includes(action.type)) {
// Forward to parent store
StandardApp.store.dispatch(action);
return state;
} else {
return action.type === syncAction ? action[key] : state;
}
};
};
var displayActions = Object.values(displayExports).filter(function (x) {
return typeof x === 'string';
});
var layersActions = Object.values(layersExports).filter(function (x) {
return typeof x === 'string';
});
var mapActions = Object.values(mapExports).filter(function (x) {
return typeof x === 'string';
});
var themeActions = Object.values(themeExports).filter(function (x) {
return typeof x === 'string';
});
var display = forwardReducer("display", displayActions, "SYNC_DISPLAY_FROM_PARENT_STORE");
var layers = forwardReducer("layers", layersActions, "SYNC_LAYERS_FROM_PARENT_STORE");
var map = forwardReducer("map", mapActions, "SYNC_MAP_FROM_PARENT_STORE");
var localConfig = forwardReducer("localConfig", [], "SYNC_LOCAL_CONFIG_FROM_PARENT_STORE");
var theme = forwardReducer("theme", themeActions, "SYNC_THEME_FROM_PARENT_STORE");
_this.store = createStore({
display: display,
layers: layers,
localConfig: localConfig,
map: map,
processNotifications: processNotifications,
theme: theme,
task: task,
windows: windows
});
// Set stored state
var storedState = _objectSpread({}, props.startupState.map3d);
if (props.startupParams.v3d) {
var values = props.startupParams.v3d.split(",").map(parseFloat).filter(function (x) {
return !isNaN(x);
});
if (values.length >= 6) {
var _values$;
storedState.camera = [values[0], values[1], values[2]];
storedState.target = [values[3], values[4], values[5]];
storedState.personHeight = (_values$ = values[6]) !== null && _values$ !== void 0 ? _values$ : 0;
}
}
if (props.startupParams.bl3d !== undefined) {
storedState.baseLayer = props.startupParams.bl3d;
}
_this.state.storedState = storedState;
return _this;
}
_inherits(View3D, _React$Component);
return _createClass(View3D, [{
key: "componentDidMount",
value: function componentDidMount() {
if (this.props.startupParams.v === "3d") {
this.props.setView3dMode(View3DMode.FULLSCREEN);
} else if (this.props.startupParams.v === "3d2d") {
this.props.setView3dMode(View3DMode.SPLITSCREEN);
}
window.addEventListener('focus', this.trackFocus, true);
this.syncParentStore({});
}
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
window.removeEventListener('focus', this.trackFocus);
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this,
_this$props$theme$cur;
if (this.props.view3dMode !== View3DMode.DISABLED && prevProps.view3dMode === View3DMode.DISABLED) {
import('../components/map3d/Map3D').then(function (component) {
_this2.map3dComponent = component["default"];
_this2.map3dComponentRef = null;
_this2.setState({
componentLoaded: true
});
});
this.syncParentStore(this.props, true);
} else if (this.props.view3dMode === View3DMode.DISABLING && prevProps.view3dMode !== View3DMode.DISABLING) {
if (this.map3dComponentRef) {
this.map3dComponentRef.store3dState().then(function (storedState) {
_this2.setState({
storedState: storedState
});
UrlParams.updateParams({
v3d: undefined,
bl3d: undefined
});
_this2.props.setView3dMode(View3DMode.DISABLED);
});
} else {
UrlParams.updateParams({
v3d: undefined,
bl3d: undefined
});
this.props.setView3dMode(View3DMode.DISABLED);
}
} else if (this.props.view3dMode === View3DMode.DISABLED && prevProps.view3dMode !== View3DMode.DISABLED) {
this.map3dComponent = null;
this.map3dComponentRef = null;
this.setState({
componentLoaded: false
});
if (this.firstPersonMarker) {
this.props.removeLayer("view3d-firstperson-marker");
this.firstPersonMarker = false;
}
}
// Sync parts of parent store
this.syncParentStore(prevProps);
// Handle view mode change
if (this.props.view3dMode !== prevProps.view3dMode) {
if (this.props.view3dMode === View3DMode.FULLSCREEN) {
UrlParams.updateParams({
v: "3d"
});
this.setState({
viewsLocked: false
});
} else if (this.props.view3dMode === View3DMode.SPLITSCREEN) {
UrlParams.updateParams({
v: "3d2d"
});
} else {
UrlParams.updateParams({
v: "2d"
});
}
}
// Switch to 2D mode if new theme has no 3D configuration
if (this.props.theme.current !== prevProps.theme.current && !((_this$props$theme$cur = this.props.theme.current) !== null && _this$props$theme$cur !== void 0 && _this$props$theme$cur.map3d) && this.props.view3dMode !== View3DMode.DISABLED) {
this.props.setView3dMode(View3D.DISABLED);
}
// Lock views
if (this.state.viewsLocked && this.props.map.bbox !== prevProps.map.bbox && this.focusedMap === "map") {
this.sync2DExtent();
}
// Clear stored state when switching away from a theme
if (prevProps.theme.current && this.props.theme.current !== prevProps.theme.current) {
this.setState({
storedState: null
});
}
}
}, {
key: "syncParentStore",
value: function syncParentStore(prevProps) {
var force = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
if (this.props.view3dMode === View3DMode.DISABLED) {
return;
}
if (this.props.display !== prevProps.display || force) {
this.store.dispatch({
type: "SYNC_DISPLAY_FROM_PARENT_STORE",
display: this.props.display
});
}
if (this.props.theme !== prevProps.theme || force) {
this.store.dispatch({
type: "SYNC_THEME_FROM_PARENT_STORE",
theme: this.props.theme
});
}
if (this.props.localConfig !== prevProps.localConfig || force) {
this.store.dispatch({
type: "SYNC_LOCAL_CONFIG_FROM_PARENT_STORE",
localConfig: this.props.localConfig
});
}
if (this.props.layers !== prevProps.layers || force) {
this.store.dispatch({
type: "SYNC_LAYERS_FROM_PARENT_STORE",
layers: this.props.layers
});
}
if (this.props.map !== prevProps.map || force) {
this.store.dispatch({
type: "SYNC_MAP_FROM_PARENT_STORE",
map: this.props.map
});
}
}
}, {
key: "render",
value: function render() {
var _this$props$theme$cur2;
var button = (_this$props$theme$cur2 = this.props.theme.current) !== null && _this$props$theme$cur2 !== void 0 && _this$props$theme$cur2.map3d ? /*#__PURE__*/React.createElement(View3DSwitcher, {
key: "View3DButton",
position: this.props.buttonPosition
}) : null;
return [button, this.render3DWindow()];
}
}]);
}(React.Component);
_defineProperty(View3D, "propTypes", {
addLayerFeatures: PropTypes.func,
/** The position slot index of the 3d switch map button, from the bottom (0: bottom slot). */
buttonPosition: PropTypes.number,
/** The position of the navigation controls. Either `top` or `bottom`. */
controlsPosition: PropTypes.string,
/** The default scene quality factor (`20`: min, `100`: max). */
defaultSceneQuality: PropTypes.number,
display: PropTypes.object,
/** Default window geometry. */
geometry: PropTypes.shape({
initialWidth: PropTypes.number,
initialHeight: PropTypes.number,
initialX: PropTypes.number,
initialY: PropTypes.number,
initiallyDocked: PropTypes.bool
}),
layers: PropTypes.object,
localConfig: PropTypes.object,
map: PropTypes.object,
/** Mouse buttons assignment. You can assign `pan`, `rotate`, `zoom` to each button. */
mouseButtons: PropTypes.shape({
left: PropTypes.string,
middle: PropTypes.string,
right: PropTypes.string
}),
panTo: PropTypes.func,
/** Options to pass to the 3D plugins, in the form `{"<PluginName>": {<options>}}`.
* Refer to the documentation of the <a href="#plugins3d">3D plugins</a> for settable options. */
pluginOptions: PropTypes.object,
plugins3d: PropTypes.object,
removeLayer: PropTypes.func,
searchProviders: PropTypes.object,
setView3dMode: PropTypes.func,
startupParams: PropTypes.object,
startupState: PropTypes.object,
theme: PropTypes.object,
view3dMode: PropTypes.number,
zoomToPoint: PropTypes.func
});
_defineProperty(View3D, "defaultProps", {
buttonPosition: 6,
controlsPosition: 'top',
defaultSceneQuality: 100,
geometry: {
initialWidth: 600,
initialHeight: 800,
initialX: 0,
initialY: 0,
initiallyDocked: true
},
pluginOptions: {},
mouseButtons: {
left: 'pan',
middle: 'zoom',
right: 'rotate'
}
});
export default (function (plugins3d) {
return connect(function (state) {
return {
plugins3d: plugins3d,
display: state.display,
map: state.map,
layers: state.layers,
theme: state.theme,
localConfig: state.localConfig,
view3dMode: state.display.view3dMode,
startupParams: state.localConfig.startupParams,
startupState: state.localConfig.startupState
};
}, {
addLayerFeatures: addLayerFeatures,
removeLayer: removeLayer,
panTo: panTo,
zoomToPoint: zoomToPoint,
setView3dMode: setView3dMode
})(View3D);
});