diff --git a/web/js/containers/tour.js b/web/js/containers/tour.js index 5a9e1c4d7a..42c5d20022 100644 --- a/web/js/containers/tour.js +++ b/web/js/containers/tour.js @@ -24,7 +24,10 @@ import { import { clearCustoms, } from '../modules/palettes/actions'; -import { BULK_PALETTE_RENDERING_SUCCESS } from '../modules/palettes/constants'; +import { + BULK_PALETTE_RENDERING_SUCCESS, + BULK_PALETTE_PRELOADING_SUCCESS, +} from '../modules/palettes/constants'; import { stop as stopAnimation } from '../modules/animation/actions'; import { onClose as closeModal } from '../modules/modal/actions'; import { LOCATION_POP_ACTION } from '../redux-location-state-customs'; @@ -39,6 +42,7 @@ import { changeTab as changeTabAction } from '../modules/sidebar/actions'; import ErrorBoundary from './error-boundary'; import history from '../main'; import util from '../util/util'; +import { promiseImageryForTour } from '../modules/map/util'; const { HIDE_TOUR } = safeLocalStorage.keys; @@ -51,6 +55,14 @@ const getTransitionAttr = function(transition) { return ''; }; +const prepareLayersList = function(layersString, config) { + let layers; + layers = layersParse12(layersString, config); + layers = uniqBy(layers, 'id'); + layers = layers.filter((layer) => !layer.custom && !layer.disabled); + return layers; +}; + class Tour extends React.Component { constructor(props) { super(props); @@ -138,7 +150,7 @@ class Tour extends React.Component { selectTour(e, currentStory, currentStoryIndex, currentStoryId) { const { - config, renderedPalettes, selectTour, processStepLink, isKioskModeActive, isEmbedModeActive, + config, renderedPalettes, selectTour, processStepLink, isKioskModeActive, isEmbedModeActive, preProcessStepLink, promiseImageryForTour, } = this.props; if (e) e.preventDefault(); const kioskParam = this.getKioskParam(isKioskModeActive); @@ -165,6 +177,11 @@ class Tour extends React.Component { config, renderedPalettes, ); + preProcessStepLink( + `${currentStory.steps[1].stepLink}&tr=${currentStoryId}${transitionParam}${kioskParam}&em=${isEmbedModeActive}`, + config, + promiseImageryForTour, + ); } fetchMetadata(currentStory, stepIndex) { @@ -243,7 +260,7 @@ class Tour extends React.Component { currentStoryId, } = this.state; const { - config, renderedPalettes, processStepLink, isKioskModeActive, activeTab, changeTab, isEmbedModeActive, + config, renderedPalettes, processStepLink, isKioskModeActive, activeTab, changeTab, isEmbedModeActive, preProcessStepLink, promiseImageryForTour, } = this.props; const kioskParam = this.getKioskParam(isKioskModeActive); @@ -264,6 +281,13 @@ class Tour extends React.Component { config, renderedPalettes, ); + if (currentStep + 2 <= totalSteps) { + preProcessStepLink( + `${currentStory.steps[newStep].stepLink}&tr=${currentStoryId}${transitionParam}${kioskParam}&em=${isEmbedModeActive}`, + config, + promiseImageryForTour, + ); + } } if (currentStep + 1 === totalSteps + 1) { this.toggleModalInProgress(e); @@ -522,6 +546,33 @@ const mapDispatchToProps = (dispatch) => ({ dispatch({ type: LOCATION_POP_ACTION, payload: location }); } }, + preProcessStepLink: async (search, config, promiseImageryForTour) => { + search = search.split('/?').pop(); + const parameters = util.fromQueryString(search); + let layersA = []; + let layersB = []; + const promisesParams = []; + + if (parameters.l) { + layersA = prepareLayersList(parameters.l, config); + promisesParams.push({ layers: layersA, dateString: parameters.t }); + } + if (parameters.l1) { + layersB = prepareLayersList(parameters.l1, config); + promisesParams.push({ layers: layersB, dateString: parameters.t1, activeString: 'activeB' }); + } + preloadPalettes([...layersA, ...layersB], {}, false).then(async (obj) => { + await dispatch({ + type: BULK_PALETTE_PRELOADING_SUCCESS, + tourStoryPalettes: obj.rendered, + }); + const promises = []; + promisesParams.forEach((set) => { + promises.push(promiseImageryForTour(set.layers, set.dateString, set.activeString)); + }); + await Promise.all(promises); + }); + }, startTour: () => { dispatch(startTourAction()); }, @@ -561,6 +612,7 @@ const mapStateToProps = (state) => { screenHeight, renderedPalettes: palettes.rendered, activeTab: sidebar.activeTab, + promiseImageryForTour: (layers, dateString, activeString) => promiseImageryForTour(state, layers, dateString, activeString), }; }; @@ -582,6 +634,7 @@ Tour.propTypes = { isActive: PropTypes.bool, isKioskModeActive: PropTypes.bool, processStepLink: PropTypes.func, + preProcessStepLink: PropTypes.func, renderedPalettes: PropTypes.object, resetProductPicker: PropTypes.func, screenHeight: PropTypes.number, diff --git a/web/js/map/layerbuilder.js b/web/js/map/layerbuilder.js index 280807ea5a..d4bd1b4ae7 100644 --- a/web/js/map/layerbuilder.js +++ b/web/js/map/layerbuilder.js @@ -241,7 +241,7 @@ export default function mapLayerBuilder(config, cache, store) { previousDate, } = getRequestDates(def, options); const date = closestDate; - if (date) { + if (date && !options.date) { options.date = date; } const dateOptions = { date, nextDate, previousDate }; diff --git a/web/js/modules/map/util.js b/web/js/modules/map/util.js index 5e1cdc65ee..c4bd264e1b 100644 --- a/web/js/modules/map/util.js +++ b/web/js/modules/map/util.js @@ -11,6 +11,7 @@ import OlRendererCanvasTileLayer from 'ol/renderer/canvas/TileLayer'; import Promise from 'bluebird'; import { encode } from '../link/util'; import { getActiveVisibleLayersAtDate } from '../layers/selectors'; +import { tryCatchDate } from '../date/util'; /* * Set default extent according to time of day: @@ -305,3 +306,43 @@ export async function promiseImageryForTime(state, date, activeString) { selected.getView().changed(); return date; } + +/** + * Trigger tile requests for all given layers on a given date. + * @method promiseImageryForTour + */ +export async function promiseImageryForTour(state, layers, dateString, activeString) { + const { map } = state; + if (!map.ui.proj) return; + const { + cache, selected, createLayer, layerKey, + } = map.ui; + const appNow = lodashGet(state, 'date.appNow'); + const date = tryCatchDate(dateString, appNow); + await Promise.all(layers.map(async (layer) => { + if (layer.type === 'granule' || layer.type === 'ttiler') { + return Promise.resolve(); + } + const options = { date, group: activeString || 'active' }; + const keys = []; + if (layer.custom) { + keys.push(`palette=${layer.custom}`); + } + if (layer.min) { + keys.push(`min=${layer.min}`); + } + if (layer.max) { + keys.push(`max=${layer.max}`); + } + if (layer.squash) { + keys.push('squash'); + } + if (keys.length > 0) { + options.style = keys.join(','); + } + + const key = layerKey(layer, options, state); + const layerGroup = cache.getItem(key) || await createLayer(layer, options); + return promiseLayerGroup(layerGroup, selected); + })); +} diff --git a/web/js/modules/palettes/constants.js b/web/js/modules/palettes/constants.js index a843678b0f..0fed270830 100644 --- a/web/js/modules/palettes/constants.js +++ b/web/js/modules/palettes/constants.js @@ -10,6 +10,7 @@ export const CLEAR_CUSTOMS = 'PALETTES/CLEAR_CUSTOMS'; export const SET_CUSTOM = 'PALETTES/SET_CUSTOM'; export const LOADED_CUSTOM_PALETTES = 'PALETTES/LOADED_CUSTOM_PALETTES'; export const BULK_PALETTE_RENDERING_SUCCESS = 'PALETTES/BULK_PALETTE_RENDERING_SUCCESS'; +export const BULK_PALETTE_PRELOADING_SUCCESS = 'PALETTES/BULK_PALETTE_PRELOADING_SUCCESS'; export const PALETTE_STRINGS_PERMALINK_ARRAY = [ 'palette', diff --git a/web/js/modules/palettes/reducers.js b/web/js/modules/palettes/reducers.js index 8bd01dc404..1bc7f31ade 100644 --- a/web/js/modules/palettes/reducers.js +++ b/web/js/modules/palettes/reducers.js @@ -12,6 +12,7 @@ import { SET_THRESHOLD_RANGE_AND_SQUASH, LOADED_CUSTOM_PALETTES, BULK_PALETTE_RENDERING_SUCCESS, + BULK_PALETTE_PRELOADING_SUCCESS, CLEAR_CUSTOM, SET_DISABLED_CLASSIFICATION, } from './constants'; @@ -23,6 +24,7 @@ export const defaultPaletteState = { active: {}, activeB: {}, isLoading: {}, + tourStoryPalettes: {}, }; export function getInitialPaletteState(config) { const rendered = lodashGet(config, 'palettes.rendered') || {}; @@ -46,6 +48,10 @@ export function paletteReducer(state = defaultPaletteState, action) { return update(state, { rendered: { $merge: action.rendered || {} }, }); + case BULK_PALETTE_PRELOADING_SUCCESS: + return update(state, { + tourStoryPalettes: { $merge: action.tourStoryPalettes || {} }, + }); case REQUEST_PALETTE_SUCCESS: { const isLoading = update(state.isLoading, { $unset: [action.id] }); return lodashAssign({}, state, { diff --git a/web/js/modules/palettes/selectors.js b/web/js/modules/palettes/selectors.js index 08d317456b..48362bee5b 100644 --- a/web/js/modules/palettes/selectors.js +++ b/web/js/modules/palettes/selectors.js @@ -350,15 +350,16 @@ export function getKey(layerId, groupStr, state) { return ''; } const def = getPalette(layerId, undefined, groupStr, state); + const { values } = getPalette(layerId, 0, groupStr, state).entries; const keys = []; if (def.custom) { keys.push(`palette=${def.custom}`); } if (def.min) { - keys.push(`min=${def.min}`); + keys.push(`min=${getMinValue(values[def.min])}`); } if (def.max) { - keys.push(`max=${def.max}`); + keys.push(`max=${getMinValue(values[def.max])}`); } if (def.squash) { keys.push('squash');