diff --git a/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.less b/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.less index 07832643e..4dc2011e9 100644 --- a/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.less +++ b/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.less @@ -7,17 +7,39 @@ width: 100%; background-color: white; padding: 10px 0; + position: relative; button { margin-left: 5px; } - .timeslider { - flex: 1 1 0; - min-width: 0; + .time-slider-container { + display: flex; + align-items: center; + width: 100%; + position: relative; - &.ant-slider-with-marks { - margin: 10px 20px 20px 20px; + .timeslider { + flex: 1 1 0; + min-width: 0; + + &.ant-slider-with-marks { + margin: 10px 20px 20px 20px; + } + } + + .spin-indicator { + margin-left: 10px; + width: 24px; + height: 24px; + display: flex; + justify-content: center; + align-items: center; + + .ant-spin { + width: 16px; + height: 16px; + } } } @@ -50,7 +72,7 @@ flex: 0 0 auto; overflow: hidden; max-width: 100px; - font-size: 1.0em; + font-size: 1em; white-space: normal; overflow-wrap: break-word; display: inline-block; diff --git a/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx b/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx index d4c8cca86..cc328cc49 100644 --- a/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx +++ b/src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx @@ -1,16 +1,23 @@ import './TimeLayerSliderPanel.less'; -import { faCalendar, faPauseCircle, faPlayCircle, faSync } from '@fortawesome/free-solid-svg-icons'; +import { + faCalendar, + faPauseCircle, + faPlayCircle, + faSync +} from '@fortawesome/free-solid-svg-icons'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { WmsLayer } from '@terrestris/ol-util/dist/typeUtils/typeUtils'; import { TimeLayerAwareConfig } from '@terrestris/react-util/dist/Hooks/useTimeLayerAware/useTimeLayerAware'; -import { DatePicker, Popover, Select } from 'antd'; +import { DatePicker, Popover, Select, Spin } from 'antd'; import dayjs from 'dayjs'; import { debounce } from 'lodash'; import _isEqual from 'lodash/isEqual'; import _isFinite from 'lodash/isFinite'; import moment, { Moment } from 'moment'; import { getUid } from 'ol'; +import { TileWMS } from 'ol/source'; +import ImageWMS from 'ol/source/ImageWMS'; import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; import SimpleButton from '../../Button/SimpleButton/SimpleButton'; @@ -82,11 +89,14 @@ export const TimeLayerSliderPanel: React.FC = memo( const [autoPlayActive, setAutoPlayActive] = useState(false); const [startDate, setStartDate] = useState(initStartDate); const [endDate, setEndDate] = useState(initEndDate); + const [loadingCount, setLoadingCount] = useState(0); const wmsTimeLayersRef = useRef([]); const intervalRef = useRef(1000); const prevPropsRef = useRef(); + const isLoading = loadingCount > 0; + const wrapTimeSlider = useCallback(() => { const wmsTimeLayers: TimeLayerAwareConfig[] = []; timeAwareLayers.forEach(l => { @@ -134,7 +144,8 @@ export const TimeLayerSliderPanel: React.FC = memo( const wmsTimeHandler = (val: moment.Moment | string | [string, string]) => { wmsTimeLayersRef.current.forEach(config => { if (config.layer && config.layer.get('type') === 'WMSTime') { - const params = config.layer.getSource()?.getParams(); + const source = config.layer.getSource(); + const params = source?.getParams(); let time; if (Array.isArray(val)) { time = val[0]; @@ -145,17 +156,23 @@ export const TimeLayerSliderPanel: React.FC = memo( time = moment(time); } const timeFormat = config.layer.get('timeFormat'); + let newTimeParam: string; if ( - timeFormat.toLowerCase().indexOf('hh') > 0 && + timeFormat.toLowerCase().includes('hh') && config.layer.get('roundToFullHours') ) { time.set('minute', 0); time.set('second', 0); - params.TIME = time.toISOString(); + newTimeParam = time.toISOString(); } else { - params.TIME = time.format(timeFormat); + newTimeParam = time.format(timeFormat); + } + + if (params.TIME !== newTimeParam) { + params.TIME = newTimeParam; + source?.updateParams(params); + source?.refresh(); } - config.layer.getSource()?.updateParams(params); } }); }; @@ -227,6 +244,59 @@ export const TimeLayerSliderPanel: React.FC = memo( updateDataRange([newStartDate, newEndDate]); }, [timeAwareLayers, startDate, endDate, updateDataRange]); + useEffect(() => { + if (timeAwareLayers.length === 0) { + return; + } + + const handleTileLoadStart = () => { + setLoadingCount(prevCount => prevCount + 1); + }; + const handleTileLoadEnd = () => { + setLoadingCount(prevCount => Math.max(prevCount - 1, 0)); + }; + const handleImageLoadStart = () => { + setLoadingCount(prevCount => prevCount + 1); + }; + const handleImageLoadEnd = () => { + setLoadingCount(prevCount => Math.max(prevCount - 1, 0)); + }; + + timeAwareLayers.forEach(layer => { + if (layer.get('type') === 'WMSTime') { + const source = layer.getSource(); + + if (source instanceof TileWMS) { + source.on('tileloadstart', handleTileLoadStart); + source.on('tileloadend', handleTileLoadEnd); + source.on('tileloaderror', handleTileLoadEnd); + } else if (source instanceof ImageWMS) { + source.on('imageloadstart', handleImageLoadStart); + source.on('imageloadend', handleImageLoadEnd); + source.on('imageloaderror', handleImageLoadEnd); + } + } + }); + + return () => { + timeAwareLayers.forEach(layer => { + if (layer.get('type') === 'WMSTime') { + const source = layer.getSource(); + + if (source instanceof TileWMS) { + source.un('tileloadstart', handleTileLoadStart); + source.un('tileloadend', handleTileLoadEnd); + source.un('tileloaderror', handleTileLoadEnd); + } else if (source instanceof ImageWMS) { + source.un('imageloadstart', handleImageLoadStart); + source.un('imageloadend', handleImageLoadEnd); + source.un('imageloaderror', handleImageLoadEnd); + } + } + }); + }; + }, [timeAwareLayers]); + useEffect(() => { window.clearInterval(intervalRef.current); if (!autoPlayActive) { @@ -403,16 +473,21 @@ export const TimeLayerSliderPanel: React.FC = memo( tooltip={tooltips.setToMostRecent} /> ) : null} - +
+ +
+ +
+
{currentValue.format(dateFormat || 'DD.MM.YYYY HH:mm:ss')}