Skip to content

Commit

Permalink
WV-3298 limit data availability to visible time extent (#5519)
Browse files Browse the repository at this point in the history
* Refactor timeline axis component to handle changes in active layers and date range

* lint fixes

* Refactor timeline axis component to handle changes in active layers and date range

* add a comment

* reload layers when activeLayers is updated and the date has changed

* Refactor UpdateProjection component to remove unused code and optimize map rendering

* Revert "Refactor UpdateProjection component to remove unused code and optimize map rendering"

This reverts commit cd54548.
  • Loading branch information
PatchesMaps authored Oct 25, 2024
1 parent 86d1137 commit d824ed4
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 55 deletions.
70 changes: 70 additions & 0 deletions web/js/components/timeline/timeline-axis/timeline-axis.js
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class TimelineAxis extends Component {
position,
dateA,
dateB,
activeLayers,
} = this.props;

const checkForPropsUpdates = nextProps.axisWidth === axisWidth
Expand All @@ -95,6 +96,7 @@ class TimelineAxis extends Component {
&& nextProps.transformX === transformX
&& nextProps.frontDate === frontDate
&& nextProps.backDate === backDate
&& nextProps.activeLayers.length === activeLayers.length
&& lodashIsEqual(nextProps.matchingTimelineCoverage, matchingTimelineCoverage);

const {
Expand Down Expand Up @@ -142,7 +144,23 @@ class TimelineAxis extends Component {
draggerTimeState,
draggerTimeStateB,
timelineEndDateLimit,
activeLayers,
proj,
frontDate,
backDate,
} = this.props;

if (activeLayers.length !== prevProps.activeLayers.length || frontDate !== prevProps.frontDate || backDate !== prevProps.backDate) {
const backDateTime = new Date(backDate).getTime();
const startDateTime = new Date(frontDate).getTime();
const draggerTimeStateDate = new Date(draggerTimeState);
const draggerTimeStateTime = draggerTimeStateDate.getTime();
// Make sure that the dragger is within the new time range
const maxBackDate = draggerTimeStateTime > backDateTime ? (draggerTimeStateDate.setDate(draggerTimeStateDate.getDate() + 1), draggerTimeStateDate.toISOString()) : backDate;
const minFrontDate = draggerTimeStateTime < startDateTime ? (draggerTimeStateDate.setDate(draggerTimeStateDate.getDate() - 1), draggerTimeStateDate.toISOString()) : frontDate;
activeLayers.forEach((layer) => this.addTimeRanges(layer, proj, [minFrontDate, maxBackDate]));
}

const { wheelZoom } = this.state;
let draggerDate = draggerSelected === 'selected' ? draggerTimeState : draggerTimeStateB;

Expand Down Expand Up @@ -1321,6 +1339,56 @@ class TimelineAxis extends Component {
updatePositioningOnAxisStopDrag(updatePositioningArguments, hoverTimeDate);
}

addTimeRanges = (def, proj, dateRange) => {
const {
addGranuleDateRanges,
} = this.props;
const {
cmrAvailability,
dataAvailability,
id,
} = def;
// if opted in to CMR availability, get granule date ranges if needed
if (cmrAvailability || dataAvailability === 'cmr') {
const worker = new Worker('js/workers/cmr.worker.js');
worker.onmessage = (event) => {
worker.terminate();
addGranuleDateRanges(def, event.data);
};
worker.onerror = () => worker.terminate();
worker.postMessage({ operation: 'getLayerGranuleRanges', args: [def] });
}
// if opted in to DescribeDomains availability, get granule date ranges if needed
if (dataAvailability === 'dd') {
const worker = new Worker('js/workers/dd.worker.js');
worker.onmessage = (event) => {
if (Array.isArray(event.data)) { // our final format is an array
worker.terminate(); // terminate the worker
return addGranuleDateRanges(def, event.data); // dispatch the action
}
// DOMParser is not available in workers so we parse the xml on the main thread before sending it back to the worker
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(event.data, 'text/xml');
const domains = xmlDoc.querySelector('Domain')?.textContent;
if (!domains) worker.terminate();
worker.postMessage({ operation: 'mergeDomains', args: [domains, 60_000] });
};
worker.onerror = () => worker.terminate();
let startDate = new Date(def.startDate);
let endDate = def.endDate ? new Date(def.endDate).toISOString() : new Date().toISOString();
if (dateRange) {
[startDate, endDate] = dateRange;
}
const params = {
startDate,
endDate,
id,
proj: proj.crs,
};
worker.postMessage({ operation: 'requestDescribeDomains', args: [params] });
}
};

/**
* @desc get matching coverage line dimensions for given date range
* @returns {Object} visible, leftOffset, width
Expand Down Expand Up @@ -1532,6 +1600,8 @@ class TimelineAxis extends Component {
}

TimelineAxis.propTypes = {
activeLayers: PropTypes.array,
addGranuleDateRanges: PropTypes.func,
animationEndLocation: PropTypes.number,
animationStartLocation: PropTypes.number,
animEndLocationDate: PropTypes.object,
Expand Down
12 changes: 12 additions & 0 deletions web/js/containers/timeline/timeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
getNextTimeSelection,
} from '../../modules/date/util';
import { toggleActiveCompareState } from '../../modules/compare/actions';
import { addGranuleDateRanges } from '../../modules/layers/actions';
import {
onActivate as openAnimation,
onClose as closeAnimation,
Expand Down Expand Up @@ -1138,6 +1139,7 @@ class Timeline extends React.Component {
render() {
const {
activeLayers,
addGranuleDateRanges,
animationDisabled,
animEndLocationDate,
animStartLocationDate,
Expand Down Expand Up @@ -1170,6 +1172,7 @@ class Timeline extends React.Component {
timeScale,
timeScaleChangeUnit,
toggleActiveCompareState,
proj,
} = this.props;
const {
animationEndLocation,
Expand Down Expand Up @@ -1281,6 +1284,8 @@ class Timeline extends React.Component {
<div id="timeline-footer" className="notranslate">
{/* Axis */}
<TimelineAxis
activeLayers={activeLayers}
addGranuleDateRanges={addGranuleDateRanges}
appNow={appNow}
axisWidth={axisWidth}
parentOffset={parentOffset}
Expand Down Expand Up @@ -1326,6 +1331,7 @@ class Timeline extends React.Component {
isDraggerDragging={isDraggerDragging}
isTimelineDragging={isTimelineDragging}
matchingTimelineCoverage={matchingTimelineCoverage}
proj={proj}
/>

<AxisHoverLine
Expand Down Expand Up @@ -1640,6 +1646,7 @@ function mapStateToProps(state) {
isKioskModeActive,
displayStaticMap,
newCustomDelta,
proj: proj.selected,
};
}

Expand Down Expand Up @@ -1699,6 +1706,9 @@ const mapDispatchToProps = (dispatch) => ({
onPauseAnimation: () => {
dispatch(pauseAnimation());
},
addGranuleDateRanges: (layer, dateRanges) => {
dispatch(addGranuleDateRanges(layer, dateRanges));
},
});

export default connect(
Expand All @@ -1707,6 +1717,7 @@ export default connect(
)(Timeline);

Timeline.propTypes = {
addGranuleDateRanges: PropTypes.func,
appNow: PropTypes.object,
activeLayers: PropTypes.array,
animationDisabled: PropTypes.bool,
Expand Down Expand Up @@ -1766,4 +1777,5 @@ Timeline.propTypes = {
toggleCustomModal: PropTypes.func,
triggerTodayButton: PropTypes.func,
updateAppNow: PropTypes.func,
proj: PropTypes.object,
};
50 changes: 0 additions & 50 deletions web/js/map/layerbuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import {
getGeographicResolutionWMS,
mergeBreakpointLayerAttributes,
} from './util';
import { addGranuleDateRanges } from '../modules/layers/actions';
import { datesInDateRanges, prevDateInDateRange } from '../modules/layers/util';
import { getSelectedDate } from '../modules/date/selectors';
import {
Expand Down Expand Up @@ -1110,53 +1109,6 @@ export default function mapLayerBuilder(config, cache, store) {
return layer;
};

const addTimeRanges = (def) => {
const state = store.getState();
const proj = state.proj.selected;
const {
cmrAvailability,
dataAvailability,
id,
} = def;
// if opted in to CMR availability, get granule date ranges if needed
if ((cmrAvailability || dataAvailability === 'cmr') && !def.granuleDateRanges) {
const worker = new Worker('js/workers/cmr.worker.js');
worker.onmessage = (event) => {
worker.terminate();
store.dispatch(addGranuleDateRanges(def, event.data));
};
worker.onerror = () => {
worker.terminate();
};
worker.postMessage({ operation: 'getLayerGranuleRanges', args: [def] });
}
// if opted in to DescribeDomains availability, get granule date ranges if needed
if (dataAvailability === 'dd' && !def.granuleDateRanges) {
const worker = new Worker('js/workers/dd.worker.js');
worker.onmessage = (event) => {
if (Array.isArray(event.data)) { // our final format is an array
worker.terminate(); // terminate the worker
return store.dispatch(addGranuleDateRanges(def, event.data)); // dispatch the action
}
// DOMParser is not available in workers so we parse the xml on the main thread before sending it back to the worker
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(event.data, 'text/xml');
const domains = xmlDoc.querySelector('Domain').textContent;
worker.postMessage({ operation: 'mergeDomains', args: [domains, 60_000] });
};
worker.onerror = () => {
worker.terminate();
};
const params = {
startDate: new Date(def.startDate).toISOString(),
endDate: def.endDate ? new Date(def.endDate).toISOString() : new Date().toISOString(),
id,
proj: proj.crs,
};
worker.postMessage({ operation: 'requestDescribeDomains', args: [params] });
}
};

/**
* Create a new OpenLayers Layer
* @param {object} def
Expand Down Expand Up @@ -1185,8 +1137,6 @@ export default function mapLayerBuilder(config, cache, store) {
let layer = cache.getItem(key);
const isGranule = type === 'granule';

addTimeRanges(def);

if (!layer || isGranule || def.type === 'titiler') {
if (!date) date = options.date || getSelectedDate(state);
const cacheOptions = getCacheOptions(period, date);
Expand Down
16 changes: 11 additions & 5 deletions web/js/mapUI/components/update-projection/updateProjection.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import OlLayerGroup from 'ol/layer/Group';
Expand Down Expand Up @@ -63,7 +63,7 @@ function UpdateProjection(props) {
* @returns {void}
*/
const clearLayers = function(saveCache) {
ui.selected.setLayers([]);
ui.selected?.setLayers([]);

if (saveCache) return;
ui.cache.clear();
Expand Down Expand Up @@ -131,7 +131,7 @@ function UpdateProjection(props) {
});
const layerResults = await Promise.allSettled(layerPromises);
const createdLayers = layerResults.filter(({ status }) => status === 'fulfilled').map(({ value }) => value);
mapUI.setLayers(createdLayers);
mapUI?.setLayers(createdLayers);
} else {
const stateArray = [['active', 'selected'], ['activeB', 'selectedB']];
if (compare && !compare.isCompareA && compare.mode === 'spy') {
Expand All @@ -140,7 +140,7 @@ function UpdateProjection(props) {
clearLayers(saveCache);
const stateArrayGroups = stateArray.map(async (arr) => getCompareLayerGroup(arr, layerState, granuleOptions));
const compareLayerGroups = await Promise.all(stateArrayGroups);
mapUI.setLayers(compareLayerGroups);
mapUI?.setLayers(compareLayerGroups);
compareMapUi.create(mapUI, compare.mode);
}
updateLayerVisibilities();
Expand Down Expand Up @@ -374,12 +374,18 @@ function UpdateProjection(props) {
.map((layer) => layer.granuleDateRanges);

const prevActiveLayers = usePrevious(activeLayers);
const [selectedDate, setSelectedDate] = useState(dateCompareState.date.selected);

useEffect(() => {
if (!ui.selected) return;
const prevL2Layers = selectL2Layers(prevActiveLayers);
const activeL2Layers = selectL2Layers(activeLayers);
const needsReload = activeL2Layers.some((dateRange, i) => dateRange?.length !== prevL2Layers[i]?.length);
// Check if new date ranges have been added to L2 layers. We don't want to reload every time new ranges are added so also check if there were no ranges before.
const hasNewDateRanges = activeL2Layers.some((dateRange, i) => dateRange?.length !== prevL2Layers[i]?.length) && prevL2Layers.includes(undefined);
const hasNewLayers = prevActiveLayers.length !== activeLayers.length; // Check if new layers have been added
const hasNewDate = selectedDate !== dateCompareState.date.selected; // Check if the date has changed
const needsReload = hasNewDateRanges || hasNewLayers || hasNewDate;
setSelectedDate(dateCompareState.date.selected);
if (needsReload) {
reloadLayers(null, true);
}
Expand Down

0 comments on commit d824ed4

Please sign in to comment.