From 8dc6c331c4ec7ff0c68cfc536ce9a344d410f157 Mon Sep 17 00:00:00 2001 From: tariqksoliman Date: Thu, 17 Nov 2022 18:37:03 -0800 Subject: [PATCH 1/2] Configure global start end time, Partial local vector time filter --- config/js/config.js | 85 +++++++++++++++++--------- src/essence/Ancillary/TimeControl.js | 54 +++++++++------- src/essence/Ancillary/TimeUI.js | 44 ++++++++++++-- src/essence/Basics/Layers_/Layers_.js | 88 +++++++++++++++++++++++++++ views/configure.pug | 16 +++-- 5 files changed, 226 insertions(+), 61 deletions(-) diff --git a/config/js/config.js b/config/js/config.js index d7ee1c55..b0a550c0 100644 --- a/config/js/config.js +++ b/config/js/config.js @@ -499,28 +499,28 @@ function initialize() { $("#tab_look #look_pagename").val(cData.look.pagename); } $("#tab_look input").prop("checked", false); - if (cData.look && cData.look.minimalist != true) { + if (cData.look && cData.look.minimalist != false) { $("#tab_look #look_minimalist").prop("checked", true); } - if (cData.look && cData.look.topbar != true) { + if (cData.look && cData.look.topbar != false) { $("#tab_look #look_topbar").prop("checked", true); } - if (cData.look && cData.look.toolbar != true) { + if (cData.look && cData.look.toolbar != false) { $("#tab_look #look_toolbar").prop("checked", true); } - if (cData.look && cData.look.scalebar != true) { + if (cData.look && cData.look.scalebar != false) { $("#tab_look #look_scalebar").prop("checked", true); } - if (cData.look && cData.look.coordinates != true) { + if (cData.look && cData.look.coordinates != false) { $("#tab_look #look_coordinates").prop("checked", true); } - if (cData.look && cData.look.zoomcontrol != true) { + if (cData.look && cData.look.zoomcontrol != false) { $("#tab_look #look_zoomcontrol").prop("checked", true); } - if (cData.look && cData.look.graticule != true) { + if (cData.look && cData.look.graticule != false) { $("#tab_look #look_graticule").prop("checked", true); } - if (cData.look && cData.look.miscellaneous != true) { + if (cData.look && cData.look.miscellaneous != false) { $("#tab_look #look_miscellaneous").prop("checked", true); } @@ -620,6 +620,12 @@ function initialize() { $("#tab_time #time_format").val( cData.time ? cData.time.format : "%Y-%m-%dT%H:%M:%SZ" ); + $("#tab_time #time_initialstart").val( + cData.time ? cData.time.initialstart : "" + ); + $("#tab_time #time_initialend").val( + cData.time ? cData.time.initialend : "now" + ); //tools //uncheck all tools @@ -885,6 +891,8 @@ function makeLayerBarAndModal(d, level, options) { vtLayerSetStylesEl = "block", timeEl = "block", timeTypeEl = "block", + timeStartPropEl = "block", + timeEndPropEl = "block", timeFormatEl = "block", timeRefreshEl = "none", timeIncrementEl = "none", @@ -904,7 +912,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "none"; maxzEl = "none"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "none"; timeTypeEl = "none"; timeFormatEl = "none"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; + timeEl = "none"; timeTypeEl = "none"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "none"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "tile": @@ -917,7 +925,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "block"; maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "block"; bbEl = "block"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "block"; timeTypeEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; + timeEl = "block"; timeTypeEl = "block"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "vectortile": @@ -930,7 +938,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "block"; maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "block"; vtIdEl = "block"; vtKeyEl = "block"; vtLayerSetStylesEl = "block"; - timeEl = "block"; timeTypeEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "block"; + timeEl = "block"; timeTypeEl = "block"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "block"; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "data": @@ -943,7 +951,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "block"; maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "block"; xmlEl = "block"; bbEl = "block"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "block"; timeTypeEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; + timeEl = "block"; timeTypeEl = "block"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "query": @@ -956,7 +964,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "none"; maxzEl = "none"; strcolEl = "block"; filcolEl = "block"; weightEl = "block"; opacityEl = "block"; radiusEl = "block"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "none"; timeTypeEl = "none"; timeFormatEl = "none"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; + timeEl = "none"; timeTypeEl = "none"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "none"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; queryEndpointEl = "block"; queryTypeEl = "block"; break; case "vector": @@ -969,7 +977,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "none"; maxzEl = "block"; strcolEl = "block"; filcolEl = "block"; weightEl = "block"; opacityEl = "block"; radiusEl = "block"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "block"; timeTypeEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "block"; + timeEl = "block"; timeTypeEl = "block"; timeStartPropEl = "block"; timeEndPropEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "block"; break; case "model": nameEl = "block"; kindEl = "none"; typeEl = "block"; urlEl = "block"; demtileurlEl = "none"; demparserEl = "none"; controlledEl = "none"; @@ -981,7 +989,7 @@ function makeLayerBarAndModal(d, level, options) { maxnzEl = "none"; maxzEl = "none"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = "block"; timeTypeEl = "block"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; + timeEl = "block"; timeTypeEl = "block"; timeStartPropEl = "none"; timeEndPropEl = "none"; timeFormatEl = "block"; timeRefreshEl = "none"; timeIncrementEl = "none"; shapeEl = "none"; queryEndpointEl = "none"; queryTypeEl = "none"; break; default: @@ -1113,14 +1121,14 @@ function makeLayerBarAndModal(d, level, options) { } var timeGlobalSel = "", - timeIndividualSel = ""; + timeLocalSel = ""; if (typeof d.time != "undefined") { switch (d.time.type) { case "global": timeGlobalSel = "selected"; break; - case "individual": - timeIndividualSel = "selected"; + case "local": + timeLocalSel = "selected"; break; default: } @@ -1434,11 +1442,19 @@ function makeLayerBarAndModal(d, level, options) { "
" + "" + - "" + + "" + + "
" + + "
" + + "" + + "" + "
" + - "
" + + "
" + + "" + + "" + + "
" + + "
" + "" + "" + "
" + @@ -1660,7 +1676,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxnzEl = "block", maxzEl = "block", strcolEl = "block", filcolEl = "block", weightEl = "block", opacityEl = "block", radiusEl = "block", variableEl = "block", vtLayerEl = "none", vtIdEl = "none", vtKeyEl = "none", vtLayerSetStylesEl = "none", - timeEl = 'block', timeTypeEl = 'block', timeFormatEl = 'block', timeRefreshEl = 'none', timeIncrementEl = 'none', + timeEl = 'block', timeTypeEl = 'block', timeStartPropEl = 'block', timeEndPropEl = 'block', timeFormatEl = 'block', timeRefreshEl = 'none', timeIncrementEl = 'none', shapeEl = 'block', queryEndpointEl = "none", queryTypeEl = "none"; //Kind of a repeat of above =\ @@ -1675,7 +1691,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "none"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'none'; timeTypeEl = 'none'; timeFormatEl = 'none'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'none'; timeTypeEl = 'none'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'none'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'none'; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "tile": barColor = "rgb(119, 15, 189)"; @@ -1687,7 +1703,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "block"; bbEl = "block"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'block'; timeTypeEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'block'; timeTypeEl = 'block'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'none'; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "vectortile": barColor = "#bd0f8e"; @@ -1699,7 +1715,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "block"; vtIdEl = "block"; vtKeyEl = "block"; vtLayerSetStylesEl = "block"; - timeEl = 'block'; timeTypeEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'block'; timeTypeEl = 'block'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'block'; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "data": barColor = "rgb(189, 15, 50)"; @@ -1711,7 +1727,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "block"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "block"; xmlEl = "block"; bbEl = "block"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'block'; timeTypeEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'block'; timeTypeEl = 'block'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'none'; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "query": barColor = "#0fbd4d"; @@ -1723,7 +1739,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "none"; strcolEl = "block"; filcolEl = "block"; weightEl = "block"; opacityEl = "block"; radiusEl = "block"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'none'; timeTypeEl = 'none'; timeFormatEl = 'none'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'none'; timeTypeEl = 'none'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'none'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'none'; queryEndpointEl = "block"; queryTypeEl = "block"; break; case "vector": barColor = "rgb(15, 119, 189)"; @@ -1735,7 +1751,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "block"; strcolEl = "block"; filcolEl = "block"; weightEl = "block"; opacityEl = "block"; radiusEl = "block"; variableEl = "block"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'block'; timeTypeEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'block'; timeTypeEl = 'block'; timeStartPropEl = 'block'; timeEndPropEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'block'; queryEndpointEl = "none"; queryTypeEl = "none"; break; case "model": barColor = "rgb(189, 189, 15)"; @@ -1747,7 +1763,7 @@ function mmgisLinkModalsToLayersTypeChange(e) { maxzEl = "none"; strcolEl = "none"; filcolEl = "none"; weightEl = "none"; opacityEl = "none"; radiusEl = "none"; variableEl = "none"; xmlEl = "none"; bbEl = "none"; vtLayerEl = "none"; vtIdEl = "none"; vtKeyEl = "none"; vtLayerSetStylesEl = "none"; - timeEl = 'block'; timeTypeEl = 'block'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; + timeEl = 'block'; timeTypeEl = 'block'; timeStartPropEl = 'none'; timeEndPropEl = 'none'; timeFormatEl = 'block'; timeRefreshEl = 'none'; timeIncrementEl = 'none'; shapeEl = 'none'; queryEndpointEl = "none"; queryTypeEl = "none"; break; default: @@ -1802,6 +1818,8 @@ function mmgisLinkModalsToLayersTypeChange(e) { mainThis.find("#vtLayerSetStylesEl").css("display", vtLayerSetStylesEl); mainThis.find("#timeEl").css("display", timeEl); mainThis.find("#timeTypeEl").css("display", timeTypeEl); + mainThis.find("#timeStartPropEl").css("display", timeStartPropEl); + mainThis.find("#timeEndPropEl").css("display", timeEndPropEl); mainThis.find("#timeFormatEl").css("display", timeFormatEl); mainThis.find("#timeRefreshEl").css("display", timeRefreshEl); mainThis.find("#timeIncrementEl").css("display", timeIncrementEl); @@ -2059,6 +2077,9 @@ function save() { json.time.visible = false; } json.time.format = $("#tab_time #time_format").val(); + json.time.initialstart = $("#tab_time #time_initialstart").val(); + json.time.initialend = $("#tab_time #time_initialend").val(); + //Tools for (var i = 0; i < tData.length; i++) { if ($("#tab_tools #tools_" + tData[i].name).prop("checked")) { @@ -2185,6 +2206,8 @@ function save() { .find("#timeTypeEl select option:selected") .text() .toLowerCase(); + var modalTimeStartProp = modal.find("#timeStartPropEl input").val(); + var modalTimeEndProp = modal.find("#timeEndPropEl input").val(); var modalTimeFormat = modal.find("#timeFormatEl input").val(); var modalShape = modal.find("#shapeEl select option:selected").val(); @@ -2328,12 +2351,14 @@ function save() { // time properties layerObject.time = {}; layerObject.time.enabled = modalTime; // static or timed - layerObject.time.type = modalTimeType; // 'global or individual' + layerObject.time.type = modalTimeType; // 'global or local' layerObject.time.isRelative = true; // absolute or relative layerObject.time.current = new Date().toISOString().split(".")[0] + "Z"; // initial time layerObject.time.start = ""; // initial start layerObject.time.end = ""; // initial end + layerObject.time.startProp = modalTimeStartProp; + layerObject.time.endProp = modalTimeEndProp; layerObject.time.format = modalTimeFormat; // time string format layerObject.time.refresh = "1 hours"; // refresh when the layer becomes stale layerObject.time.increment = "5 minutes"; // time bar steps diff --git a/src/essence/Ancillary/TimeControl.js b/src/essence/Ancillary/TimeControl.js index 0b3ccc58..00858bf8 100644 --- a/src/essence/Ancillary/TimeControl.js +++ b/src/essence/Ancillary/TimeControl.js @@ -197,24 +197,38 @@ var TimeControl = { } else { // replace start/endtime keywords if (layer.time && layer.time.enabled == true) { - var originalUrl = layer.url - layer.url = layer.url - .replace( - /{starttime}/g, - layerTimeFormat(Date.parse(layer.time.start)) - ) - .replace( - /{endtime}/g, - layerTimeFormat(Date.parse(layer.time.end)) - ) + if (layer.time.type === 'global') { + var originalUrl = layer.url + layer.url = layer.url + .replace( + /{starttime}/g, + layerTimeFormat(Date.parse(layer.time.start)) + ) + .replace( + /{endtime}/g, + layerTimeFormat(Date.parse(layer.time.end)) + ) + } } - // refresh map - if (L_.toggledArray[layer.name] || evenIfOff) { - await Map_.refreshLayer(layer) + if ( + layer.type === 'vector' && + layer.time.type === 'local' && + layer.time.endProp != null + ) { + 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) + } + // put start/endtime keywords back + if (layer.time && layer.time.enabled == true) + layer.url = originalUrl } - // put start/endtime keywords back - if (layer.time && layer.time.enabled == true) - layer.url = originalUrl } return true @@ -235,11 +249,7 @@ var TimeControl = { var updatedLayers = [] for (let layerName in L_.layersNamed) { const layer = L_.layersNamed[layerName] - if ( - layer.time && - layer.time.enabled == true && - layer.time.type == 'global' - ) { + 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( @@ -249,7 +259,7 @@ var TimeControl = { layer.time.end ) updatedLayers.push(layer.name) - if (layer.type == 'tile') { + if (layer.type === 'tile') { TimeControl.setLayerWmsParams(layer) } } diff --git a/src/essence/Ancillary/TimeUI.js b/src/essence/Ancillary/TimeUI.js index b9a34468..11fd1391 100644 --- a/src/essence/Ancillary/TimeUI.js +++ b/src/essence/Ancillary/TimeUI.js @@ -230,7 +230,23 @@ const TimeUI = { }) setTimeout(() => { - let date = new Date() + let date + + // 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) + const offsetEndDate = new Date( date.getTime() + date.getTimezoneOffset() * 60000 ) @@ -238,8 +254,26 @@ const TimeUI = { new Date(offsetEndDate) ) TimeUI.endTempus.dates.setValue(parsedEnd) + + // Initial start // Start 1 month ago - date.setUTCMonth(date.getUTCMonth() - 1) + 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) + 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 + } + const offsetStartDate = new Date( date.getTime() + date.getTimezoneOffset() * 60000 ) @@ -256,7 +290,7 @@ const TimeUI = { ) TimeUI._remakeTimeSlider() - TimeUI._setCurrentTime(true) + TimeUI._setCurrentTime(true, savedEndDate) }, 2000) }, toggleTimeNow(force) { @@ -407,9 +441,9 @@ const TimeUI = { ) }) }, - _setCurrentTime(force) { + _setCurrentTime(force, forceDate) { if (TimeUI.now === true || force === true) { - let date = new Date() + let date = forceDate || new Date() const offsetNowDate = new Date( date.getTime() + date.getTimezoneOffset() * 60000 ) diff --git a/src/essence/Basics/Layers_/Layers_.js b/src/essence/Basics/Layers_/Layers_.js index fff8f3e4..e60a6ba3 100644 --- a/src/essence/Basics/Layers_/Layers_.js +++ b/src/essence/Basics/Layers_/Layers_.js @@ -65,6 +65,7 @@ var L_ = { layerFilters: {}, //Name -> parent layersParent: {}, + _localTimeFilterCache: {}, //FUTURES FUTURES: { site: null, @@ -128,6 +129,7 @@ var L_ = { L_.opacityArray = {} L_.layerFilters = {} L_.layersParent = {} + L_._localTimeFilterCache = {} L_.FUTURES = { site: null, mapView: null, @@ -2577,6 +2579,92 @@ var L_ = { updateQueueLayers: async function () { await L_.updateLayersHelper(L_.addLayerQueue) }, + // Limits a Local, Time-Enabled, Prop-set, vector layer to a range of time + // start and end are unix timestamps + timeFilterVectorLayer: function (layerName, start, end) { + let reset = false + if (start === false) reset = true + + start = start || 0 + + const layerConfig = L_.layersNamed[layerName] + const layer = L_.layersGroup[layerName] + + if ( + layerConfig.type === 'vector' && + layerConfig.time.type === 'local' && + layerConfig.time.endProp != null && + layer != false && + layer._sourceGeoJSON != null + ) { + const filteredGeoJSON = JSON.parse( + JSON.stringify( + L_._localTimeFilterCache[layer.name] || layer._sourceGeoJSON + ) + ) + if (L_._localTimeFilterCache[layer.name] == null) + L_._localTimeFilterCache[layer.name] = JSON.parse( + JSON.stringify(filteredGeoJSON) + ) + + if (reset === false) { + filteredGeoJSON.features = filteredGeoJSON.features.filter( + (f) => { + let startTimeValue = false + if (layerConfig.time.startProp) + startTimeValue = F_.getIn( + f.properties, + layerConfig.time.startProp, + 0 + ) + let endTimeValue = false + if (layerConfig.time.endProp) + endTimeValue = F_.getIn( + f.properties, + layerConfig.time.endProp, + false + ) + + // No prop, won't show + if (endTimeValue === false) return false + + if (startTimeValue === false) { + //Single Point in time, just compare end times + let endDate = new Date(endTimeValue) + if (endDate == 'Invalid Date') return false + + endDate = endDate.getTime() + if (endDate <= end && endDate >= start) return true + return false + } else { + // Then we have a range + let startDate = new Date(startTimeValue) + let endDate = new Date(endTimeValue) + + // Bad prop value, won't show + if ( + startDate == 'Invalid Date' || + endDate == 'Invalid Date' + ) + return false + + startDate = startDate.getTime() + endDate = endDate.getTime() + + if (end < startDate) return false + if (start > endDate) return false + + return true + } + } + ) + } + + // Update layer + L_.clearVectorLayer(layerName) + L_.updateVectorLayer(layerName, filteredGeoJSON) + } + }, } //Takes in a configData object and does a depth-first search through its diff --git a/views/configure.pug b/views/configure.pug index 6f2f18d0..fb3569db 100644 --- a/views/configure.pug +++ b/views/configure.pug @@ -458,17 +458,25 @@ script(type='text/javascript' src='src/pre/RefreshAuth.js') ul#tab_time_rows li.row.title .col.s2.push-s2 User Interface - li.row.checkboxRow.col.s2.push-s2 - p + li.row + #time_enabledEl.input-field.col.s2.push-s2 input#time_enabled.filled-in.checkbox-color(type='checkbox') label(for='time_enabled' style='color: black;') Enabled - p + #time_visibledEl.input-field.col.s2.push-s2 input#time_visible.filled-in.checkbox-color(type='checkbox') label(for='time_visible' style='color: black;') Visible + li.row.title + .col.s2.push-s2 Settings li.row - #time_format_row.input-field.col.s4.push-s2 + #time_format_row.input-field.col.s3.push-s2 input#time_format.validate(type='text' value='%Y-%m-%dT%H:%M:%SZ') label(for='time_format') Time Format + #time_initialstartEl.input-field.col.s3.push-s2 + input#time_initialstart.validate(type='text' value='' title="Parsable time (defaults to a month before the end time)") + label(for='time_initialstart') Initial Start Time + #time_initialendEl.input-field.col.s3.push-s2 + input#time_initialend.validate(type='text' value='now' title="Parsable time (default to 'now')") + label(for='time_initialend') Initial End Time #tab_tools.col.s12 a.helpFromDocs(href='docs/?page=Tools_Tab' target='__blank' rel='noopener') From e84e29ee32de37470cea2f5fbaa31fcddbf5e44c Mon Sep 17 00:00:00 2001 From: Tariq Soliman Date: Mon, 21 Nov 2022 15:06:42 -0800 Subject: [PATCH 2/2] #279 Time Improvements --- config/js/config.js | 16 +-- src/essence/Ancillary/TimeControl.js | 2 + src/essence/Ancillary/TimeUI.css | 34 ++++- src/essence/Ancillary/TimeUI.js | 181 ++++++++++++++++-------- src/essence/Tools/Layers/LayersTool.css | 5 +- src/essence/Tools/Layers/LayersTool.js | 6 +- 6 files changed, 165 insertions(+), 79 deletions(-) diff --git a/config/js/config.js b/config/js/config.js index b0a550c0..b670357f 100644 --- a/config/js/config.js +++ b/config/js/config.js @@ -1247,7 +1247,7 @@ function makeLayerBarAndModal(d, level, options) { "

" + "

" + - "
" + + "
" + "" + "" + "
" + @@ -1274,6 +1274,13 @@ function makeLayerBarAndModal(d, level, options) { "" + "" + "
" + + "
" + + "" + + "" + + "
" + "
" + "
" + @@ -1403,13 +1410,6 @@ function makeLayerBarAndModal(d, level, options) { "" + "" + "
" + - "
" + - "" + - "" + - "
" + "
" + "" + "" + diff --git a/src/essence/Ancillary/TimeControl.js b/src/essence/Ancillary/TimeControl.js index 00858bf8..90f42243 100644 --- a/src/essence/Ancillary/TimeControl.js +++ b/src/essence/Ancillary/TimeControl.js @@ -182,6 +182,8 @@ var TimeControl = { 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 diff --git a/src/essence/Ancillary/TimeUI.css b/src/essence/Ancillary/TimeUI.css index d3009b75..b13fa6ea 100644 --- a/src/essence/Ancillary/TimeUI.css +++ b/src/essence/Ancillary/TimeUI.css @@ -6,7 +6,7 @@ bottom: -40px; left: 0px; margin: 0; - z-index: 777; + z-index: 77777; font-size: 14px; opacity: 0; pointer-events: none; @@ -51,6 +51,7 @@ flex: 1; display: flex; justify-content: space-between; + position: relative; } #mmgisTimeUI input { @@ -92,9 +93,16 @@ display: flex; flex: 1; position: relative; - /*cursor: ew-resize;*/ user-select: none; } +#mmgisTimeUITimelineFixedStartSlider { + position: absolute; + left: 200px; + width: 4px; + height: 40px; + background: var(--color-h); + pointer-events: none; +} #mmgisTimeUITimelineSlider { width: 100%; } @@ -105,12 +113,14 @@ } #mmgisTimeUITimelineSlider .rangeSlider { height: 2px; - margin: 20px 6px 20px 8px; + margin: 20px 0px 20px 4px; background: var(--color-a2); } #mmgisTimeUITimelineSlider .rangeHandle:first-child { display: none; pointer-events: none; + width: 0px; + padding: 0px; } #mmgisTimeUITimelineSlider .rangeHandle { top: 0px; @@ -162,7 +172,7 @@ font-size: 12px; line-height: 22px; text-transform: capitalize; - color: var(--color-a7); + color: var(--color-a5); } #mmgisTimeUIStartWrapper { } @@ -210,3 +220,19 @@ height: 100%; transition: margin-top 0.2s ease-in-out; } + +#mmgisTimeUIRateDropdown { + width: 60px; +} +#mmgisTimeUIRateDropdown .dropy__title i { + color: var(--color-a5); +} +#mmgisTimeUIRateDropdown .dropy__title span { + font-size: 12px; + padding: 14px 0px 14px 12px; + color: var(--color-a5); +} +#mmgisTimeUIRateDropdown li a { + font-size: 13px; + padding: 5px 8px; +} diff --git a/src/essence/Ancillary/TimeUI.js b/src/essence/Ancillary/TimeUI.js index 11fd1391..5b01cdcf 100644 --- a/src/essence/Ancillary/TimeUI.js +++ b/src/essence/Ancillary/TimeUI.js @@ -11,6 +11,7 @@ import F_ from '../Basics/Formulae_/Formulae_' import L_ from '../Basics/Layers_/Layers_' import calls from '../../pre/calls' import tippy from 'tippy.js' +import Dropy from '../../external/Dropy/dropy' import { TempusDominus, Namespace } from '@eonasdan/tempus-dominus' import '@eonasdan/tempus-dominus/dist/css/tempus-dominus.css' @@ -36,41 +37,41 @@ const TimeUI = { _endTimestamp: null, _timeSliderTimestamp: null, now: false, - intervalIndex: 3, - intervalKeys: [ + numFrames: 24, + intervalIndex: 6, + intervalValues: [100, 250, 500, 1000, 2000, 3000, 4000, 5000, 10000, 20000], + intervalNames: [ + '.1s', + '.25s', + '0.5s', '1s', '2s', + '3s', + '4s', '5s', '10s', '20s', - '30s', - '1m', - '2m', - '5m', - '10m', - ], - intervalValues: [ - 1000, - 2000, - 5000, - 10000, - 20000, - 30000, - 60000, - 60000 * 2, - 60000 * 5, - 60000 * 10, ], init: function (timeChange) { TimeUI.timeChange = timeChange // prettier-ignore const markup = [ `
`, + `
`, + `
`, + ``, + `
`, + `
`, + `
`, + ``, + `
`, + `
`, `
`, `
`, `Start Time`, ``, `
`, + `
`, `
`, `
`, `
`, @@ -82,12 +83,8 @@ const TimeUI = { `
`, `
`, `
`, - `
`, - ``, - `
`, - `
`, - `
`, - `
${TimeUI.intervalKeys[TimeUI.intervalIndex]}
`, + `
`, + ``, `
`, `
`, `
`, @@ -201,34 +198,34 @@ const TimeUI = { } }) - // Interval Cycler - $('#mmgisTimeUIIntervalCycler').on('click', function () { - TimeUI.intervalIndex++ - if (TimeUI.intervalIndex >= TimeUI.intervalKeys.length) - TimeUI.intervalIndex = 0 - $('#mmgisTimeUIIntervalCycler').text( - TimeUI.intervalKeys[TimeUI.intervalIndex] - ) - - clearInterval(TimeUI.loopTime) - TimeUI.loopTime = setInterval( - TimeUI._setCurrentTime, - TimeUI.intervalValues[TimeUI.intervalIndex] - ) - }) - // tippy tippy('#mmgisTimeUIPlay', { - content: 'Play (Drag slider to End Time for Current Time too)', + content: 'Play', placement: 'top', theme: 'blue', }) - tippy('#mmgisTimeUIIntervalCycler', { - content: 'Play Interval', + tippy('#mmgisTimeUIRate', { + content: 'Frame Duration', + placement: 'top', + theme: 'blue', + }) + tippy('#mmgisTimeUIPresent', { + content: 'Present', placement: 'top', theme: 'blue', }) + $('#mmgisTimeUIRateDropdown').html( + Dropy.construct(TimeUI.intervalNames, null, TimeUI.intervalIndex, { + openUp: true, + dark: true, + }) + ) + Dropy.init($('#mmgisTimeUIRateDropdown'), function (idx) { + TimeUI.intervalIndex = idx + TimeUI._refreshIntervals() + }) + setTimeout(() => { let date @@ -282,33 +279,84 @@ const TimeUI = { ) TimeUI.startTempus.dates.setValue(parsedStart) - $('#mmgisTimeUIPlay').on('click', TimeUI.toggleTimeNow) + $('#mmgisTimeUIPlay').on('click', TimeUI.togglePlay) + $('#mmgisTimeUIPresent').on('click', TimeUI.toggleTimeNow) - TimeUI.loopTime = setInterval( + TimeUI._remakeTimeSlider() + TimeUI._setCurrentTime(true, savedEndDate) + }, 2000) + }, + togglePlay() { + if (TimeUI.play) { + $('#mmgisTimeUIPlay') + .css('background', '') + .css('color', 'var(--color-a4)') + TimeUI.play = false + } else { + $('#mmgisTimeUIPlay') + .css('background', 'var(--color-p4)') + .css('color', 'white') + TimeUI.play = true + TimeUI.now = false + $('#mmgisTimeUIPresent') + .css('background', '') + .css('color', 'var(--color-a4)') + $('#mmgisTimeUIEnd').css('pointer-events', 'inherit') + $('#mmgisTimeUIEndWrapper').css('cursor', 'inherit') + } + TimeUI._refreshIntervals() + }, + _refreshIntervals() { + clearInterval(TimeUI.playInterval) + if (TimeUI.play) { + TimeUI.playInterval = setInterval( + TimeUI._loopTime, + TimeUI.intervalValues[TimeUI.intervalIndex] + ) + } + + clearInterval(TimeUI.presentTimeInterval) + if (TimeUI.now) { + TimeUI.presentTimeInterval = setInterval( TimeUI._setCurrentTime, TimeUI.intervalValues[TimeUI.intervalIndex] ) + } + }, + _loopTime() { + const start = TimeUI._startTimestamp + const end = TimeUI._endTimestamp + const current = TimeUI.getCurrentTimestamp() - TimeUI._remakeTimeSlider() - TimeUI._setCurrentTime(true, savedEndDate) - }, 2000) + let next = (end - start) / TimeUI.numFrames + current + if (next > end) next = end + if (current === end) next = start + + TimeUI.setCurrentTime(next) + TimeUI._remakeTimeSlider(true) }, toggleTimeNow(force) { if ((!TimeUI.now && typeof force != 'boolean') || force === true) { - $('#mmgisTimeUIEnd').css('pointer-events', 'none') - $('#mmgisTimeUIEndWrapper').css('cursor', 'not-allowed') - $('#mmgisTimeUIPlay') + $('#mmgisTimeUIPresent') .css('background', 'var(--color-p4)') .css('color', 'white') + $('#mmgisTimeUIEnd').css('pointer-events', 'none') + $('#mmgisTimeUIEndWrapper').css('cursor', 'not-allowed') TimeUI.now = true - } else { - $('#mmgisTimeUIEnd').css('pointer-events', 'inherit') - $('#mmgisTimeUIEndWrapper').css('cursor', 'inherit') + TimeUI.play = false $('#mmgisTimeUIPlay') .css('background', '') .css('color', 'var(--color-a4)') + } else { + clearInterval(TimeUI.presentTimeInterval) + $('#mmgisTimeUIPresent') + .css('background', '') + .css('color', 'var(--color-a4)') + $('#mmgisTimeUIEnd').css('pointer-events', 'inherit') + $('#mmgisTimeUIEndWrapper').css('cursor', 'inherit') TimeUI.now = false } + TimeUI._refreshIntervals() }, _remakeTimeSlider(ignoreHistogram) { if (TimeUI.timeSlider) { @@ -331,7 +379,7 @@ const TimeUI = { damping: 0.5, }, handleFormatter: (v) => { - return moment.utc(v).format(FORMAT) + return moment.utc(TimeUI.removeOffset(v)).format(FORMAT) }, }, }) @@ -349,7 +397,7 @@ const TimeUI = { ) }) TimeUI.timeSlider.$on('stop', (e) => { - TimeUI.setCurrentTime(new Date(e.detail.value).toISOString()) + TimeUI.setCurrentTime(new Date(e.detail.value).toISOString(), false) }) if ($('#toggleTimeUI').hasClass('active') && ignoreHistogram !== true) @@ -450,6 +498,8 @@ const TimeUI = { const parsedNow = TimeUI.endTempus.dates.parseInput( new Date(offsetNowDate) ) + TimeUI.setCurrentTime(parsedNow) + TimeUI._remakeTimeSlider(true) TimeUI.endTempus.dates.setValue(parsedNow) } }, @@ -529,11 +579,18 @@ const TimeUI = { if (disableChange != true) TimeUI.change() } }, - setCurrentTime(ISOString, disableChange) { - const timestamp = Date.parse(ISOString) + setCurrentTime(ISOString, disableChange, dontRemoveOffset) { + const timestamp = + typeof ISOString === 'string' ? Date.parse(ISOString) : ISOString TimeUI._timeSliderTimestamp = timestamp $('#mmgisTimeUICurrentTime').text( - moment.utc(TimeUI.getCurrentTimestamp(true)).format(FORMAT) + moment + .utc( + TimeUI.getCurrentTimestamp( + dontRemoveOffset === true ? false : true + ) + ) + .format(FORMAT) ) if (disableChange != true) TimeUI.change() }, @@ -558,8 +615,8 @@ const TimeUI = { const timelineElm = $('#mmgisTimeUITimelineInner') timelineElm.empty() - const s = TimeUI._startTimestamp - const e = TimeUI._endTimestamp + const s = TimeUI.removeOffset(TimeUI._startTimestamp) + const e = TimeUI.removeOffset(TimeUI._endTimestamp) if (e == null || s == null) return const dif = e - s diff --git a/src/essence/Tools/Layers/LayersTool.css b/src/essence/Tools/Layers/LayersTool.css index 3887c7df..fb626f34 100644 --- a/src/essence/Tools/Layers/LayersTool.css +++ b/src/essence/Tools/Layers/LayersTool.css @@ -279,7 +279,8 @@ #layersTool .reset, #layersTool .layerDownload, #layersTool .gears, -#layersTool .LayersToolInfo { +#layersTool .LayersToolInfo, +#layersTool .time { width: 30px; height: 100%; text-align: center; @@ -421,7 +422,7 @@ } #layersTool .settings .checkbox { border: 1px solid var(--color-a4); -} +} #layersTool .checkbox.on { background: var(--color-a7); diff --git a/src/essence/Tools/Layers/LayersTool.js b/src/essence/Tools/Layers/LayersTool.js index ccbd96da..5c9d5e3d 100644 --- a/src/essence/Tools/Layers/LayersTool.js +++ b/src/essence/Tools/Layers/LayersTool.js @@ -463,15 +463,15 @@ function interfaceWithMMGIS(fromInit) { (layerExport != '') ? ['
', '', '
'].join('\n') : '', - (timeDisplay != '') ? ['
', - '', - '
'].join('\n') : '', '
', '', '
', '
', '', '
', + (timeDisplay != '') ? ['
', + '', + '
'].join('\n') : '', '
', '
', layerExport,