diff --git a/API/Backend/Utils/routes/utils.js b/API/Backend/Utils/routes/utils.js
index 04051eab..065812c3 100644
--- a/API/Backend/Utils/routes/utils.js
+++ b/API/Backend/Utils/routes/utils.js
@@ -87,7 +87,8 @@ router.get("/queryTilesetTimes", function (req, res, next) {
const split = name.split("Z-");
let t = split.shift();
const n = split.join("");
- t = t.replace(/_/g, ":") + "Z";
+ t = t.replace(/_/g, ":");
+ if (t[t.length - 1] !== "Z") t += "Z";
dirStore[relUrlSplit[0]].dirs.push({ t: t, n: n });
});
diff --git a/run/init-db.js b/run/init-db.js
index c837103c..e380d8fc 100644
--- a/run/init-db.js
+++ b/run/init-db.js
@@ -39,6 +39,7 @@ async function initializeDatabase() {
"connection"
);
keepGoing();
+ return null;
})
.catch((err) => {
logger(
@@ -47,6 +48,7 @@ async function initializeDatabase() {
"connection"
);
keepGoing();
+ return null;
});
async function keepGoing() {
@@ -76,6 +78,7 @@ async function initializeDatabase() {
.then(() => {
logger("info", `Created POSTGIS extension.`, "connection");
resolve();
+ return null;
})
.catch((err) => {
logger(
@@ -85,6 +88,7 @@ async function initializeDatabase() {
);
resolve();
+ return null;
});
})
.catch((err) => {
@@ -96,7 +100,9 @@ async function initializeDatabase() {
err
);
reject();
+ return null;
});
}
+ return null;
});
}
diff --git a/src/essence/Ancillary/Coordinates.js b/src/essence/Ancillary/Coordinates.js
index 2fb4701a..3383c6ce 100644
--- a/src/essence/Ancillary/Coordinates.js
+++ b/src/essence/Ancillary/Coordinates.js
@@ -765,6 +765,8 @@ function toggleTimeUI() {
const newBottom = active ? 0 : 40
const timeBottom = active ? -40 : 0
+ Map_.map._fadeAnimated = active
+
$('#CoordinatesDiv').css({
bottom: newBottom + (UserInterface.pxIsTools || 0) + 'px',
})
diff --git a/src/essence/Ancillary/TimeControl.js b/src/essence/Ancillary/TimeControl.js
index 01f0a01f..bdb2f62e 100644
--- a/src/essence/Ancillary/TimeControl.js
+++ b/src/essence/Ancillary/TimeControl.js
@@ -37,12 +37,9 @@ var TimeControl = {
return
}
- TimeControl.timeUI = TimeUI.init(timeInputChange)
+ TimeControl.timeUI = TimeUI.init(timeInputChange, TimeControl.enabled)
//updateTime()
- if (L_.configData.time.visible == false) {
- TimeControl.toggleTimeUI(false)
- }
initLayerTimes()
initLayerDataTimes()
@@ -51,14 +48,6 @@ var TimeControl = {
if ((TimeControl.enabled = true && TimeControl.timeUI != null))
TimeControl.timeUI.fina()
},
- toggleTimeUI: function (isOn) {
- d3.select('#timeUI').style('visibility', function () {
- if (!isOn) $('#toggleTimeUI').click()
-
- return isOn === true ? 'visible' : 'hidden'
- })
- return isOn
- },
setTime: function (
startTime,
endTime,
@@ -173,8 +162,7 @@ var TimeControl = {
}
if (L_.toggledArray[layer.name] || evenIfOff) {
- L_.toggleLayer(layer)
- L_.toggleLayer(layer)
+ L_.layersGroup[layer.name].refresh()
}
} else {
var originalUrl = layer.url
diff --git a/src/essence/Ancillary/TimeUI.css b/src/essence/Ancillary/TimeUI.css
index b13fa6ea..28d9e815 100644
--- a/src/essence/Ancillary/TimeUI.css
+++ b/src/essence/Ancillary/TimeUI.css
@@ -1,5 +1,5 @@
#timeUI {
- background: rgba(29, 31, 32, 0.65);
+ background: rgba(15, 16, 16, 0.6);
height: 40px;
width: 100%;
position: absolute;
@@ -86,7 +86,7 @@
font-size: 11px;
text-transform: uppercase;
pointer-events: none;
- color: var(--color-a5);
+ color: var(--color-h);
}
#mmgisTimeUITimeline {
@@ -95,14 +95,6 @@
position: relative;
user-select: none;
}
-#mmgisTimeUITimelineFixedStartSlider {
- position: absolute;
- left: 200px;
- width: 4px;
- height: 40px;
- background: var(--color-h);
- pointer-events: none;
-}
#mmgisTimeUITimelineSlider {
width: 100%;
}
@@ -110,18 +102,13 @@
background: var(--color-h);
height: 2px;
opacity: 0.75;
+ pointer-events: none;
}
#mmgisTimeUITimelineSlider .rangeSlider {
height: 2px;
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;
width: 20px;
@@ -144,6 +131,11 @@
margin: 0px 7px;
}
+#mmgisTimeUITimelineInner {
+ height: 100%;
+ width: 100%;
+ position: absolute;
+}
.mmgisTimeUITick {
position: absolute;
top: 0;
@@ -156,14 +148,13 @@
}
.mmgisTimeUITick .mmgisTimeUITickBig {
height: 40px;
- background: var(--color-a4);
+ background: var(--color-a3);
width: 1px;
}
.mmgisTimeUITick .mmgisTimeUITickLabel {
height: 40px;
line-height: 20px;
padding-left: 4px;
- text-transform: uppercase;
font-size: 13px;
color: var(--color-a7);
}
@@ -215,23 +206,40 @@
pointer-events: none;
}
#mmgisTimeUITimelineHisto > div {
- background: var(--color-c2);
- opacity: 0.38;
+ background: #056e94;
height: 100%;
transition: margin-top 0.2s ease-in-out;
}
+#mmgisTimeUIModeDropdown {
+ background: var(--color-a-5);
+ width: 80px;
+}
+#mmgisTimeUIStepDropdown {
+ width: 80px;
+}
#mmgisTimeUIRateDropdown {
width: 60px;
}
+#mmgisTimeUIModeDropdown .dropy__title i,
+#mmgisTimeUIStepDropdown .dropy__title i,
#mmgisTimeUIRateDropdown .dropy__title i {
color: var(--color-a5);
}
+#mmgisTimeUIModeDropdown .dropy__title span,
+#mmgisTimeUIStepDropdown .dropy__title span,
#mmgisTimeUIRateDropdown .dropy__title span {
font-size: 12px;
padding: 14px 0px 14px 12px;
color: var(--color-a5);
}
+#mmgisTimeUIModeDropdown .dropy__title span {
+ color: var(--color-a5);
+ text-transform: uppercase;
+}
+
+#mmgisTimeUIModeDropdown li a,
+#mmgisTimeUIStepDropdown li a,
#mmgisTimeUIRateDropdown li a {
font-size: 13px;
padding: 5px 8px;
diff --git a/src/essence/Ancillary/TimeUI.js b/src/essence/Ancillary/TimeUI.js
index fcc9eff2..59d00947 100644
--- a/src/essence/Ancillary/TimeUI.js
+++ b/src/essence/Ancillary/TimeUI.js
@@ -8,6 +8,7 @@ import $ from 'jquery'
import * as d3 from 'd3'
import * as moment from 'moment'
import F_ from '../Basics/Formulae_/Formulae_'
+import Map_ from '../Basics/Map_/Map_'
import L_ from '../Basics/Layers_/Layers_'
import calls from '../../pre/calls'
import tippy from 'tippy.js'
@@ -21,8 +22,10 @@ import './TimeUI.css'
const FORMAT = 'MM/DD/yyyy, hh:mm:ss A'
const MS = {
+ decade: 315576000000,
year: 31557600000,
month: 2629800000,
+ week: 604800000,
day: 86400000,
hour: 3600000,
minute: 60000,
@@ -36,6 +39,8 @@ const TimeUI = {
_startTimestamp: null,
_endTimestamp: null,
_timeSliderTimestamp: null,
+ _timelineStartTimestamp: null,
+ _timelineEndTimestamp: null,
now: false,
numFrames: 24,
intervalIndex: 6,
@@ -52,18 +57,31 @@ const TimeUI = {
'10s',
'20s',
],
+ stepIndex: 3,
+ modes: ['Range', 'Point'],
+ modeIndex: 0,
_initialStart: null,
_initialEnd: null,
- init: function (timeChange) {
+ _tickTippies: [],
+ init: function (timeChange, enabled) {
TimeUI.timeChange = timeChange
+ TimeUI.enabled = enabled
// prettier-ignore
const markup = [
`
`,
`
`,
+ `
`,
+ `
`,
`
`,
``,
`
`,
`
`,
+ `
`,
+ `
`,
`
`,
@@ -73,14 +91,13 @@ const TimeUI = {
`
Start Time`,
`
`,
`
`,
- `
`,
`
`,
`
`,
- `End Time`,
+ `Active Time`,
``,
`
`,
`
`,
@@ -89,10 +106,12 @@ const TimeUI = {
``,
``,
``,
+ /*
``,
+ */
``
].join('\n')
@@ -107,6 +126,76 @@ const TimeUI = {
},
getElement: function () {},
attachEvents: function (timeChange) {
+ // Timeline pan and zoom
+ // zoom
+ $('#mmgisTimeUITimelineInner').on('mousewheel', function (e) {
+ if (TimeUI.play) return
+ const x = e.originalEvent.offsetX
+ const width = document
+ .getElementById('mmgisTimeUITimelineInner')
+ .getBoundingClientRect().width
+ // As opposed to per-cent
+ const perun = x / width
+ const direction = e.originalEvent.deltaY > 0 ? 1 : -1
+ const AMOUNT = 0.2
+ const dif =
+ TimeUI._timelineEndTimestamp - TimeUI._timelineStartTimestamp
+ const maxChangeAmount = dif * AMOUNT
+
+ const nextStart =
+ TimeUI._timelineStartTimestamp -
+ 0 -
+ perun * maxChangeAmount * direction
+ const nextEnd =
+ TimeUI._timelineEndTimestamp -
+ 0 +
+ (1 - perun) * maxChangeAmount * direction
+
+ TimeUI._drawTimeLine(nextStart, nextEnd)
+
+ clearTimeout(TimeUI._zoomHistoTimeout)
+ $('#mmgisTimeUITimelineHisto').empty()
+ TimeUI._zoomHistoTimeout = setTimeout(() => {
+ TimeUI._makeHistogram()
+ }, 3000)
+ })
+
+ // pan
+ $('#mmgisTimeUITimelineInner').on('mousedown', function () {
+ if (TimeUI.play) {
+ TimeUI._timelineDragging = false
+ return
+ }
+ TimeUI._timelineDragging = true
+ $('#mmgisTimeUITimelineSlider').css({ pointerEvents: 'none' })
+ $('#mmgisTimeUITimelineInner').on('mousemove', TimeUI._timelineDrag)
+ })
+ $('#mmgisTimeUITimelineInner').on('mouseout', function () {
+ if (TimeUI._timelineDragging === true) {
+ $('#mmgisTimeUITimelineSlider').css({
+ pointerEvents: 'inherit',
+ })
+ $('#mmgisTimeUITimelineInner').off(
+ 'mousemove',
+ TimeUI._timelineDrag
+ )
+ TimeUI._timelineDragging = false
+ }
+ })
+ $('#mmgisTimeUITimelineInner').on('mouseup', function () {
+ if (TimeUI._timelineDragging === true) {
+ $('#mmgisTimeUITimelineSlider').css({
+ pointerEvents: 'inherit',
+ })
+ $('#mmgisTimeUITimelineInner').off(
+ 'mousemove',
+ TimeUI._timelineDrag
+ )
+ TimeUI._timelineDragging = false
+ }
+ })
+
+ // Time
const options = {
display: {
viewMode: 'months',
@@ -157,33 +246,43 @@ const TimeUI = {
// Don't let end date be before start date
TimeUI.startTempus.subscribe(Namespace.events.change, (e) => {
- TimeUI.setStartTime(
- moment.utc(e.date).toISOString(),
- TimeUI.startTempus.dontChangeNext
- )
- TimeUI.endTempus.updateOptions({
- restrictions: {
- minDate: e.date,
- },
- })
- TimeUI._remakeTimeSlider()
- TimeUI.startTempus.dontChangeNext = false
+ if (TimeUI.startTempus.dontChangeAnythingElse !== true) {
+ TimeUI.setStartTime(
+ moment.utc(e.date).toISOString(),
+ TimeUI.startTempus.dontChangeNext
+ )
+ TimeUI.startTempusSavedLastDate = e.date
+ TimeUI.endTempus.updateOptions({
+ restrictions: {
+ minDate: e.date,
+ },
+ })
+ TimeUI._remakeTimeSlider()
+ TimeUI.startTempus.dontChangeNext = false
+ }
+ TimeUI.startTempus.dontChangeAnythingElse = false
})
// Don't let start date be after end date
TimeUI.endTempus.subscribe(Namespace.events.change, (e) => {
- if (TimeUI._startTimestamp != null) {
+ if (
+ TimeUI._startTimestamp != null &&
+ TimeUI.endTempus.dontChangeAnythingElse !== true
+ ) {
TimeUI.setEndTime(
moment.utc(e.date).toISOString(),
TimeUI.endTempus.dontChangeNext
)
+ TimeUI.endTempusSavedLastDate = e.date
TimeUI.startTempus.updateOptions({
restrictions: {
maxDate: e.date,
},
})
+
TimeUI._remakeTimeSlider()
TimeUI.endTempus.dontChangeNext = false
}
+ TimeUI.endTempus.dontChangeAnythingElse = false
})
// Disable Now when picking so that date restrictions don't keep applying and hiding calendar
@@ -201,13 +300,23 @@ const TimeUI = {
})
// tippy
+ tippy('#mmgisTimeUIMode', {
+ content: 'Mode',
+ placement: 'top',
+ theme: 'blue',
+ })
tippy('#mmgisTimeUIPlay', {
content: 'Play',
placement: 'top',
theme: 'blue',
})
+ tippy('#mmgisTimeUIStep', {
+ content: 'Step Size',
+ placement: 'top',
+ theme: 'blue',
+ })
tippy('#mmgisTimeUIRate', {
- content: 'Frame Duration',
+ content: 'Step Duration',
placement: 'top',
theme: 'blue',
})
@@ -217,12 +326,69 @@ const TimeUI = {
theme: 'blue',
})
- $('#mmgisTimeUIRateDropdown').html(
- Dropy.construct(TimeUI.intervalNames, null, TimeUI.intervalIndex, {
+ // Mode dropdown
+ $('#mmgisTimeUIModeDropdown').html(
+ Dropy.construct(TimeUI.modes, 'Mode', TimeUI.modeIndex, {
openUp: true,
dark: true,
})
)
+ Dropy.init($('#mmgisTimeUIModeDropdown'), function (idx) {
+ TimeUI.modeIndex = idx
+ if (TimeUI.modes[TimeUI.modeIndex] === 'Point') {
+ $('#mmgisTimeUIStartWrapper').css({ display: 'none' })
+ // Remove end date enforcement
+ TimeUI.endTempus.updateOptions({
+ restrictions: {
+ minDate: new Date(0).toISOString(),
+ },
+ })
+ } else {
+ $('#mmgisTimeUIStartWrapper').css({ display: 'inherit' })
+ // Reinforce min date
+ TimeUI.endTempus.updateOptions({
+ restrictions: {
+ minDate: TimeUI.startTempusSavedLastDate,
+ },
+ })
+ if (TimeUI._startTimestamp >= TimeUI._endTimestamp) {
+ const offsetStartDate = new Date(TimeUI._endTimestamp)
+ const parsedStart = TimeUI.startTempus.dates.parseInput(
+ new Date(offsetStartDate)
+ )
+ TimeUI.startTempus.dates.setValue(parsedStart)
+ }
+ }
+ TimeUI._remakeTimeSlider(true)
+ })
+ // Step dropdown
+ $('#mmgisTimeUIStepDropdown').html(
+ Dropy.construct(
+ Object.keys(MS).map((k) => k.capitalizeFirstLetter()),
+ 'Step',
+ TimeUI.stepIndex,
+ {
+ openUp: true,
+ dark: true,
+ }
+ )
+ )
+ Dropy.init($('#mmgisTimeUIStepDropdown'), function (idx) {
+ TimeUI.stepIndex = idx
+ TimeUI._refreshIntervals()
+ })
+ // Rate dropdown
+ $('#mmgisTimeUIRateDropdown').html(
+ Dropy.construct(
+ TimeUI.intervalNames,
+ 'Rate',
+ TimeUI.intervalIndex,
+ {
+ openUp: true,
+ dark: true,
+ }
+ )
+ )
Dropy.init($('#mmgisTimeUIRateDropdown'), function (idx) {
TimeUI.intervalIndex = idx
TimeUI._refreshIntervals()
@@ -329,18 +495,34 @@ const TimeUI = {
TimeUI._remakeTimeSlider()
TimeUI._setCurrentTime(true, savedEndDate)
+
+ if (TimeUI.enabled) {
+ TimeUI._makeHistogram()
+ }
},
- togglePlay() {
- if (TimeUI.play) {
+ togglePlay(force) {
+ const mode = TimeUI.modes[TimeUI.modeIndex]
+ if (TimeUI.play || force === false) {
$('#mmgisTimeUIPlay')
.css('background', '')
.css('color', 'var(--color-a4)')
TimeUI.play = false
+
+ // Don't reposition active time on Stop for Point Mode
+ if (mode === 'Point') TimeUI._savedPlayEnd = null
+
+ // But do for Range Mode
+ if (TimeUI._savedPlayEnd != null) {
+ TimeUI.setCurrentTime(TimeUI._savedPlayEnd)
+ TimeUI._remakeTimeSlider(true)
+ TimeUI._savedPlayEnd = null
+ }
} else {
$('#mmgisTimeUIPlay')
.css('background', 'var(--color-p4)')
.css('color', 'white')
TimeUI.play = true
+ TimeUI._savedPlayEnd = TimeUI.getCurrentTimestamp()
TimeUI.now = false
$('#mmgisTimeUIPresent')
.css('background', '')
@@ -353,6 +535,7 @@ const TimeUI = {
_refreshIntervals() {
clearInterval(TimeUI.playInterval)
if (TimeUI.play) {
+ TimeUI._loopTime()
TimeUI.playInterval = setInterval(
TimeUI._loopTime,
TimeUI.intervalValues[TimeUI.intervalIndex]
@@ -368,15 +551,25 @@ const TimeUI = {
}
},
_loopTime() {
- const start = TimeUI._startTimestamp
- const end = TimeUI._endTimestamp
+ const mode = TimeUI.modes[TimeUI.modeIndex]
+ const start =
+ mode === 'Range'
+ ? TimeUI._startTimestamp
+ : TimeUI._timelineStartTimestamp
+
+ const end =
+ mode === 'Range'
+ ? TimeUI._endTimestamp
+ : TimeUI._timelineEndTimestamp
const current = TimeUI.getCurrentTimestamp()
- let next = (end - start) / TimeUI.numFrames + current
+ let next = current + MS[Object.keys(MS)[TimeUI.stepIndex]]
if (next > end) next = end
if (current === end) next = start
- TimeUI.setCurrentTime(next)
+ if (mode === 'Range') TimeUI.setCurrentTime(next)
+ if (mode === 'Point') TimeUI.setCurrentTime(next, null, null, true)
+
TimeUI._remakeTimeSlider(true)
},
toggleTimeNow(force) {
@@ -387,10 +580,7 @@ const TimeUI = {
$('#mmgisTimeUIEnd').css('pointer-events', 'none')
$('#mmgisTimeUIEndWrapper').css('cursor', 'not-allowed')
TimeUI.now = true
- TimeUI.play = false
- $('#mmgisTimeUIPlay')
- .css('background', '')
- .css('color', 'var(--color-a4)')
+ TimeUI.togglePlay(false)
} else {
clearInterval(TimeUI.presentTimeInterval)
$('#mmgisTimeUIPresent')
@@ -403,19 +593,28 @@ const TimeUI = {
TimeUI._refreshIntervals()
},
_remakeTimeSlider(ignoreHistogram) {
+ const rangeMode =
+ TimeUI.modes[TimeUI.modeIndex] === 'Range' ? true : false
if (TimeUI.timeSlider) {
$('#mmgisTimeUITimelineSlider').empty()
TimeUI.timeSlider = null
+ if (rangeMode) $('#mmgisTimeUITimelineSlider').addClass('rangeMode')
+ else $('#mmgisTimeUITimelineSlider').removeClass('rangeMode')
}
TimeUI.timeSlider = new RangeSliderPips({
target: document.querySelector('#mmgisTimeUITimelineSlider'),
props: {
- values: [TimeUI._startTimestamp, TimeUI.getCurrentTimestamp()],
+ values: rangeMode
+ ? [
+ TimeUI.removeOffset(TimeUI._startTimestamp),
+ TimeUI.removeOffset(TimeUI.getCurrentTimestamp()),
+ ]
+ : [TimeUI.removeOffset(TimeUI.getCurrentTimestamp())],
pips: false,
- min: TimeUI._startTimestamp,
- max: TimeUI._endTimestamp,
- range: true,
+ min: TimeUI._timelineStartTimestamp,
+ max: TimeUI._timelineEndTimestamp,
+ range: rangeMode,
pushy: false,
float: false,
springValues: {
@@ -428,28 +627,66 @@ const TimeUI = {
},
})
- $('#mmgisTimeUICurrentTime').text(
- moment.utc(TimeUI.getCurrentTimestamp(true)).format(FORMAT)
- )
-
TimeUI.timeSlider.$on('start', (e) => {
TimeUI.toggleTimeNow(false)
+ if (TimeUI.play) {
+ TimeUI._savedPlayEnd = null
+ TimeUI.togglePlay(false)
+ }
})
TimeUI.timeSlider.$on('change', (e) => {
- $('#mmgisTimeUICurrentTime').text(
- moment.utc(TimeUI.removeOffset(e.detail.value)).format(FORMAT)
+ let idx = 0
+ if (TimeUI.modes[TimeUI.modeIndex] === 'Point') idx -= 1
+
+ const date = new Date(e.detail.value)
+ const offsetNowDate = new Date(
+ date.getTime() + date.getTimezoneOffset() * 60000
)
+ if (e.detail.activeHandle === idx) {
+ const parsedNow = TimeUI.startTempus.dates.parseInput(
+ new Date(offsetNowDate)
+ )
+ TimeUI.startTempus.dontChangeAnythingElse = true
+ TimeUI.startTempus.dates.setValue(parsedNow)
+ }
+ if (e.detail.activeHandle === idx + 1) {
+ const parsedNow = TimeUI.endTempus.dates.parseInput(
+ new Date(offsetNowDate)
+ )
+ TimeUI.endTempus.dontChangeAnythingElse = true
+ TimeUI.endTempus.dates.setValue(parsedNow)
+ }
})
TimeUI.timeSlider.$on('stop', (e) => {
- TimeUI.setCurrentTime(new Date(e.detail.value).toISOString(), false)
+ let idx = 0
+ if (TimeUI.modes[TimeUI.modeIndex] === 'Point') idx -= 1
+
+ const date = new Date(e.detail.value)
+ const offsetNowDate = new Date(
+ date.getTime() + date.getTimezoneOffset() * 60000
+ )
+ if (e.detail.activeHandle === idx) {
+ const parsedNow = TimeUI.startTempus.dates.parseInput(
+ new Date(offsetNowDate)
+ )
+ TimeUI.startTempus.dates.setValue(parsedNow)
+ }
+ if (e.detail.activeHandle === idx + 1) {
+ const parsedNow = TimeUI.endTempus.dates.parseInput(
+ new Date(offsetNowDate)
+ )
+ TimeUI.endTempus.dates.setValue(parsedNow)
+ }
})
if ($('#toggleTimeUI').hasClass('active') && ignoreHistogram !== true)
TimeUI._makeHistogram()
},
_makeHistogram() {
- const startTimestamp = TimeUI._startTimestamp
- const endTimestamp = TimeUI.getCurrentTimestamp()
+ const startTimestamp = TimeUI.removeOffset(
+ TimeUI._timelineStartTimestamp
+ )
+ const endTimestamp = TimeUI.removeOffset(TimeUI._timelineEndTimestamp)
// Don't remake if nothing changes
if (
@@ -485,8 +722,10 @@ const TimeUI = {
}
})
- const starttimeISO = new Date(TimeUI._startTimestamp).toISOString()
- const endtimeISO = new Date(endTimestamp).toISOString()
+ const starttimeISO = new Date(
+ TimeUI._timelineStartTimestamp
+ ).toISOString()
+ const endtimeISO = new Date(TimeUI._timelineEndTimestamp).toISOString()
const NUM_BINS = Math.min(endTimestamp - startTimestamp, 360)
const bins = new Array(NUM_BINS).fill(0)
@@ -507,7 +746,9 @@ const TimeUI = {
F_.linearScale(
[startTimestamp, endTimestamp],
[0, NUM_BINS],
- new Date(time.t).getTime()
+ TimeUI.removeOffset(
+ new Date(time.t).getTime()
+ )
)
)
]++
@@ -533,7 +774,7 @@ const TimeUI = {
)
})
},
- _setCurrentTime(force, forceDate) {
+ _setCurrentTime(force, forceDate, disableChange) {
if (TimeUI.now === true || force === true) {
let date = forceDate || new Date()
const offsetNowDate = new Date(
@@ -542,8 +783,9 @@ const TimeUI = {
const parsedNow = TimeUI.endTempus.dates.parseInput(
new Date(offsetNowDate)
)
- TimeUI.setCurrentTime(parsedNow)
- TimeUI._remakeTimeSlider(true)
+
+ TimeUI.setCurrentTime(parsedNow, disableChange)
+ //TimeUI._remakeTimeSlider(true)
TimeUI.endTempus.dates.setValue(parsedNow)
}
},
@@ -553,9 +795,7 @@ const TimeUI = {
// Start
if (start != null) {
date = new Date(start)
- const offsetStartDate = new Date(
- date.getTime() + date.getTimezoneOffset() * 60000
- )
+ const offsetStartDate = TimeUI.addOffset(date)
const parsedStart = TimeUI.startTempus.dates.parseInput(
new Date(offsetStartDate)
)
@@ -584,13 +824,22 @@ const TimeUI = {
TimeUI.change()
},
- setStartTime(ISOString, disableChange) {
+ setStartTime(ISOString, disableChange, dontRedrawTimeline) {
const timestamp = Date.parse(ISOString)
TimeUI._startTimestamp = timestamp
+ if (TimeUI._timelineStartTimestamp == null)
+ TimeUI._timelineStartTimestamp = TimeUI._startTimestamp
- TimeUI._drawTimeLine()
+ if (dontRedrawTimeline != true) TimeUI._drawTimeLine()
if (disableChange != true) TimeUI.change()
},
+ addOffset(timestamp) {
+ const date = new Date(timestamp)
+ const addedOffset = new Date(
+ date.getTime() + date.getTimezoneOffset() * 60000
+ )
+ return addedOffset
+ },
removeOffset(timestamp) {
const date = new Date(timestamp)
const removedOffset = new Date(
@@ -616,6 +865,10 @@ const TimeUI = {
TimeUI._endTimestamp === TimeUI.getCurrentTimestamp()
const timestamp = Date.parse(ISOString)
TimeUI._endTimestamp = timestamp
+
+ if (TimeUI._timelineEndTimestamp == null)
+ TimeUI._timelineEndTimestamp = TimeUI._endTimestamp
+
TimeUI._drawTimeLine()
if (sliderFixedToEnd) {
@@ -623,30 +876,43 @@ const TimeUI = {
if (disableChange != true) TimeUI.change()
}
},
- setCurrentTime(ISOString, disableChange, dontRemoveOffset) {
+ setCurrentTime(
+ ISOString,
+ disableChange,
+ dontRemoveOffset,
+ ignoreDontChange
+ ) {
const timestamp =
typeof ISOString === 'string' ? Date.parse(ISOString) : ISOString
TimeUI._timeSliderTimestamp = timestamp
- $('#mmgisTimeUICurrentTime').text(
- moment
- .utc(
- TimeUI.getCurrentTimestamp(
- dontRemoveOffset === true ? false : true
- )
- )
- .format(FORMAT)
- )
+ if (TimeUI.play) {
+ const date = new Date(TimeUI._timeSliderTimestamp)
+ const offsetNowDate = new Date(
+ date.getTime() + date.getTimezoneOffset() * 60000
+ )
+ const parsedNow = TimeUI.endTempus.dates.parseInput(
+ new Date(offsetNowDate)
+ )
+ if (ignoreDontChange !== true)
+ TimeUI.endTempus.dontChangeAnythingElse = true
+ TimeUI.endTempus.dates.setValue(parsedNow)
+ }
+
if (disableChange != true) TimeUI.change()
},
change() {
if (
typeof TimeUI.timeChange === 'function' &&
- TimeUI._startTimestamp &&
- TimeUI._endTimestamp
+ TimeUI._startTimestamp != null &&
+ TimeUI._endTimestamp != null
) {
+ const mode = TimeUI.modes[TimeUI.modeIndex]
+
TimeUI.timeChange(
new Date(
- TimeUI.removeOffset(TimeUI._startTimestamp)
+ mode === 'Range'
+ ? TimeUI.removeOffset(TimeUI._startTimestamp)
+ : 0
).toISOString(),
new Date(
TimeUI.removeOffset(TimeUI._endTimestamp)
@@ -655,32 +921,67 @@ const TimeUI = {
)
}
},
- _drawTimeLine() {
+ _timelineDrag: function (e) {
+ if (TimeUI._timelineDragging === true) {
+ const dx = e.originalEvent.movementX
+ const width = document
+ .getElementById('mmgisTimeUITimelineInner')
+ .getBoundingClientRect().width
+ const dif =
+ TimeUI._timelineEndTimestamp - TimeUI._timelineStartTimestamp
+
+ const nextStart =
+ TimeUI._timelineStartTimestamp - 0 - (dif / width) * dx
+ const nextEnd =
+ TimeUI._timelineEndTimestamp - 0 - (dif / width) * dx
+
+ TimeUI._drawTimeLine(nextStart, nextEnd)
+
+ clearTimeout(TimeUI._panHistoTimeout)
+ $('#mmgisTimeUITimelineHisto').empty()
+ TimeUI._panHistoTimeout = setTimeout(() => {
+ TimeUI._makeHistogram()
+ }, 3000)
+ }
+ },
+ _drawTimeLine(forceStart, forceEnd) {
const timelineElm = $('#mmgisTimeUITimelineInner')
timelineElm.empty()
- const s = TimeUI.removeOffset(TimeUI._startTimestamp)
- const e = TimeUI.removeOffset(TimeUI._endTimestamp)
+ let s = forceStart || TimeUI._timelineStartTimestamp
+ let e = forceEnd || TimeUI._timelineEndTimestamp
+
if (e == null || s == null) return
+ s = Math.max(s - 0, 0) // Year 1970
+ e = Math.min(e - 0, 3155788800000) // Year 2070
+
+ TimeUI._timelineStartTimestamp = parseInt(s)
+ TimeUI._timelineEndTimestamp = parseInt(e)
+
const dif = e - s
let unit = null
- if (dif / MS.year > 3) {
+ if (dif / MS.decade > 2) {
+ unit = 'decade'
+ } else if (dif / MS.year > 2) {
unit = 'year'
} else if (dif / MS.month > 1) {
unit = 'month'
- } else if (dif / MS.day > 2) {
+ } else if (dif / MS.day > 1.5) {
unit = 'day'
- } else if (dif / MS.hour > 1) {
+ } else if (dif / MS.hour > 0.75) {
unit = 'hour'
- } else if (dif / MS.minute > 1) {
+ } else if (dif / MS.minute > 0.75) {
unit = 'minute'
} else unit = 'second'
let first = true
const bigTicks = F_.getTimeStartsBetweenTimestamps(s, e, unit)
+ TimeUI._tickTippies.forEach((t) => {
+ if (t[0].state.isDestroyed != true) t[0].destroy()
+ })
for (let i = 0; i < bigTicks.length; i++) {
const left = F_.linearScale([s, e], [0, 100], bigTicks[i].ts)
if (left >= 0 && left <= 100) {
@@ -688,15 +989,26 @@ const TimeUI = {
[
``,
`
`,
- `
${
+ `
${
bigTicks[i].label
- }${first ? `
${unit}` : ''}
${unit}` : ''}
`,
`
`,
].join('\n')
)
first = false
+ TimeUI._tickTippies.push(
+ tippy(`#mmgisTimeUITickLabel_${i}`, {
+ content: moment(
+ TimeUI.addOffset(new Date(bigTicks[i].ts))
+ ).format(FORMAT),
+ placement: 'top',
+ theme: 'blue',
+ })
+ )
}
}
+
+ TimeUI._remakeTimeSlider(true)
},
}
diff --git a/src/essence/Basics/Formulae_/Formulae_.js b/src/essence/Basics/Formulae_/Formulae_.js
index a957f880..da42584a 100644
--- a/src/essence/Basics/Formulae_/Formulae_.js
+++ b/src/essence/Basics/Formulae_/Formulae_.js
@@ -59,6 +59,9 @@ var Formulae_ = {
range[0]
)
},
+ getBase64Transparent256Tile: function () {
+ return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAABFUlEQVR42u3BMQEAAADCoPVP7WsIoAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeAMBPAAB2ClDBAAAAABJRU5ErkJggg=='
+ },
monthNumberToName: function (monthNumber) {
switch (monthNumber) {
case 0:
@@ -100,6 +103,23 @@ var Formulae_ = {
let currentDate
switch (unit) {
+ case 'decade':
+ currentDate = new Date(
+ Date.UTC(Math.floor(startDate.getFullYear() / 10) * 10)
+ )
+ while (currentDate < endDate) {
+ currentDate.setUTCFullYear(
+ Math.floor(currentDate.getUTCFullYear() / 10) * 10 + 10
+ )
+ timeStarts.push({
+ ts: Date.parse(currentDate),
+ label:
+ Math.floor(currentDate.getUTCFullYear() / 10) * 10 +
+ 's',
+ })
+ }
+
+ break
case 'year':
currentDate = new Date(Date.UTC(startDate.getFullYear()))
while (currentDate < endDate) {
@@ -121,7 +141,7 @@ var Formulae_ = {
ts: Date.parse(currentDate),
label: Formulae_.monthNumberToName(
currentDate.getUTCMonth()
- ),
+ ).toUpperCase(),
})
}
break
diff --git a/src/essence/Basics/Layers_/LayerConstructors.js b/src/essence/Basics/Layers_/LayerConstructors.js
index b2a68779..247c4fc7 100644
--- a/src/essence/Basics/Layers_/LayerConstructors.js
+++ b/src/essence/Basics/Layers_/LayerConstructors.js
@@ -219,6 +219,28 @@ export const constructVectorLayer = (
}
layerObj.shape = 'directional_circle'
}
+ /*
+ const markerXY = Map_.map.latLngToLayerPoint(latlong)
+ const markerLatLong = Map_.map.containerPointToLatLng([
+ markerXY.x,
+ markerXY.y,
+ ])
+ const pixelBelowMarkerLatLong = Map_.map.containerPointToLatLng(
+ [markerXY.x, markerXY.y + 1]
+ )
+ console.log(
+ latlong,
+ markerXY,
+ markerLatLong,
+ pixelBelowMarkerLatLong,
+ F_.bearingBetweenTwoLatLngs(
+ pixelBelowMarkerLatLong.lat,
+ pixelBelowMarkerLatLong.lng,
+ markerLatLong.lat,
+ markerLatLong.lng
+ )
+ )
+ */
}
switch (layerObj.shape) {
diff --git a/src/essence/Basics/Layers_/leaflet-tilelayer-middleware.js b/src/essence/Basics/Layers_/leaflet-tilelayer-middleware.js
index 262993d4..a00e2542 100644
--- a/src/essence/Basics/Layers_/leaflet-tilelayer-middleware.js
+++ b/src/essence/Basics/Layers_/leaflet-tilelayer-middleware.js
@@ -11,6 +11,8 @@
https://github.com/xtk93x/Leaflet.TileLayer.ColorFilter
*/
+import F_ from '../../Basics/Formulae_/Formulae_'
+
var colorFilterExtension = {
intialize: function (url, options) {
L.TileLayer.prototype.initialize.call(this, url, options)
@@ -93,6 +95,37 @@ var colorFilterExtension = {
this._container.style['mix-blend-mode'] = this.colorBlend()
}
},
+ // Reduces tile flicker. This and refresh() from https://github.com/Leaflet/Leaflet/issues/6659#issuecomment-813328622
+ _refreshTileUrl: function (tile, url) {
+ //use a image in background, so that only replace the actual tile, once image is loaded in cache!
+ let img = new Image()
+ img.onload = function (e) {
+ L.Util.requestAnimFrame(function () {
+ tile.el.src = url
+ tile.el.style.opacity = 1
+ })
+ }
+ img.onerror = function (e) {
+ tile.el.src = F_.getBase64Transparent256Tile()
+ tile.el.style.opacity = 0
+ }
+ img.src = url
+ },
+ refresh: function () {
+ if (this._map == null) return
+
+ for (let key in this._tiles) {
+ const tile = this._tiles[key]
+ if (tile.current && tile.active) {
+ const oldsrc = tile.el.src
+ const newsrc = this.getTileUrl(tile.coords)
+ if (oldsrc != newsrc) {
+ //L.DomEvent.off(tile, 'load', this._tileOnLoad); ... this doesnt work!
+ this._refreshTileUrl(tile, newsrc)
+ }
+ }
+ }
+ },
}
var wmsExtension = {
@@ -257,5 +290,7 @@ L.tileLayer.colorFilter = function (url, options) {
}
url = url.replace(/{t}/g, '_time_')
- return new L.TileLayer.ColorFilter(url, options)
+ const tileLayer = new L.TileLayer.ColorFilter(url, options)
+
+ return tileLayer
}
diff --git a/src/essence/Basics/Map_/Map_.js b/src/essence/Basics/Map_/Map_.js
index 1a1e0271..15f8e423 100644
--- a/src/essence/Basics/Map_/Map_.js
+++ b/src/essence/Basics/Map_/Map_.js
@@ -860,6 +860,8 @@ async function makeLayer(layerObj, evenIfOff, forceGeoJSON) {
continuousWorld: true,
reuseTiles: true,
bounds: bb,
+ timeEnabled:
+ layerObj.time != null && layerObj.time.enabled === true,
time: typeof layerObj.time === 'undefined' ? '' : layerObj.time.end,
compositeTile:
typeof layerObj.time === 'undefined'