Skip to content

Commit

Permalink
Merge pull request #4013 from terrestris/further-timelayersliderpanel…
Browse files Browse the repository at this point in the history
…-updates

Further TimeLayerSliderPanel updates (loading spin)
  • Loading branch information
mholthausen authored Sep 20, 2024
2 parents baa8a03 + 52a43a1 commit 710040c
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 28 deletions.
2 changes: 1 addition & 1 deletion jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module.exports = {
transformIgnorePatterns: [
'node_modules/(?!(ol|@camptocamp/inkmap|@terrestris/*[a-z]*-util|d3-selection|color-*[a-z]*)|(rc-*[a-z]*)|' +
'filter-obj|query-string|decode-uri-component|split-on-first|shpjs/|rbush|quickselect|geostyler-openlayers-parser|' +
'geostyler-style)'
'geostyler-style|geotiff|quick-lru)'
],
setupFiles: [
'<rootDir>/jest/__mocks__/matchMediaMock.js'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ class TimeLayerSliderPanelExample extends React.Component {
initEndDate={moment()}
timeAwareLayers={this.layers}
tooltips={tooltips}
autoPlaySpeedOptions={[0.5, 1, 2, 3, 4, 5, 600]}
autoPlaySpeedOptions={[0.5, 1, 2, 3]}
dateFormat='YYYY-MM-DD HH:mm'
/>
</div>
Expand Down
34 changes: 28 additions & 6 deletions src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.less
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}

Expand Down Expand Up @@ -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;
Expand Down
115 changes: 95 additions & 20 deletions src/Panel/TimeLayerSliderPanel/TimeLayerSliderPanel.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -82,11 +89,14 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = memo(
const [autoPlayActive, setAutoPlayActive] = useState(false);
const [startDate, setStartDate] = useState<Moment>(initStartDate);
const [endDate, setEndDate] = useState<Moment>(initEndDate);
const [loadingCount, setLoadingCount] = useState(0);

const wmsTimeLayersRef = useRef<TimeLayerAwareConfig[]>([]);
const intervalRef = useRef<number | undefined>(1000);
const prevPropsRef = useRef<TimeLayerSliderPanelProps>();

const isLoading = loadingCount > 0;

const wrapTimeSlider = useCallback(() => {
const wmsTimeLayers: TimeLayerAwareConfig[] = [];
timeAwareLayers.forEach(l => {
Expand Down Expand Up @@ -134,7 +144,8 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = 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];
Expand All @@ -145,17 +156,23 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = 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);
}
});
};
Expand Down Expand Up @@ -227,6 +244,59 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = 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) {
Expand All @@ -243,10 +313,10 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = memo(

if (_isFinite(playbackSpeed)) {
wmsTimeHandler(
moment(newValue.clone().add(playbackSpeed, 'seconds').format())
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
setCurrentValue(
moment(newValue.clone().add(playbackSpeed, 'seconds').format())
moment(newValue.clone().add(playbackSpeed, 'hours').format())
);
} else {
const time = moment(
Expand Down Expand Up @@ -403,16 +473,21 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = memo(
tooltip={tooltips.setToMostRecent}
/>
) : null}
<TimeSlider
className={`${extraCls} timeslider ${futureClass}`.trim()}
formatString={dateFormat}
defaultValue={startDateString}
min={startDateString}
max={endDateString}
value={valueString}
marks={marks}
onChange={onTimeChanged}
/>
<div className="time-slider-container">
<TimeSlider
className={`${extraCls} timeslider ${futureClass}`.trim()}
formatString={dateFormat}
defaultValue={startDateString}
min={startDateString}
max={endDateString}
value={valueString}
marks={marks}
onChange={onTimeChanged}
/>
<div className="spin-indicator">
<Spin spinning={isLoading} size="small" />
</div>
</div>
<div className="time-value">
{currentValue.format(dateFormat || 'DD.MM.YYYY HH:mm:ss')}
</div>
Expand All @@ -427,7 +502,7 @@ export const TimeLayerSliderPanel: React.FC<TimeLayerSliderPanelProps> = memo(
pressedIcon={<FontAwesomeIcon icon={faPauseCircle} />}
/>
<Select
defaultValue={1}
defaultValue={'hours'}
className={extraCls + ' speed-picker'}
onChange={onPlaybackSpeedChange}
popupMatchSelectWidth={false}
Expand Down

0 comments on commit 710040c

Please sign in to comment.