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

WV-3298 limit data availability to visible time extent #5519

Merged
merged 7 commits into from
Oct 25, 2024
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
Loading