Skip to content

Commit eb11252

Browse files
authored
Support for local values in animation (#3339)
1 parent 7aaa7e0 commit eb11252

File tree

3 files changed

+60
-18
lines changed

3 files changed

+60
-18
lines changed

web/client/epics/playback.js

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,15 @@ const {
1919
selectLayer,
2020
onRangeChanged
2121
} = require('../actions/timeline');
22-
const { currentTimeSelector, layersWithTimeDataSelector } = require('../selectors/dimension');
22+
23+
const { error } = require('../actions/notifications');
24+
25+
const { currentTimeSelector, layersWithTimeDataSelector, layerTimeSequenceSelectorCreator } = require('../selectors/dimension');
2326

2427
const { LOCATION_CHANGE } = require('react-router-redux');
2528

2629
const { currentFrameSelector, currentFrameValueSelector, lastFrameSelector, playbackRangeSelector, playbackSettingsSelector, frameDurationSelector, statusSelector } = require('../selectors/playback');
27-
const { selectedLayerName, selectedLayerUrl, rangeSelector } = require('../selectors/timeline');
30+
const { selectedLayerName, selectedLayerUrl, selectedLayerData, selectedLayerTimeDimensionConfiguration, rangeSelector } = require('../selectors/timeline');
2831

2932
const pausable = require('../observables/pausable');
3033
const { wrapStartStop } = require('../observables/epics');
@@ -73,8 +76,36 @@ const createAnimationValues = (getState, { fromValue } = {}) => {
7376
return Rx.Observable.of(values);
7477
};
7578

79+
/**
80+
* Gets the static list of times to animate
81+
*/
82+
const filterAnimationValues = (values, getState, {fromValue} = {}) => {
83+
const playbackRange = playbackRangeSelector(getState()) || {};
84+
const startPlaybackTime = playbackRange.startPlaybackTime;
85+
const endPlaybackTime = playbackRange.endPlaybackTime;
86+
return Rx.Observable.of(values
87+
// remove times before out of playback range
88+
.filter(v => startPlaybackTime && endPlaybackTime ? moment(v).isSameOrAfter(startPlaybackTime) && moment(v).isSameOrBefore(endPlaybackTime) : true)
89+
// Remove values before fromValue
90+
.filter(v => fromValue ? moment(v).isAfter(fromValue) : true)
91+
// limit size to BUFFER_SIZE
92+
.slice(0, BUFFER_SIZE));
93+
};
94+
95+
/**
96+
* Returns an observable that emit an array of time frames, based of the current configuration:
97+
* - If configured as fixed steps, it returns the list of next animation frame calculating them
98+
* - If there is a selected layer and there is the Multidim extension, then use it (in favour of static values configured)
99+
* - If there are values in the dimension configuration, and the Multidim extension is not present, use them to animate
100+
*/
76101
const getAnimationFrames = (getState, { fromValue } = {}) => {
77102
if (selectedLayerName(getState())) {
103+
const values = layerTimeSequenceSelectorCreator(selectedLayerData(getState()))(getState());
104+
const timeDimConfig = selectedLayerTimeDimensionConfiguration(getState());
105+
// check if multidim extension is available. It has priority to local values
106+
if (timeDimConfig && !timeDimConfig.source && !(timeDimConfig.source.type === "multidim-extension") && values && values.length > 0) {
107+
return filterAnimationValues(values, getState, {fromValue});
108+
}
78109
return getDomainValues(...domainArgs(getState, {
79110
fromValue: fromValue
80111
}))
@@ -98,7 +129,13 @@ module.exports = {
98129
action$.ofType(PLAY).exhaustMap(() =>
99130
getAnimationFrames(getState)
100131
.map((frames) => setFrames(frames))
101-
.let(wrapStartStop(framesLoading(true), framesLoading(false)))
132+
.let(wrapStartStop(framesLoading(true), framesLoading(false)), () => Rx.Observable.of(
133+
error({
134+
title: "There was an error retriving animation",
135+
message: "Please contact the administrator"
136+
}),
137+
stop()
138+
))
102139
.concat(
103140
action$
104141
.ofType(SET_CURRENT_FRAME)
@@ -118,19 +155,21 @@ module.exports = {
118155
.map(() => currentFrameValueSelector(getState()))
119156
.map(t => t ? moveTime(t) : stop()),
120157
timeDimensionPlayback: (action$, { getState = () => { } } = {}) =>
121-
action$.ofType(SET_FRAMES).exhaustMap(() =>
122-
Rx.Observable.interval(frameDurationSelector(getState()) * 1000).startWith(0) // start immediately
123-
.let(pausable(
124-
action$
125-
.ofType(PLAY, PAUSE)
126-
.map(a => a.type === PLAY)
127-
))
128-
// pause is with loss, so the count of timer is not correct.
129-
// the following scan emit a for every event emitted effectively, with correct count
130-
// TODO: in case of loop, we can reset to 0 on load end.
131-
.map(() => setCurrentFrame(currentFrameSelector(getState()) + 1))
132-
.concat(Rx.Observable.of(stop()))
133-
.takeUntil(action$.ofType(STOP, LOCATION_CHANGE))
158+
action$.ofType(SET_FRAMES)
159+
.exhaustMap(() =>
160+
Rx.Observable.interval(frameDurationSelector(getState()) * 1000).startWith(0) // start immediately
161+
.let(pausable(
162+
action$
163+
.ofType(PLAY, PAUSE)
164+
.map(a => a.type === PLAY)
165+
))
166+
// pause is with loss, so the count of timer is not correct.
167+
// the following scan emit a for every event emitted effectively, with correct count
168+
// TODO: in case of loop, we can reset to 0 on load end.
169+
.map(() => setCurrentFrame(currentFrameSelector(getState()) + 1))
170+
.concat(Rx.Observable.of(stop()))
171+
.takeUntil(action$.ofType(STOP, LOCATION_CHANGE))
172+
134173
),
135174
/**
136175
* Synchronizes the fixed animation step toggle with guide layer on timeline

web/client/observables/epics.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const start = (stream$, actions = []) => stream$
1616
* @memberof observables.epics
1717
* @param {object|object[]} startAction start action(s)
1818
* @param {object|object[]} endAction end action(s)
19-
* @param {Observable} exceptionStream$ an optional stream for exception.
19+
* @param {function} exception an optional function that returns the stream for exceptions
2020
*/
2121
const wrapStartStop = (startAction, endAction, exceptionStream$) => stream$ =>
2222
(exceptionStream$ ?

web/client/selectors/timeline.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ const calculateOffsetTimeSelector = (state) => {
125125

126126
const selectedLayerData = state => getLayerFromId(state, selectedLayerSelector(state));
127127
const selectedLayerName = state => selectedLayerData(state) && selectedLayerData(state).name;
128-
const selectedLayerUrl = state => selectedLayerData(state) && selectedLayerData(state).dimensions && selectedLayerData(state).dimensions.filter((x) => x.name === "time").map((l) => l.source.url);
128+
const selectedLayerTimeDimensionConfiguration = state => selectedLayerData(state) && selectedLayerData(state).dimensions && selectedLayerData(state).dimensions.filter((x) => x.name === "time");
129+
const selectedLayerUrl = state => selectedLayerTimeDimensionConfiguration(state).map((l) => l.source.url);
129130

130131
const mouseEventSelector = state => get(state, "timeline.mouseEvent");
131132

@@ -143,6 +144,8 @@ module.exports = {
143144
loadingSelector,
144145
selectedLayerSelector,
145146
calculateOffsetTimeSelector,
147+
selectedLayerData,
148+
selectedLayerTimeDimensionConfiguration,
146149
selectedLayerName,
147150
selectedLayerUrl
148151
};

0 commit comments

Comments
 (0)