Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #9208 Timeline map sync does not update for time ranges visualization #9209

Merged
merged 2 commits into from
Jun 5, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 22 additions & 1 deletion web/client/epics/__tests__/timeline-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ import {
SET_SNAP_RADIO_BUTTON_ENABLED,
AUTOSELECT,
SELECT_TIME,
SET_TIME_LAYERS
SET_TIME_LAYERS,
setMapSync
} from '../../actions/timeline';
import { changeLayerProperties, removeNode } from '../../actions/layers';
import { SET_CURRENT_TIME, SET_OFFSET_TIME, updateLayerDimensionData } from '../../actions/dimension';
Expand Down Expand Up @@ -1123,6 +1124,26 @@ describe('timeline Epics', () => {
done();
}, MAPSYNC_OFF_STATE);
});
it('mapSync off via action should trigger update range', done => {
// tests with mapsync on take around 400 ms to run, so this have to be set with a bigger time
// TODO: use mock-axios or other tools to speed up these tests
testEpic(addTimeoutEpic(updateRangeDataOnRangeChange, 500), 4, setMapSync(false), ([action1, action2, action3, action4]) => {
const { type: startType } = action1;
const { type: range1Type, range } = action2;
const { type: range2Type } = action3;
const { type: endType } = action4;
// first action moves the current timeline view to center the current time
expect(startType).toBe(LOADING);
expect(endType).toBe(LOADING);
// in this case loading fixed file of domain values causes double trigger of range data loaded with domain
// in real world the 2nd response is histogram. TODO: test also histogram
expect(range1Type).toBe(RANGE_DATA_LOADED);
expect(range2Type).toBe(RANGE_DATA_LOADED);
expect(range.start).toBe("2000-01-01T00:00:00.000Z");
expect(range.end).toBe("2001-12-31T00:00:00.000Z");
done();
}, MAPSYNC_OFF_STATE);
});
describe('fixed domain values', () => {
const MAPSYNC_ON_DOMAIN_VALUES_STATE = set('dimension.data.time.TEST_LAYER.domain',
"2000-03-01T00:00:00.000Z,2000-06-08T00:00:00.000Z"
Expand Down
10 changes: 5 additions & 5 deletions web/client/epics/dimension.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@ import { layersWithTimeDataSelector, offsetTimeSelector, currentTimeSelector } f
import { describeDomains, getMultidimURL } from '../api/MultiDim';
import { domainsToDimensionsObject } from '../utils/TimeUtils';
import { pick, find, get, flatten } from 'lodash';

import { expandLimitSelector } from '../selectors/timeline';

const DESCRIBE_DOMAIN_OPTIONS = {
expandLimit: 10 // TODO: increase this limit to max client allowed
expandLimit: 10
};


Expand All @@ -50,7 +50,7 @@ export const updateLayerDimensionOnCurrentTimeSelection = (action$, { getState =
* Check the presence of Multidimensional API extension, then setup layers properly.
* Updates also current dimension state
*/
export const queryMultidimensionalAPIExtensionOnAddLayer = (action$) =>
export const queryMultidimensionalAPIExtensionOnAddLayer = (action$, {getState = () => {}} = {}) =>
action$
.ofType(ADD_LAYER)
.filter(
Expand All @@ -61,7 +61,7 @@ export const queryMultidimensionalAPIExtensionOnAddLayer = (action$) =>
.map(({ layer = {} } = {}) => ({ layer, multidimURL: getMultidimURL(layer)}))
// every add layer has it's own flow, this is why it uses
.flatMap(({ layer = {}, multidimURL } = {}) =>
describeDomains(multidimURL, layer.name, undefined, DESCRIBE_DOMAIN_OPTIONS)
describeDomains(multidimURL, layer.name, undefined, { expandLimit: expandLimitSelector(getState()) || DESCRIBE_DOMAIN_OPTIONS.expandLimit })
.switchMap( domains => {
const dimensions = domainsToDimensionsObject(domains, multidimURL) || [];
if (dimensions && dimensions.length > 0) {
Expand Down Expand Up @@ -106,7 +106,7 @@ export const updateLayerDimensionDataOnMapLoad = (action$, {getState = () => {}}
.concat(Observable.from(layersWithMultidim)
// one flow for each dimension
.mergeMap(l =>
describeDomains(getTimeMultidimURL(l), l.name, undefined, DESCRIBE_DOMAIN_OPTIONS)
describeDomains(getTimeMultidimURL(l), l.name, undefined, { expandLimit: expandLimitSelector(getState()) || DESCRIBE_DOMAIN_OPTIONS.expandLimit })
.switchMap( domains =>
Observable.from(flatten(domainsToDimensionsObject(domains, getTimeMultidimURL(l))
.map(d => [
Expand Down
6 changes: 4 additions & 2 deletions web/client/epics/timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,11 +427,13 @@ export const updateRangeDataOnRangeChange = (action$, { getState = () => { } } =
)
.debounceTime(400)
.merge(action$.ofType(UPDATE_LAYER_DIMENSION_DATA).debounceTime(50))
.switchMap( () => {
.switchMap( (action) => {
// we should force the range data update when we turn off the map sync
const resetMapSync = action.mapSync === false;
const timeData = timeDataSelector(getState()) || {};
const layerIds = Object.keys(timeData).filter(id => timeData[id] && timeData[id].domain
// when data is already fully downloaded, no need to refresh, except if the mapSync is active
&& (isTimeDomainInterval(timeData[id].domain)) || isMapSync(getState()));
&& (isTimeDomainInterval(timeData[id].domain)) || isMapSync(getState()) || resetMapSync);
// update range data for every layer that need to sync with histogram/domain
return Rx.Observable.merge(
...layerIds.map(id =>
Expand Down
38 changes: 36 additions & 2 deletions web/client/selectors/__tests__/timeline-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ import {
multidimOptionsSelectorCreator,
timelineLayersSelector,
timelineLayersSetting,
timelineLayersParsedSettings
timelineLayersParsedSettings,
getTimeItems
} from '../timeline';

import { set, compose } from '../../utils/ImmutableUtils';
Expand Down Expand Up @@ -169,7 +170,40 @@ describe('timeline selector', () => {
expect(timelayer[0].id).toEqual(TEST_LAYER_ID);
expect(timelayer[0].hideInTimeline).toEqual(true);
});

it('getTimeItems should give priority to rangeData', () => {
const data = {
source: {
type: 'multidim-extension',
version: '1.2',
url: '/geoserver/gwc/service/wmts'
},
name: 'time',
domain: '2022-06-01T00:00:00.000Z/2023-06-01T00:00:00.000Z,2023-01-01T00:00:00.000Z/2023-06-01T00:00:00.000Z,2023-01-01T00:00:00.000Z/2023-12-31T00:00:00.000Z'
};
const range = {
start: '2022-06-01T00:00:00.000Z',
end: '2024-01-09T11:07:53.218Z'
};
let timeItems = getTimeItems(data, range);
expect(timeItems.length).toBe(3);
const rangeData = {
range: {
start: '2022-06-01T00:00:00.000Z',
end: '2024-01-09T11:07:53.218Z'
},
histogram: {
values: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
domain: '2022-06-01T00:00:00.000Z/2023-06-01T00:00:00.000Z/PT704H'
},
domain: {
values: [
'2022-06-01T00:00:00.000Z/2023-06-01T00:00:00.000Z'
]
}
};
timeItems = getTimeItems(data, range, rangeData);
expect(timeItems.length).toBe(1);
});
it('itemsSelector', () => {
const histogramItems = itemsSelector(SAMPLE_STATE_HISTOGRAM);
expect(histogramItems.length).toBe(31);
Expand Down
8 changes: 6 additions & 2 deletions web/client/selectors/timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,10 +130,14 @@ export const rangeDataToItems = (rangeData = {}, range) => {
* @param {object} rangeData object that contains domain or histogram
*/
export const getTimeItems = (data = {}, range, rangeData) => {
// rangeData populates when some changes ara applied with map sync
// we should use this when available
// because represent the latest updated value
if (rangeData?.domain || rangeData?.histogram) {
return rangeDataToItems(rangeData, range);
}
if (data && data.values || data && data.domain && !isTimeDomainInterval(data.domain)) {
return valuesToItems(data.values || data.domain.split(','), range);
} else if (rangeData && rangeData.histogram) {
return rangeDataToItems(rangeData, range);
}
return [];
};
Expand Down