qwc2
Version:
QGIS Web Client
979 lines (976 loc) • 46.2 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 _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 _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 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); }
function _slicedToArray(r, e) { return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); }
function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure 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 _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 _iterableToArrayLimit(r, l) { var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (null != t) { var e, n, i, u, a = [], f = !0, o = !1; try { if (i = (t = t.call(r)).next, 0 === l) { if (Object(t) !== t) return; f = !1; } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); } catch (r) { o = !0, n = r; } finally { try { if (!f && null != t["return"] && (u = t["return"](), Object(u) !== u)) return; } finally { if (o) throw n; } } return a; } }
function _arrayWithHoles(r) { if (Array.isArray(r)) return r; }
/**
* 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 } from 'react-redux';
import dateParser, { Format } from 'any-date-parser';
import dayjs from 'dayjs';
import utc from 'dayjs/plugin/utc';
import isEmpty from 'lodash.isempty';
import isEqual from 'lodash.isequal';
import ol from 'openlayers';
import PropTypes from 'prop-types';
import { createSelector } from 'reselect';
import { v4 as uuidv4 } from 'uuid';
import { setLayerDimensions, addLayerFeatures, refreshLayer, removeLayer, LayerRole } from '../actions/layers';
import { setCurrentTask, setCurrentTaskBlocked } from '../actions/task';
import Icon from '../components/Icon';
import ResizeableWindow from '../components/ResizeableWindow';
import FixedTimeline from '../components/timeline/FixedTimeline';
import InfiniteTimeline from '../components/timeline/InfiniteTimeline';
import TimelineFeaturesSlider from '../components/timeline/TimelineFeaturesSlider';
import ButtonBar from '../components/widgets/ButtonBar';
import NumberInput from '../components/widgets/NumberInput';
import ToggleSwitch from '../components/widgets/ToggleSwitch';
import IdentifyUtils from '../utils/IdentifyUtils';
import LayerUtils from '../utils/LayerUtils';
import LocaleUtils from '../utils/LocaleUtils';
import VectorLayerUtils from '../utils/VectorLayerUtils';
import markerIcon from '../utils/img/marker-icon.png';
import './style/TimeManager.css';
dayjs.extend(utc);
var DateUnitLabels = {
"ms": LocaleUtils.trmsg("timemanager.unit.milliseconds"),
"s": LocaleUtils.trmsg("timemanager.unit.seconds"),
"m": LocaleUtils.trmsg("timemanager.unit.minutes"),
"h": LocaleUtils.trmsg("timemanager.unit.hours"),
"d": LocaleUtils.trmsg("timemanager.unit.days"),
"M": LocaleUtils.trmsg("timemanager.unit.months"),
"y": LocaleUtils.trmsg("timemanager.unit.years"),
"10y": LocaleUtils.trmsg("timemanager.unit.decade"),
"100y": LocaleUtils.trmsg("timemanager.unit.century")
};
var qgisDateFormat = new Format({
// $dateExpr $hour $minute $second $millisecond $zone $offset
matcher: /^(.*?)[\s,-]*([01]\d|2[0-3]):([0-5]\d)(?::([0-5]\d|60)(?:[.,](\d{9}|\d{6}|\d{1,3}))?)?[\s,-]*\(?(UTC)?[\s,-]*([+-]0\d?:?(?:[0-5]\d)?)?[\s,-]*\)?$/i,
// eslint-disable-next-line
handler: function handler(_ref) {
var _ref2 = _slicedToArray(_ref, 8),
match = _ref2[0],
dateExpr = _ref2[1],
hour = _ref2[2],
minute = _ref2[3],
second = _ref2[4],
millisecond = _ref2[5],
zone = _ref2[6],
offset = _ref2[7];
var result = {};
if (dateExpr) {
result = this.parser.attempt(dateExpr);
if (result.invalid) {
return result;
}
}
result.hour = hour;
result.minute = minute;
if (second) {
result.second = second;
}
if (millisecond && millisecond.length > 3) {
result.millisecond = millisecond.slice(0, 3);
} else if (millisecond) {
result.millisecond = millisecond;
}
if (offset) {
result.offset = offset;
}
return result;
}
});
dateParser.addFormat(qgisDateFormat);
// QGIS server does not return any feature that does not have "enddate" set.
// To workaround this limitation, a placeholder date is used to make features
// with no "enddate" visible. This variable represents that placeholder date.
// This information is needed in the QWC2 so that features with no "enddate"
// are represented correctly. It is also used to differentiate them from features with
// a valid "enddate".
var DUMMY_END_DATE = new Date('9999-01-01 00:00:00');
/**
* Allows controling the time dimension of temporal WMS layers.
*/
var TimeManager = /*#__PURE__*/function (_React$Component) {
function TimeManager(props) {
var _this;
_classCallCheck(this, TimeManager);
_this = _callSuper(this, TimeManager, [props]);
_defineProperty(_this, "renderBody", function () {
var timeButtons = [{
key: "rewind",
tooltip: LocaleUtils.tr("timemanager.rewind"),
icon: "nav-start"
}, {
key: "now",
tooltip: LocaleUtils.tr("timemanager.now"),
icon: "today"
}, {
key: "prev",
tooltip: LocaleUtils.tr("timemanager.stepback"),
icon: "nav-left"
}, {
key: "playrev",
tooltip: LocaleUtils.tr("timemanager.playrev"),
icon: "triangle-left",
disabled: _this.state.animationActive
}, {
key: "stop",
tooltip: LocaleUtils.tr("timemanager.stop"),
icon: "square",
disabled: !_this.state.animationActive
}, {
key: "play",
tooltip: LocaleUtils.tr("timemanager.play"),
icon: "triangle-right",
disabled: _this.state.animationActive
}, {
key: "next",
tooltip: LocaleUtils.tr("timemanager.stepfwd"),
icon: "nav-right"
}, {
key: "loop",
tooltip: LocaleUtils.tr("timemanager.loop"),
icon: "refresh",
pressed: _this.state.animationLoop
}];
var markerConfiguration = _objectSpread(_objectSpread({}, TimeManager.defaultProps.markerConfiguration), _this.props.markerConfiguration);
var options = /*#__PURE__*/React.createElement("div", {
className: "time-manager-options"
}, /*#__PURE__*/React.createElement("table", null, /*#__PURE__*/React.createElement("tbody", null, /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, LocaleUtils.tr("timemanager.stepsize"), ":"), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement(NumberInput, {
max: 100,
min: 1,
mobile: true,
onChange: function onChange(value) {
return _this.setState({
stepSize: value
});
},
value: _this.state.stepSize
})), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement("select", {
onChange: function onChange(ev) {
return _this.setState({
stepSizeUnit: ev.target.value
});
},
value: _this.state.stepSizeUnit
}, _this.props.stepUnits.map(function (unit) {
return /*#__PURE__*/React.createElement("option", {
key: unit,
value: unit
}, LocaleUtils.tr(DateUnitLabels[unit]));
})))), /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, LocaleUtils.tr("timemanager.animationinterval"), ":"), /*#__PURE__*/React.createElement("td", null, /*#__PURE__*/React.createElement(NumberInput, {
max: 10,
min: 1,
mobile: true,
onChange: function onChange(value) {
return _this.setState({
animationInterval: value
});
},
value: _this.state.animationInterval
})), /*#__PURE__*/React.createElement("td", null, "\xA0", LocaleUtils.tr("timemanager.unit.seconds"))), /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, LocaleUtils.tr("timemanager.timeline"), ":"), /*#__PURE__*/React.createElement("td", {
colSpan: "2"
}, /*#__PURE__*/React.createElement("select", {
onChange: function onChange(ev) {
return _this.setState({
timelineMode: ev.target.value
});
},
value: _this.state.timelineMode
}, /*#__PURE__*/React.createElement("option", {
value: "fixed"
}, LocaleUtils.tr("timemanager.timeline_fixed")), /*#__PURE__*/React.createElement("option", {
value: "infinite"
}, LocaleUtils.tr("timemanager.timeline_infinite"))))), _this.state.timelineDisplay !== "hidden" ? /*#__PURE__*/React.createElement("tr", null, /*#__PURE__*/React.createElement("td", null, LocaleUtils.tr("timemanager.timelinedisplay"), ":"), /*#__PURE__*/React.createElement("td", {
colSpan: "2"
}, /*#__PURE__*/React.createElement("select", {
onChange: function onChange(ev) {
return _this.setState({
timelineDisplay: ev.target.value
});
},
value: _this.state.timelineDisplay
}, /*#__PURE__*/React.createElement("option", {
value: "minimal"
}, LocaleUtils.tr("timemanager.displayminimal")), /*#__PURE__*/React.createElement("option", {
value: "features"
}, LocaleUtils.tr("timemanager.displayfeatures")), /*#__PURE__*/React.createElement("option", {
value: "layers"
}, LocaleUtils.tr("timemanager.displaylayers"))))) : null)));
var timeSpan = _this.state.endTime !== null ? _this.state.endTime.diff(_this.state.startTime) : dayjs().diff(_this.state.startTime);
var Timeline = _this.state.timelineMode === 'infinite' ? InfiniteTimeline : FixedTimeline;
var filterActive = !isEmpty(_this.props.filter.filterParams) || !!_this.props.filter.filterGeom;
var startdate = _this.state.timeData.values.length > 0 ? _this.state.timeData.values[0].hour(0).minute(0).second(0) : null;
var enddate = _this.state.timeData.values.length > 0 ? _this.state.timeData.values[_this.state.timeData.values.length - 1].hour(23).minute(59).second(59) : null;
if (startdate && _this.props.filter.timeRange && dayjs(_this.props.filter.timeRange.tstart) > startdate) {
startdate = dayjs(_this.props.filter.timeRange.tstart);
}
if (enddate && _this.props.filter.timeRange && dayjs(_this.props.filter.timeRange.tend) < enddate) {
enddate = dayjs(_this.props.filter.timeRange.tend);
}
return /*#__PURE__*/React.createElement("div", {
className: "time-manager-body"
}, /*#__PURE__*/React.createElement("div", {
className: "time-manager-toolbar"
}, /*#__PURE__*/React.createElement("div", {
className: "time-manager-toolbar-controls"
}, /*#__PURE__*/React.createElement("span", {
className: "time-manager-toolbar-block"
}, /*#__PURE__*/React.createElement("span", null, LocaleUtils.tr("timemanager.toggle")), /*#__PURE__*/React.createElement(ToggleSwitch, {
active: _this.state.timeEnabled,
onChange: _this.toggleTimeEnabled
})), /*#__PURE__*/React.createElement(ButtonBar, {
buttons: timeButtons,
disabled: !_this.state.timeEnabled,
onClick: _this.animationButtonClicked
}), _this.props.markerConfiguration.markersAvailable ? /*#__PURE__*/React.createElement("span", {
className: "time-manager-toolbar-block"
}, /*#__PURE__*/React.createElement("span", null, LocaleUtils.tr("timemanager.markers"), ": \xA0"), /*#__PURE__*/React.createElement(ToggleSwitch, {
active: _this.state.markersEnabled,
onChange: function onChange(value) {
return _this.setState({
markersEnabled: value
});
},
readOnly: !_this.state.markersCanBeEnabled
})) : null), /*#__PURE__*/React.createElement("div", {
className: "time-manager-options-menubutton"
}, /*#__PURE__*/React.createElement("button", {
className: "button" + (_this.state.settingsPopup ? " pressed" : ""),
onClick: function onClick() {
return _this.setState(function (state) {
return {
settingsPopup: !state.settingsPopup
};
});
}
}, /*#__PURE__*/React.createElement(Icon, {
icon: "cog"
})), _this.state.settingsPopup ? options : null)), filterActive ? /*#__PURE__*/React.createElement("div", {
className: "time-manager-filter-warning"
}, /*#__PURE__*/React.createElement(Icon, {
icon: "warning"
}), " ", LocaleUtils.tr("timemanager.filterwarning"), " ", /*#__PURE__*/React.createElement("button", {
className: "button",
onClick: function onClick() {
return _this.props.setCurrentTask("MapFilter");
},
type: "button"
}, LocaleUtils.tr("timemanager.edit"))) : null, /*#__PURE__*/React.createElement("div", {
className: "time-manager-timeline"
}, /*#__PURE__*/React.createElement(Timeline, {
currentTimestamp: _this.state.currentTimestamp,
dataEndTime: enddate,
dataStartTime: startdate,
dateFormat: _this.props.dateFormat,
dialogWidth: _this.state.dialogWidth,
endTime: _this.state.endTime,
setEndTime: _this.setEndTime,
setMarkersCanBeEnabled: function setMarkersCanBeEnabled(value) {
return _this.setState({
markersCanBeEnabled: value,
markersEnabled: false
});
},
setStartTime: _this.setStartTime,
startTime: _this.state.startTime,
timeSpan: timeSpan
}, function (computePixelFromTime, computeTimeFromPixel) {
return /*#__PURE__*/React.createElement(TimelineFeaturesSlider, {
computePixelFromTime: computePixelFromTime,
computeTimeFromPixel: computeTimeFromPixel,
currentTimestamp: _this.state.currentTimestamp,
cursorFormat: _this.props.cursorFormat,
dateFormat: _this.props.dateFormat,
displayMode: _this.state.timelineDisplay,
endTime: _this.state.endTime,
markerConfiguration: markerConfiguration,
markersEnabled: _this.state.markersEnabled,
startTime: _this.state.startTime,
stepSizeUnit: _this.state.stepSizeUnit,
timeEnabled: _this.state.timeEnabled,
timeFeatures: _this.state.timeFeatures,
timestampChanged: function timestampChanged(timestamp) {
return _this.setState({
currentTimestamp: timestamp
});
}
});
})));
});
_defineProperty(_this, "dialogGeomChanged", function (geom) {
_this.setState({
dialogWidth: geom.docked ? document.body.offsetWidth : geom.width
});
});
_defineProperty(_this, "toggleTimeEnabled", function (enabled) {
clearInterval(_this.animationTimer);
clearTimeout(_this.updateMapMarkersTimeout);
_this.animationTimer = null;
_this.updateMapMarkersTimeout = null;
_this.setState(function (state) {
return {
timeEnabled: enabled,
currentTimestamp: +state.startTime,
animationActive: false,
timeMarkers: null
};
});
});
_defineProperty(_this, "animationButtonClicked", function (action) {
_this.stopAnimation();
if (action === "rewind") {
_this.setState(function (state) {
return {
currentTimestamp: +state.startTime,
animationActive: false
};
});
} else if (action === "now") {
_this.setState({
currentTimestamp: +dayjs(),
animationActive: false
});
} else if (action === "prev") {
var newday = _this.step(-1);
_this.setState(function (state) {
return {
currentTimestamp: +Math.max(newday, state.startTime)
};
});
} else if (action === "next") {
var _newday = _this.step(+1);
_this.setState(function (state) {
return {
currentTimestamp: +Math.min(_newday, state.endTime)
};
});
} else if (action === "stop") {
/* Already stopped above, pass */
} else if (action === "play") {
var curday = dayjs(_this.state.currentTimestamp);
var lastday = _this.state.endTime;
if (curday >= lastday) {
_this.setState(function (state) {
return {
currentTimestamp: +state.startTime
};
});
}
_this.animationTimer = setInterval(function () {
_this.advanceAnimation(+1);
}, 1000 * _this.state.animationInterval);
_this.setState({
animationActive: true
});
} else if (action === "playrev") {
var _curday = dayjs(_this.state.currentTimestamp);
var firstday = _this.state.startTime;
if (_curday <= firstday) {
_this.setState(function (state) {
return {
currentTimestamp: +state.endTime
};
});
}
_this.animationTimer = setInterval(function () {
_this.advanceAnimation(-1);
}, 1000 * _this.state.animationInterval);
_this.setState({
animationActive: true
});
} else if (action === "loop") {
_this.setState(function (state) {
return {
animationLoop: !state.animationLoop
};
});
}
});
_defineProperty(_this, "advanceAnimation", function (stepdir) {
var newday = _this.step(stepdir);
var firstday = _this.state.startTime;
var lastday = _this.state.endTime;
if (newday > lastday) {
if (stepdir > 0 && _this.state.animationLoop) {
_this.setState(function (state) {
return {
currentTimestamp: +state.startTime
};
});
} else {
_this.setState({
currentTimestamp: +lastday,
animationActive: false
});
clearInterval(_this.animationTimer);
_this.animationTimer = null;
}
} else if (newday < firstday) {
if (stepdir < 0 && _this.state.animationLoop) {
_this.setState(function (state) {
return {
currentTimestamp: +state.endTime
};
});
} else {
_this.setState({
currentTimestamp: +firstday,
animationActive: false
});
clearInterval(_this.animationTimer);
_this.animationTimer = null;
}
} else {
_this.setState({
currentTimestamp: +newday
});
}
});
_defineProperty(_this, "stopAnimation", function () {
if (_this.state.animationActive) {
clearInterval(_this.animationTimer);
_this.animationTimer = null;
_this.setState({
animationActive: false
});
}
});
_defineProperty(_this, "onClose", function () {
_this.toggleTimeEnabled(false);
_this.setState({
visible: false
});
_this.props.removeLayer("timemarkers");
});
_defineProperty(_this, "step", function (direction) {
var day = dayjs(_this.state.currentTimestamp);
var num = parseInt(_this.state.stepSizeUnit.slice(0, -1), 10) || 1;
var newday = day.add(direction * _this.state.stepSize * num, _this.state.stepSizeUnit.slice(-1));
if (_this.state.stepSizeUnit.endsWith("m")) {
return newday.second(0);
} else if (_this.state.stepSizeUnit.endsWith("h")) {
return newday.second(0).minute(0);
} else if (_this.state.stepSizeUnit.endsWith("d")) {
return newday.second(0).minute(0).hour(0);
} else if (_this.state.stepSizeUnit.endsWith("M")) {
return newday.second(0).minute(0).hour(0).date(1);
} else if (_this.state.stepSizeUnit.endsWith("y")) {
return newday.second(0).minute(0).hour(0).date(1).month(0);
}
return newday;
});
_defineProperty(_this, "updateLayerTimeDimensions", function (timeData, currentTimestamp) {
var currentTime = _this.state.timeEnabled ? new Date(currentTimestamp).toISOString() : undefined;
timeData.layers.forEach(function (layer) {
var dimensions = timeData.layerDimensions[layer.id].reduce(function (res, dimension) {
res[dimension.toUpperCase()] = currentTime;
return res;
}, _objectSpread({}, layer.dimensionValues || {}));
_this.props.setLayerDimensions(layer.id, dimensions);
});
});
_defineProperty(_this, "setStartTime", function (value) {
var date = (value ? dayjs.utc(value) : _this.state.timeData.values[0]).hour(0).minute(0).second(0);
if (_this.props.filter.timeRange && _this.props.filter.timeRange.tstart > date) {
date = _this.props.filter.timeRange.tstart;
}
if (date < _this.state.endTime) {
_this.setState({
startTime: date
});
}
if (dayjs(_this.state.currentTimestamp) < date) {
_this.setState({
currentTimestamp: +date
});
}
});
_defineProperty(_this, "setEndTime", function (value) {
var date = (value ? dayjs.utc(value) : _this.state.timeData.values[_this.state.timeData.values.length - 1]).hour(23).minute(59).second(59);
if (_this.props.filter.timeRange && _this.props.filter.timeRange.tend < date) {
date = _this.props.filter.timeRange.tstart;
}
if (date > _this.state.startTime) {
_this.setState({
endTime: date
});
if (dayjs(_this.state.currentTimestamp) > date) {
_this.setState({
currentTimestamp: +date
});
}
}
});
_defineProperty(_this, "updateTimeFeatures", function (timeData) {
// Query all features in extent
var xmin = _this.props.map.bbox.bounds[0];
var ymin = _this.props.map.bbox.bounds[1];
var xmax = _this.props.map.bbox.bounds[2];
var ymax = _this.props.map.bbox.bounds[3];
var filterGeom = VectorLayerUtils.geoJSONGeomToWkt({
type: 'Polygon',
coordinates: [[[xmin, ymin], [xmax, ymin], [xmax, ymax], [xmin, ymax], [xmin, ymin]]]
});
var pending = 0;
var reqUUID = uuidv4();
timeData.layers.forEach(function (layer) {
var sublayerattrs = timeData.attributes[layer.id];
var queryLayers = Object.keys(sublayerattrs).join(",");
var options = {
GEOMCENTROID: true,
with_htmlcontent: false,
feature_count: _this.state.featureCount
};
var request = IdentifyUtils.buildFilterRequest(layer, queryLayers, filterGeom, _this.props.map, options);
IdentifyUtils.sendRequest(request, function (response) {
if (_this.state.timeFeatures && _this.state.timeFeatures.reqUUID === reqUUID && response) {
var layerFeatures = IdentifyUtils.parseXmlResponse(response, _this.props.map.projection, layer);
_this.setState(function (state) {
return {
timeFeatures: {
features: _objectSpread(_objectSpread({}, state.timeFeatures.features), Object.entries(layerFeatures).reduce(function (res, _ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
layername = _ref4[0],
features = _ref4[1];
return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, layername, features.map(function (feature) {
var startdate = dateParser.fromString(feature.properties[sublayerattrs[feature.layername][0]]);
var enddate = dateParser.fromString(feature.properties[sublayerattrs[feature.layername][1]]);
if (enddate && !enddate.invalid && enddate.getFullYear() === DUMMY_END_DATE.getFullYear()) {
enddate = null;
}
return _objectSpread(_objectSpread({}, feature), {}, {
id: feature.layername + "::" + feature.id,
properties: _objectSpread(_objectSpread({}, feature.properties), {}, {
__startdate: dayjs.utc(startdate),
__enddate: dayjs.utc(enddate)
})
});
})));
}, {})),
attributes: _objectSpread(_objectSpread({}, state.timeFeatures.attributes), Object.entries(layerFeatures).reduce(function (res, _ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
layername = _ref6[0],
features = _ref6[1];
return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, layername, Object.keys((features[0] || {
properties: {}
}).properties)));
}, {})),
pendingRequests: state.timeFeatures.pendingRequests - 1
}
};
});
} else {
_this.setState(function (state) {
return {
timeFeatures: _objectSpread(_objectSpread({}, state.timeFeatures), {}, {
pendingRequests: state.timeFeatures.pendingRequests - 1
})
};
});
}
});
++pending;
});
_this.setState({
timeFeatures: {
features: {},
attributes: {},
pendingRequests: pending,
reqUUID: reqUUID
}
});
});
_defineProperty(_this, "markerStyle", function (feature) {
var style = [];
var currentTime = dayjs(_this.state.currentTimestamp);
var featprops = feature.getProperties();
if (_this.state.timeEnabled && (featprops.__startdate > currentTime || featprops.__enddate < currentTime)) {
return style;
}
var offset = _this.props.markerConfiguration.markerOffset;
if (_this.props.markerConfiguration.markerPins) {
style.push(new ol.style.Style({
image: new ol.style.Icon({
anchor: [0.5, 1],
anchorXUnits: 'fraction',
anchorYUnits: 'fraction',
displacement: offset,
src: markerIcon
})
}));
}
if (featprops.__startdate.isValid() && featprops.__enddate.isValid()) {
var deltaT = _this.state.endTime.diff(_this.state.startTime);
var markerStartTime = dayjs(Math.max(_this.state.startTime, featprops.__startdate));
var markerEndTime = dayjs(Math.min(_this.state.endTime, featprops.__enddate));
var markerMidTime = 0.5 * (markerStartTime + markerEndTime);
var gradBarMaxWidth = 192;
var gradBarHeight = 16;
var gradBarWidth = gradBarMaxWidth * markerEndTime.diff(markerStartTime) / deltaT;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var gradient = context.createLinearGradient(-gradBarWidth * (markerMidTime - _this.state.startTime) / (markerMidTime - markerStartTime), 0, gradBarWidth * (_this.state.endTime - markerMidTime) / (markerEndTime - markerMidTime), 0);
var nStops = _this.props.markerConfiguration.gradient.length;
_this.props.markerConfiguration.gradient.forEach(function (stop, idx) {
gradient.addColorStop(idx / (nStops - 1), stop);
});
style.push(new ol.style.Style({
image: new ol.style.RegularShape({
fill: new ol.style.Fill({
color: gradient
}),
stroke: new ol.style.Stroke({
color: 'black',
width: 1
}),
points: 4,
radius: gradBarWidth / Math.SQRT2,
radius2: gradBarWidth,
angle: 0,
scale: [1, 1 / gradBarWidth * gradBarHeight],
displacement: [offset[0], offset[1] * gradBarHeight / gradBarWidth - gradBarHeight]
})
}));
}
return style;
});
_this.animationTimer = null;
_this.updateMapMarkersTimeout = null;
TimeManager.defaultState.stepSize = props.defaultStepSize;
TimeManager.defaultState.stepSizeUnit = props.defaultStepUnit;
TimeManager.defaultState.timelineDisplay = props.defaultTimelineDisplay;
TimeManager.defaultState.timeEnabled = props.defaultEnabled;
if (!props.stepUnits.includes(TimeManager.defaultState.stepSizeUnit)) {
TimeManager.defaultState.stepSizeUnit = props.stepUnits[0];
}
TimeManager.defaultState.animationInterval = props.defaultAnimationInterval;
TimeManager.defaultState.featureCount = props.defaultFeatureCount;
TimeManager.defaultState.timelineMode = props.defaultTimelineMode;
TimeManager.defaultState.timelineDisplay = props.defaultTimelineDisplay;
_this.state = _objectSpread(_objectSpread({}, _this.state), TimeManager.defaultState);
return _this;
}
_inherits(TimeManager, _React$Component);
return _createClass(TimeManager, [{
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps, prevState) {
var _this2 = this;
var activated = !prevProps.active && this.props.active;
if (activated) {
this.setState({
visible: true
});
// Clear task immediately after showing, visibility is controlled by internal state
this.props.setCurrentTask(null);
}
if (!this.state.visible && prevState.visible) {
this.updateLayerTimeDimensions(this.state.timeData, this.state.currentTimestamp);
this.setState(TimeManager.defaultState);
return;
}
if (!activated && !this.state.visible) {
return;
}
if (this.props.theme !== prevProps.theme) {
this.setState({
currentTimestamp: null
});
}
if (activated || !isEqual(this.props.layerVisibilities, prevProps.layerVisibilities)) {
this.stopAnimation();
var timeData = {
layerDimensions: {},
values: new Set(),
attributes: {},
layers: []
};
this.props.layers.forEach(function (layer) {
if (layer.type === "wms") {
var layertimeData = LayerUtils.getTimeDimensionValues(layer);
if (layertimeData.names.size > 0) {
timeData.layerDimensions[layer.id] = _toConsumableArray(layertimeData.names);
layertimeData.values.forEach(function (x) {
return timeData.values.add(x);
});
timeData.attributes[layer.id] = _objectSpread(_objectSpread({}, timeData.attributes[layer.id]), layertimeData.attributes);
// Filter time dimension from layer - object cache in updateTimeFeatures below should query all objects regardless of time
var layerNoTimeDims = _objectSpread({}, layer);
var layerDimsUC = timeData.layerDimensions[layer.id].map(function (name) {
return name.toUpperCase();
});
layerNoTimeDims.dimensionValues = Object.entries(layerNoTimeDims.dimensionValues || {}).reduce(function (res, _ref7) {
var _ref8 = _slicedToArray(_ref7, 2),
key = _ref8[0],
value = _ref8[1];
if (layerDimsUC.includes(key)) {
return res;
} else {
return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, key, value));
}
}, {});
timeData.layers.push(layerNoTimeDims);
}
}
});
timeData.values = _toConsumableArray(timeData.values).sort().map(function (d) {
return dayjs.utc(d);
});
var startdate = timeData.values.length > 0 ? timeData.values[0].hour(0).minute(0).second(0) : null;
var enddate = timeData.values.length > 0 ? timeData.values[timeData.values.length - 1].hour(23).minute(59).second(59) : null;
if (startdate && this.props.filter.timeRange && dayjs(this.props.filter.timeRange.tstart) > startdate) {
startdate = dayjs(this.props.filter.timeRange.tstart);
}
if (enddate && this.props.filter.timeRange && dayjs(this.props.filter.timeRange.tend) < enddate) {
enddate = dayjs(this.props.filter.timeRange.tend);
}
this.setState(function (state) {
var _state$currentTimesta;
return {
timeData: timeData,
currentTimestamp: (_state$currentTimesta = state.currentTimestamp) !== null && _state$currentTimesta !== void 0 ? _state$currentTimesta : timeData.values.length > 0 ? +timeData.values[0] : null,
startTime: startdate,
endTime: enddate && enddate.year() !== DUMMY_END_DATE.getFullYear() ? enddate : null
};
});
this.updateTimeFeatures(timeData);
} else {
if (this.state.currentTimestamp !== prevState.currentTimestamp || this.state.timeEnabled !== prevState.timeEnabled) {
this.updateLayerTimeDimensions(this.state.timeData, this.state.currentTimestamp);
}
if (this.state.visible && this.props.map.bbox !== prevProps.map.bbox) {
this.updateTimeFeatures(this.state.timeData);
}
}
if (this.props.filter.timeRange !== prevProps.filter.timeRange) {
this.setState(function (state) {
var startdate = state.timeData.values.length > 0 ? state.timeData.values[0].hour(0).minute(0).second(0) : null;
var enddate = state.timeData.values.length > 0 ? state.timeData.values[state.timeData.values.length - 1].hour(23).minute(59).second(59) : null;
if (startdate && _this2.props.filter.timeRange && dayjs(_this2.props.filter.timeRange.tstart) > startdate) {
startdate = dayjs(_this2.props.filter.timeRange.tstart);
}
if (enddate && _this2.props.filter.timeRange && dayjs(_this2.props.filter.timeRange.tend) < enddate) {
enddate = dayjs(_this2.props.filter.timeRange.tend);
}
return {
startTime: startdate,
endTime: enddate && enddate.year() !== DUMMY_END_DATE.getFullYear() ? enddate : null
};
});
}
if (this.state.animationActive && this.state.animationInterval !== prevState.animationInterval) {
this.stopAnimation();
}
if (!this.state.markersEnabled && prevState.markersEnabled) {
this.props.removeLayer("timemarkers");
} else if (this.state.markersEnabled && this.state.timeFeatures) {
if (this.state.markersEnabled !== prevState.markersEnabled || this.state.timeFeatures !== prevState.timeFeatures) {
var layer = {
id: "timemarkers",
role: LayerRole.MARKER,
styleFunction: this.markerStyle,
rev: +new Date()
};
var features = Object.values(this.state.timeFeatures.features).flat();
this.props.addLayerFeatures(layer, features, true);
} else if (this.state.currentTimestamp !== prevState.currentTimestamp || this.state.timeEnabled !== prevState.timeEnabled) {
this.props.refreshLayer(function (layer) {
return layer.id === "timemarkers";
});
}
}
}
}, {
key: "render",
value: function render() {
if (!this.state.visible) {
return null;
}
var timeValues = this.state.timeData.values;
var body = null;
if (timeValues.length < 2) {
body = /*#__PURE__*/React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "time-manager-notemporaldata"
}, LocaleUtils.tr("timemanager.notemporaldata")));
} else {
body = this.renderBody(timeValues);
}
return /*#__PURE__*/React.createElement(ResizeableWindow, {
dockable: "bottom",
icon: "clock",
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,
onClose: this.onClose,
onGeometryChanged: this.dialogGeomChanged,
scrollable: true,
splitScreenWhenDocked: true,
title: LocaleUtils.tr("timemanager.title")
}, body);
}
}]);
}(React.Component);
_defineProperty(TimeManager, "propTypes", {
active: PropTypes.bool,
addLayerFeatures: PropTypes.func,
/** The format of the time cursor label. Either `date`, `time` or `datetime`. */
cursorFormat: PropTypes.string,
/** The date format in the time controls, i.e. YYYY-MM-DD. */
dateFormat: PropTypes.string,
/** The default interval for the temporal animation, in seconds. */
defaultAnimationInterval: PropTypes.number,
/** Default for TimeManager enabled when loading application. `true` or `false` */
defaultEnabled: PropTypes.bool,
/** The default number of features that will be requested. */
defaultFeatureCount: PropTypes.number,
/** The default step size for the temporal animation, in step units. */
defaultStepSize: PropTypes.number,
/** The default step unit for the temporal animation, one of `ms`, `s`, `m`, `d`, `M`, `y`, `10y`, `100y` */
defaultStepUnit: PropTypes.string,
/** The default timeline display mode. One of `hidden`, `minimal`, `features`, `layers`. */
defaultTimelineDisplay: PropTypes.string,
/** The default timeline mode. One of `fixed`, `infinite`. */
defaultTimelineMode: PropTypes.string,
filter: PropTypes.object,
/** Default window geometry with size, position and docking status. Positive position values (including '0') are related to top (InitialY) and left (InitialX), negative values (including '-0') to bottom (InitialY) and right (InitialX). */
geometry: PropTypes.shape({
initialWidth: PropTypes.number,
initialHeight: PropTypes.number,
initialX: PropTypes.number,
initialY: PropTypes.number,
initiallyDocked: PropTypes.bool
}),
layerVisibilities: PropTypes.object,
layers: PropTypes.array,
map: PropTypes.object,
/** The feature marker configuration. */
markerConfiguration: PropTypes.shape({
markersAvailable: PropTypes.bool,
gradient: PropTypes.arrayOf(PropTypes.string),
markerOffset: PropTypes.array,
markerPins: PropTypes.bool
}),
refreshLayer: PropTypes.func,
removeLayer: PropTypes.func,
setCurrentTask: PropTypes.func,
setLayerDimensions: PropTypes.func,
/** The available temporal animation step units. */
stepUnits: PropTypes.arrayOf(PropTypes.string),
theme: PropTypes.object
});
_defineProperty(TimeManager, "defaultProps", {
cursorFormat: "datetime",
dateFormat: "YYYY-MM-DD[\n]HH:mm:ss",
defaultAnimationInterval: 1,
defaultEnabled: false,
defaultStepSize: 1,
defaultStepUnit: "d",
defaultFeatureCount: 100,
defaultTimelineMode: "fixed",
markerConfiguration: {
markersAvailable: true,
gradient: ["#f7af7d", "#eacc6e", "#fef89a", "#c5e09b", "#a3d29c", "#7cc096", "#79c8c5", "#34afce"],
markerOffset: [0, 0],
markerPins: true
},
featureTimelineAvailable: true,
stepUnits: ["s", "m", "h", "d", "M", "y"],
geometry: {
initialWidth: 800,
initialHeight: 320,
initiallyDocked: true
}
});
_defineProperty(TimeManager, "defaultState", {
timeEnabled: false,
startTime: null,
endTime: null,
currentTimestamp: null,
animationActive: false,
animationLoop: false,
animationInterval: 1,
stepSize: 1,
stepSizeUnit: 'd',
// 1 day
dialogWidth: 0,
markersEnabled: false,
markersCanBeEnabled: true,
timelineDisplay: 'layers',
featureCount: 100,
timelineMode: 'continuous',
timeData: {
layerDimensions: {},
values: [],
attributes: {},
layers: []
},
timeFeatures: null,
settingsPopup: false,
visible: false,
geometry: {
initialWidth: 900,
initialHeight: 320,
initialX: null,
initialY: null,
initiallyDocked: false
}
});
var layerVisiblitiesSelector = createSelector([function (state) {
return state.layers.flat;
}], function (layers) {
return layers.filter(function (layer) {
return layer.type === "wms";
}).reduce(function (res, layer) {
return _objectSpread(_objectSpread({}, res), {}, _defineProperty({}, layer.id, LayerUtils.computeLayerVisibility(layer)));
}, {});
});
var selector = createSelector([function (state) {
return state;
}, layerVisiblitiesSelector], function (state, layerVisibilities) {
return {
active: state.task.id === "TimeManager",
layers: state.layers.flat,
filter: state.layers.filter,
layerVisibilities: layerVisibilities,
map: state.map,
theme: state.theme.current
};
});
export default connect(selector, {
addLayerFeatures: addLayerFeatures,
refreshLayer: refreshLayer,
removeLayer: removeLayer,
setLayerDimensions: setLayerDimensions,
setCurrentTask: setCurrentTask,
setCurrentTaskBlocked: setCurrentTaskBlocked
})(TimeManager);