From 3e9604e61217a8dda30a078448af096f21379556 Mon Sep 17 00:00:00 2001 From: Tariq Soliman Date: Mon, 12 Dec 2022 14:13:50 -0800 Subject: [PATCH] #292 Controlled Time Layers only make initial query, refactored to remove a conflicting setTimeout (#293) --- config/js/config.js | 14 +- docs/pages/Configure/Layers/Vector/Vector.md | 8 +- src/essence/Ancillary/TimeControl.js | 172 +++++-------------- src/essence/Ancillary/TimeUI.js | 148 ++++++++++------ src/essence/Basics/Layers_/LayerCapturer.js | 5 +- src/essence/Basics/Layers_/Layers_.js | 2 +- 6 files changed, 159 insertions(+), 190 deletions(-) diff --git a/config/js/config.js b/config/js/config.js index a5f6fe7d..6f2e6bc3 100644 --- a/config/js/config.js +++ b/config/js/config.js @@ -1121,12 +1121,12 @@ function makeLayerBarAndModal(d, level, options) { timeFalseSel = "selected"; } - var timeGlobalSel = "", + var timeRequerySel = "", timeLocalSel = ""; if (typeof d.time != "undefined") { switch (d.time.type) { - case "global": - timeGlobalSel = "selected"; + case "requery": + timeRequerySel = "selected"; break; case "local": timeLocalSel = "selected"; @@ -1134,7 +1134,7 @@ function makeLayerBarAndModal(d, level, options) { default: } } else { - timeGlobalSel = "selected"; + timeRequerySel = "selected"; } var timeCompositeTileTrueSel = "", @@ -1458,10 +1458,10 @@ function makeLayerBarAndModal(d, level, options) { "" + "
" + "" + - "" + + "" + "
" + "
" + "" + @@ -2379,7 +2379,7 @@ function save() { // time properties layerObject.time = {}; layerObject.time.enabled = modalTime; // static or timed - layerObject.time.type = modalTimeType; // 'global or local' + layerObject.time.type = modalTimeType; // 'requery or local' layerObject.time.isRelative = true; // absolute or relative layerObject.time.current = new Date().toISOString().split(".")[0] + "Z"; // initial time diff --git a/docs/pages/Configure/Layers/Vector/Vector.md b/docs/pages/Configure/Layers/Vector/Vector.md index e9c77f10..c54e555f 100644 --- a/docs/pages/Configure/Layers/Vector/Vector.md +++ b/docs/pages/Configure/Layers/Vector/Vector.md @@ -30,6 +30,8 @@ A file path that points to a geojson. If the path is relative, it will be relati _type:_ bool Whether the layer can be dynamically updated or not. If true, the layer can be dynamically updated and the URL is not required. +If true and a URL is set and Time Enabled is true, the initial url query will be performed. + #### Legend _type:_ string @@ -55,10 +57,12 @@ A value from 0 to 1 of the layer's initial opacity. 1 is fully opaque. _type:_ bool True if the layer is time enabled. URLs that contain `{starttime}` or `{endtime}` will be dynamically replaced by their set values when the layer is fetched. +If true and a URL is set and Controlled is true, only the initial url query will be performed. + #### Time Type -_type:_ enum [Global, Individual] -Whether the layer should use global time values or function independently with its own time values. +_type:_ enum [Requery, Local] +When the time changes, whether the layer should Requery the source or filter the layer Locally (based on feature properties. #### Time Format diff --git a/src/essence/Ancillary/TimeControl.js b/src/essence/Ancillary/TimeControl.js index 90f42243..d1e414fe 100644 --- a/src/essence/Ancillary/TimeControl.js +++ b/src/essence/Ancillary/TimeControl.js @@ -16,10 +16,10 @@ const relativeTimeFormat = new RegExp( var TimeControl = { enabled: false, isRelative: true, - currentTime: new Date().toISOString().split('.')[0] + 'Z', + currentTime: null, timeOffset: '01:00:00', - startTime: new Date().toISOString().split('.')[0] + 'Z', - endTime: new Date().toISOString().split('.')[0] + 'Z', + startTime: null, + endTime: null, relativeStartTime: '01:00:00', relativeEndTime: '00:00:00', globalTimeFormat: null, @@ -37,29 +37,6 @@ var TimeControl = { return } - // prettier-ignore - const markup = [ - "", - - "", - ``, - - "", - ``, - - "", - ``, - - ``, - "", - - "", - ``, - - "", - ``, - ].join('\n'); - TimeControl.timeUI = TimeUI.init(timeInputChange) //updateTime() @@ -67,10 +44,11 @@ var TimeControl = { TimeControl.toggleTimeUI(false) } - //initLayerDataTimes() + initLayerTimes() + initLayerDataTimes() }, fina: function () { - initLayerTimes() + TimeControl.timeUI.fina() }, toggleTimeUI: function (isOn) { d3.select('#timeUI').style('visibility', function () { @@ -177,18 +155,19 @@ var TimeControl = { if (layer.time) return layer.time.end return false }, - reloadLayer: async function (layer, evenIfOff) { + reloadLayer: async function (layer, evenIfOff, evenIfControlled) { // reload layer if (typeof layer == 'string') { layer = L_.layersNamed[layer] } + if (L_.layersGroup[layer.name] === null) return var layerTimeFormat = d3.utcFormat(layer.time.format) layer.time.current = TimeControl.currentTime // keeps track of when layer was refreshed - if (layer.type == 'tile') { - if (layer.time && layer.time.enabled == true) { + if (layer.type === 'tile') { + if (layer.time && layer.time.enabled === true) { TimeControl.setLayerWmsParams(layer) } @@ -197,10 +176,14 @@ var TimeControl = { L_.toggleLayer(layer) } } else { + var originalUrl = layer.url + // replace start/endtime keywords - if (layer.time && layer.time.enabled == true) { - if (layer.time.type === 'global') { - var originalUrl = layer.url + if (layer.time && layer.time.enabled === true) { + if ( + layer.time.type === 'global' || + layer.time.type === 'requery' + ) { layer.url = layer.url .replace( /{starttime}/g, @@ -217,18 +200,20 @@ var TimeControl = { layer.time.type === 'local' && layer.time.endProp != null ) { - L_.timeFilterVectorLayer( - layer.name, - new Date(layer.time.start).getTime(), - new Date(layer.time.end).getTime() - ) + if (evenIfControlled === true || layer.controlled !== true) + L_.timeFilterVectorLayer( + layer.name, + new Date(layer.time.start).getTime(), + new Date(layer.time.end).getTime() + ) } else { // refresh map - if (L_.toggledArray[layer.name] || evenIfOff) { - await Map_.refreshLayer(layer) - } + if (evenIfControlled === true || layer.controlled !== true) + if (L_.toggledArray[layer.name] || evenIfOff) { + await Map_.refreshLayer(layer) + } // put start/endtime keywords back - if (layer.time && layer.time.enabled == true) + if (layer.time && layer.time.enabled === true) layer.url = originalUrl } } @@ -240,7 +225,7 @@ var TimeControl = { var reloadedLayers = [] for (let layerName in L_.layersNamed) { const layer = L_.layersNamed[layerName] - if (layer.time && layer.time.enabled == true) { + if (layer.time && layer.time.enabled === true) { TimeControl.reloadLayer(layer) reloadedLayers.push(layer.name) } @@ -251,7 +236,7 @@ var TimeControl = { var updatedLayers = [] for (let layerName in L_.layersNamed) { const layer = L_.layersNamed[layerName] - if (layer.time && layer.time.enabled == true) { + if (layer.time && layer.time.enabled === true) { layer.time.start = TimeControl.startTime layer.time.end = TimeControl.currentTime d3.select('.starttime.' + layer.name.replace(/\s/g, '')).text( @@ -287,8 +272,8 @@ var TimeControl = { const layer = L_.layersNamed[layerName] if ( layer.time && - layer.time.enabled == true && - layer.time.type == 'global' + layer.time.enabled === true && + (layer.time.type === 'global' || layer.time.type === 'requery') ) { TimeControl.setLayerTimeStatus(layer, color) updatedLayers.push(layer.name) @@ -298,17 +283,12 @@ var TimeControl = { }, setLayerWmsParams: function (layer) { var layerTimeFormat = d3.utcFormat(layer.time.format) + const l = L_.layersGroup[layer.name] - if (layer.type == 'tile') { - L_.layersGroup[layer.name].options.time = layerTimeFormat( - Date.parse(layer.time.end) - ) - L_.layersGroup[layer.name].options.starttime = layerTimeFormat( - Date.parse(layer.time.start) - ) - L_.layersGroup[layer.name].options.endtime = layerTimeFormat( - Date.parse(layer.time.end) - ) + if (l != null && layer.type === 'tile') { + l.options.time = layerTimeFormat(Date.parse(layer.time.end)) + l.options.starttime = layerTimeFormat(Date.parse(layer.time.start)) + l.options.endtime = layerTimeFormat(Date.parse(layer.time.end)) } }, } @@ -316,19 +296,13 @@ var TimeControl = { function initLayerDataTimes() { for (let i in L_.layersData) { const layer = L_.layersData[i] - if (layer.time && layer.time.enabled == true) { + if (layer.time && layer.time.enabled === true) { layer.time.start = L_.FUTURES.startTime ? L_.FUTURES.startTime.toISOString().split('.')[0] + 'Z' : TimeControl.startTime layer.time.end = L_.FUTURES.endTime ? L_.FUTURES.endTime.toISOString().split('.')[0] + 'Z' : TimeControl.endTime - d3.select('.starttime.' + layer.name.replace(/\s/g, '')).text( - layer.time.start - ) - d3.select('.endtime.' + layer.name.replace(/\s/g, '')).text( - layer.time.end - ) } } } @@ -336,7 +310,7 @@ function initLayerDataTimes() { function initLayerTimes() { for (let layerName in L_.layersNamed) { const layer = L_.layersNamed[layerName] - if (layer.time && layer.time.enabled == true) { + if (layer.time && layer.time.enabled === true) { layer.time.start = L_.FUTURES.startTime ? L_.FUTURES.startTime.toISOString().split('.')[0] + 'Z' : TimeControl.startTime @@ -357,58 +331,16 @@ function initLayerTimes() { } } -function updateTime() { - if (!TimeControl._updateLockedForAcceptingInput && TimeControl.enabled) { - // Continuously update global time clock and UI elements - var now = new Date() - var offset = 0 - var offsetTime = d3.select('#offsetTimeInput').property('value') - if (relativeTimeFormat.test(offsetTime)) { - offset = parseTime(offsetTime) - } - var currentTime = new moment(now).add(offset, 'seconds') - d3.select('#currentTimeLabel').text( - TimeControl.globalTimeFormat(currentTime) - ) - TimeControl.currentTime = - currentTime.toDate().toISOString().split('.')[0] + 'Z' - - if (d3.select('#isRelativeTime').property('checked') == true) { - var start = parseTime( - d3.select('#startRelativeTimeInput').property('value') - ) - var end = parseTime( - d3.select('#endRelativeTimeInput').property('value') - ) - var startTime = new moment(currentTime).subtract(start, 'seconds') - var endTime = new moment(currentTime).add(end, 'seconds') - - TimeControl.startTime = - startTime.toDate().toISOString().split('.')[0] + 'Z' - TimeControl.endTime = - endTime.toDate().toISOString().split('.')[0] + 'Z' - - d3.select('#startTimeInput').property( - 'value', - startTime.toISOString().split('.')[0] + 'Z' - ) - d3.select('#endTimeInput').property( - 'value', - endTime.toISOString().split('.')[0] + 'Z' - ) - } - } - setTimeout(updateTime, 100) -} - -function timeInputChange(startTime, endTime, currentTime) { +function timeInputChange(startTime, endTime, currentTime, skipUpdate) { TimeControl.startTime = startTime TimeControl.currentTime = currentTime == null ? endTime : currentTime TimeControl.endTime = endTime - // Update layer times and reload - TimeControl.updateLayersTime() - TimeControl.reloadTimeLayers() + if (skipUpdate !== true) { + // Update layer times and reload + TimeControl.updateLayersTime() + TimeControl.reloadTimeLayers() + } } function parseTime(t) { @@ -423,18 +355,4 @@ function parseTime(t) { return seconds } -function formatTimeString(seconds) { - // converts seconds to hh:mm:ss - if (typeof seconds === 'undefined') { - return '00:00:00' - } - var t = Math.abs(seconds) - var days = Math.floor(t / 86400) - var dt = new Date(t * 1000) - var dtString = dt.toISOString().substr(11, 8) - var s = dtString.split(':') - var hours = +s[0] + days * 24 - return (seconds < 0 ? '-' : '') + hours + ':' + s[1] + ':' + s[2] -} - export default TimeControl diff --git a/src/essence/Ancillary/TimeUI.js b/src/essence/Ancillary/TimeUI.js index 5b01cdcf..fcc9eff2 100644 --- a/src/essence/Ancillary/TimeUI.js +++ b/src/essence/Ancillary/TimeUI.js @@ -52,6 +52,8 @@ const TimeUI = { '10s', '20s', ], + _initialStart: null, + _initialEnd: null, init: function (timeChange) { TimeUI.timeChange = timeChange // prettier-ignore @@ -226,65 +228,107 @@ const TimeUI = { TimeUI._refreshIntervals() }) - setTimeout(() => { - let date + // Initial end + if (L_.FUTURES.endTime != null) { + L_.configData.time.initialend = L_.FUTURES.endTime + } + if ( + L_.configData.time.initialend != null && + L_.configData.time.initialend != 'now' + ) { + const dateStaged = new Date(L_.configData.time.initialend) + if (dateStaged == 'Invalid Date') { + TimeUI._initialEnd = new Date() + console.warn( + "Invalid 'Initial End Time' provided. Defaulting to 'now'." + ) + } else TimeUI._initialEnd = dateStaged + } else TimeUI._initialEnd = new Date() + + // Initial start + // Start 1 month ago + TimeUI._initialStart = new Date(TimeUI._initialEnd) + if (L_.FUTURES.startTime != null) { + L_.configData.time.initialstart = L_.FUTURES.startTime + } + if (L_.configData.time.initialstart == null) + TimeUI._initialStart.setUTCMonth( + TimeUI._initialStart.getUTCMonth() - 1 + ) + else { + const dateStaged = new Date(L_.configData.time.initialstart) + if (dateStaged === 'Invalid Date') { + TimeUI._initialStart.setUTCMonth( + TimeUI._initialStart.getUTCMonth() - 1 + ) + console.warn( + "Invalid 'Initial Start Time' provided. Defaulting to 1 month before the end time." + ) + } else if (dateStaged.getTime() > TimeUI._initialEnd.getTime()) { + TimeUI._initialStart.setUTCMonth( + TimeUI._initialStart.getUTCMonth() - 1 + ) + console.warn( + "'Initial Start Time' cannot be later than the end time. Defaulting to 1 month before the end time." + ) + } else TimeUI._initialStart = dateStaged + } - // Initial end - if ( - L_.configData.time.initialend != null && - L_.configData.time.initialend != 'now' - ) { - const dateStaged = new Date(L_.configData.time.initialend) - if (dateStaged == 'Invalid Date') { - date = new Date() - console.warn( - "Invalid 'Initial End Time' provided. Defaulting to 'now'." - ) - } else date = dateStaged - } else date = new Date() - const savedEndDate = new Date(date) + // Initialize the time control times, but don't trigger events + TimeUI.timeChange( + TimeUI._initialStart.toISOString(), + TimeUI._initialEnd.toISOString(), + null, + true + ) + }, + fina() { + let date + // Initial end + date = new Date(TimeUI._initialEnd) + const savedEndDate = new Date(date) - const offsetEndDate = new Date( - date.getTime() + date.getTimezoneOffset() * 60000 - ) - const parsedEnd = TimeUI.endTempus.dates.parseInput( - new Date(offsetEndDate) - ) - TimeUI.endTempus.dates.setValue(parsedEnd) + const offsetEndDate = new Date( + date.getTime() + date.getTimezoneOffset() * 60000 + ) + const parsedEnd = TimeUI.endTempus.dates.parseInput( + new Date(offsetEndDate) + ) + TimeUI.endTempus.dates.setValue(parsedEnd) - // Initial start - // Start 1 month ago - if (L_.configData.time.initialstart == null) + // Initial start + // Start 1 month ago + if (L_.configData.time.initialstart == null) + date.setUTCMonth(date.getUTCMonth() - 1) + else { + const dateStaged = new Date(L_.configData.time.initialstart) + if (dateStaged === 'Invalid Date') { date.setUTCMonth(date.getUTCMonth() - 1) - else { - const dateStaged = new Date(L_.configData.time.initialstart) - if (dateStaged == 'Invalid Date') { - date.setUTCMonth(date.getUTCMonth() - 1) - console.warn( - "Invalid 'Initial Start Time' provided. Defaulting to 1 month before the end time." - ) - } else if (dateStaged.getTime() > savedEndDate.getTime()) { - date.setUTCMonth(date.getUTCMonth() - 1) - console.warn( - "'Initial Start Time' cannot be later than the end time. Defaulting to 1 month before the end time." - ) - } else date = dateStaged - } + console.warn( + "Invalid 'Initial Start Time' provided. Defaulting to 1 month before the end time." + ) + } else if (dateStaged.getTime() > savedEndDate.getTime()) { + date.setUTCMonth(date.getUTCMonth() - 1) + console.warn( + "'Initial Start Time' cannot be later than the end time. Defaulting to 1 month before the end time." + ) + } else date = dateStaged + } + date = new Date(TimeUI._initialStart) - const offsetStartDate = new Date( - date.getTime() + date.getTimezoneOffset() * 60000 - ) - const parsedStart = TimeUI.startTempus.dates.parseInput( - new Date(offsetStartDate) - ) - TimeUI.startTempus.dates.setValue(parsedStart) + const offsetStartDate = new Date( + date.getTime() + date.getTimezoneOffset() * 60000 + ) + const parsedStart = TimeUI.startTempus.dates.parseInput( + new Date(offsetStartDate) + ) + TimeUI.startTempus.dates.setValue(parsedStart) - $('#mmgisTimeUIPlay').on('click', TimeUI.togglePlay) - $('#mmgisTimeUIPresent').on('click', TimeUI.toggleTimeNow) + $('#mmgisTimeUIPlay').on('click', TimeUI.togglePlay) + $('#mmgisTimeUIPresent').on('click', TimeUI.toggleTimeNow) - TimeUI._remakeTimeSlider() - TimeUI._setCurrentTime(true, savedEndDate) - }, 2000) + TimeUI._remakeTimeSlider() + TimeUI._setCurrentTime(true, savedEndDate) }, togglePlay() { if (TimeUI.play) { diff --git a/src/essence/Basics/Layers_/LayerCapturer.js b/src/essence/Basics/Layers_/LayerCapturer.js index 5511b864..74eac432 100644 --- a/src/essence/Basics/Layers_/LayerCapturer.js +++ b/src/essence/Basics/Layers_/LayerCapturer.js @@ -12,7 +12,10 @@ export const captureVector = (layerObj, options, cb) => { // If there is no url to a JSON file but the "controlled" option is checked in the layer config, // create the geoJSON layer with empty GeoJSON data - if (options.useEmptyGeoJSON || layerData.controlled) { + if ( + options.useEmptyGeoJSON || + (layerData.controlled && layerUrl.length === 0) + ) { cb(F_.getBaseGeoJSON()) return } diff --git a/src/essence/Basics/Layers_/Layers_.js b/src/essence/Basics/Layers_/Layers_.js index e60a6ba3..c88aef14 100644 --- a/src/essence/Basics/Layers_/Layers_.js +++ b/src/essence/Basics/Layers_/Layers_.js @@ -2529,7 +2529,7 @@ var L_ = { if (type === 'updateLayer' && layerName in L_.layersNamed) { // Update layer - await L_.TimeControl_.reloadLayer(layerName, true) + await L_.TimeControl_.reloadLayer(layerName, true, true) } else if (type === 'addLayer') { // Add layer await L_.Map_.makeLayer(L_.layersDataByName[layerName])