680 lines
27 KiB
JavaScript
680 lines
27 KiB
JavaScript
var playbackRunning = false;
|
|
var playbackSpeedFactor = 1;
|
|
var refreshIntervalSecs = 1;
|
|
var historyTimerangeCurrentlyDisplayed = null;
|
|
var persistPaths = false;
|
|
|
|
// return object with attributes matching the filter being excluded/dropped
|
|
var filter_object = function(obj, filter, invertMatch) {
|
|
var filteredObj = {};
|
|
for (key in obj) {
|
|
if(invertMatch){
|
|
if (obj.hasOwnProperty(key) && !filter.test(key)) {
|
|
filteredObj[key] = obj[key];
|
|
}
|
|
} else {
|
|
if (obj.hasOwnProperty(key) && filter.test(key)) {
|
|
filteredObj.key = obj[key];
|
|
}
|
|
}
|
|
}
|
|
return filteredObj;
|
|
}
|
|
|
|
function playbackChangeState(state) {
|
|
playbackRunning = state;
|
|
if(state){
|
|
$('#control-play').prop('disabled', true);
|
|
$('#control-pause').prop('disabled', false);
|
|
} else {
|
|
$('#control-play').prop('disabled', false);
|
|
$('#control-pause').prop('disabled', true);
|
|
}
|
|
}
|
|
|
|
// filter out k/v pairs from query/hash part of url which match pattern
|
|
function filter_hashPartUrl(str, filter){
|
|
filteredComponents = [];
|
|
location.hash.replace('#', '').split('&').forEach(function(item){
|
|
var key = item.split('=')[0];
|
|
var val = item.split('=')[1];
|
|
if(!filter.test(key)){
|
|
filteredComponents.push(item);
|
|
}
|
|
});
|
|
return filteredComponents.join('&');
|
|
}
|
|
|
|
// downloads static data, locks filters and interaction and
|
|
function initStaticView(urlHashPartStr) {
|
|
prepareHistoryUIElements();
|
|
$('#data-loading').show();
|
|
|
|
var urlHashFiltered = filter_hashPartUrl(urlHashPartStr, /mode/);
|
|
|
|
// timeout is larger than server's timeout returning 5xx when it fails delivering
|
|
timeout(600000, fetch(rcd_globals.history_endpoint_prefix+'staticdata.json?'+urlHashFiltered, customRequestOptions)).then(function(data){
|
|
if (!data.ok) {
|
|
throw Error(data.statusText);
|
|
}
|
|
return data.json().then(function(json){
|
|
if(Object.keys(json).length === 0) rcToast("Response from API was empty. Either the timeframe you requested was very small or the server hasn't stored the requested data anymore", true);
|
|
return json;
|
|
});
|
|
}).then(function(json) {
|
|
|
|
aircraftListJson = []; // fed into updateAircraftFeatures
|
|
flightPathsJson = []; // fed into updateTrack
|
|
|
|
// generate a `fake` aircraftist.json from the last positions of the aircrafts
|
|
json.forEach(function (ac){
|
|
|
|
// FIXME: we need dynamic metadata based on where in the user clicks!
|
|
var ac_metainfo = filter_object(ac, /flights/, true);
|
|
var lastFlight = ac.flights.slice(-1)[0]; // slicing returns an array (len 1)
|
|
var ac_metainfo_flight = filter_object(lastFlight, /track/, true);
|
|
var lastTrack = lastFlight.track.slice(-1)[0];
|
|
var lastTrackSlice = lastTrack.slice(-1)[0]; //last/newest track point of last path is used as position of icon
|
|
var lastTrackPoint = { //last/newest track point of last path is used as position of icon
|
|
'lat': lastTrackSlice[0],
|
|
'lon': lastTrackSlice[1],
|
|
'alt': lastTrackSlice[2],
|
|
'spd': lastTrackSlice[3],
|
|
'uti': lastTrackSlice[4],
|
|
'trk': lastTrackSlice[5],
|
|
'vrt': lastTrackSlice[6],
|
|
'src': lastTrackSlice[7],
|
|
'gda': lastTrackSlice[8],
|
|
};
|
|
Object.assign(ac_metainfo, lastTrackPoint); //merge metadata from last trackpoint to flight object
|
|
Object.assign(ac_metainfo, ac_metainfo_flight);
|
|
aircraftListJson.push(ac_metainfo);
|
|
|
|
//ac.src = lastTrackPoint.src; //assume src of all flights for trackJson to be the one of the last point
|
|
/*ac_flightsbundle = {
|
|
icao: ac.hex,
|
|
src: ac.src, // assume source is aircraft-specific, does not change when between flights
|
|
flights: []
|
|
};
|
|
ac.flights.forEach(function(flight){
|
|
// flight-specific info, independent of aircraft
|
|
var flight_info = {
|
|
fli: flight.fli,
|
|
cou: flight.cou,
|
|
dst: flight.dst,
|
|
org: flight.org,
|
|
fpth: []
|
|
};
|
|
|
|
console.log(flight);
|
|
flight.track.forEach(function(point){
|
|
flight_info.fpth.push({
|
|
uti: point.uti,
|
|
lat: point.lat,
|
|
lon: point.lon,
|
|
alt: point.alt,
|
|
gnd: point.gnd
|
|
});
|
|
});
|
|
ac_flightsbundle.flights.push(flight_info);
|
|
});*/
|
|
//console.log(ac);
|
|
flightPathsJson.push(ac);
|
|
});
|
|
|
|
|
|
updateAvailablePlanes(null, aircraftListJson)
|
|
.then(function(){ return updateAircraftFeatures(
|
|
false, // no forcefetch
|
|
null, // no timerange needed with prepared json
|
|
aircraftListJson // prepared json
|
|
);}) // populate aircrafts on map
|
|
.then(function(){return updateTrack(trackSource, flightPathsJson);});
|
|
|
|
$('#data-loading').hide();
|
|
instantiateDisabledSlider();
|
|
|
|
}).catch(function(err) {
|
|
if(!err.message.includes("Failed to fetch")){
|
|
console.log("[staticdownload] A problem occurred: "+err.message);
|
|
rcToast("Couldn't fetch static view from API, you may try reloading the page when the server is available again (Error was: "+err.message+")", true);
|
|
$('#data-loading').hide();
|
|
$('#data-loading-error').show();
|
|
}
|
|
});
|
|
}
|
|
|
|
function prepareHistoryUIElements(){
|
|
// scale mapcontainer to size that controls have space
|
|
$(".container-fluid-map").addClass("mapcontainer_timecontrols");
|
|
$('.historycontrol').show();
|
|
|
|
// disable playback and pause button per default
|
|
$('#control-play').prop('disabled', true);
|
|
$('#control-pause').prop('disabled', true);
|
|
$('#refresh-interval-cell').hide();
|
|
|
|
$('.control-staticdownload').prop('disabled', true);
|
|
|
|
}
|
|
function instantiateDisabledSlider(){
|
|
// sliderhull, the object in the DOM is replaced after this call with an object with the new id specified as data-slider-id
|
|
var slider = new Slider('#sliderHull', {
|
|
formatter: function(value) {
|
|
// read unix timestamp in seconds and print out time highlighted
|
|
var str = moment(value, "X").utc().format("YYYY-MM-DD HH:*B*mm:ss*BB*");
|
|
str = str.replace("*B*", ""); // originally meant to insert tags to style time, but it's not working
|
|
str = str.replace("*BB*", " UTC");
|
|
return str;
|
|
},
|
|
tooltip: "hide",
|
|
handle: "sqare"
|
|
});
|
|
slider.disable();
|
|
return slider;
|
|
}
|
|
|
|
function initInteractiveHistoryControls(){
|
|
|
|
prepareHistoryUIElements();
|
|
var slider = instantiateDisabledSlider();
|
|
|
|
//restore playback speed from localStorage
|
|
parseAndSetPlaybackspeed(localStorage.getItem("ol_playbackspeed"));
|
|
|
|
// restore whether to accumulate flight points
|
|
persistPaths = (localStorage.getItem("ol_persistPaths") === 'true');
|
|
if(persistPaths === true){
|
|
$("#control-persist").addClass("active");
|
|
}
|
|
|
|
function computeRangeObject(minDate, maxDate){
|
|
var lda = {};
|
|
|
|
// if minDate is after start of maxDate day, set minDate as start of selected timerange
|
|
// (our available daterange doesnt even span a day, thus selecting a day would fail)
|
|
// attention: instantiate moment before using a variable since startOf etc. actions aren't const
|
|
if(minDate.diff(moment(maxDate).startOf('day')) > 0){
|
|
lda.begin = minDate;
|
|
lda.end = maxDate;
|
|
} else {
|
|
lda.begin = moment(maxDate).startOf('day');
|
|
lda.end = moment(maxDate);
|
|
}
|
|
return {
|
|
'Latest Day available': [
|
|
lda.begin,
|
|
lda.end
|
|
],
|
|
'Today': [
|
|
moment().utc().startOf('day'),
|
|
moment().utc().endOf('day')
|
|
],
|
|
'Yesterday': [
|
|
moment().utc().subtract(1, 'days').startOf('day'),
|
|
moment().utc().subtract(1, 'days').endOf('day')
|
|
],
|
|
'This Week': [
|
|
moment().utc().startOf('isoWeek'),
|
|
moment().utc().endOf('isoWeek')
|
|
],
|
|
'Last Week': [
|
|
moment().utc().subtract(1, 'week').startOf('isoWeek'),
|
|
moment().utc().subtract(1, 'week').endOf('isoWeek')
|
|
],
|
|
'Last 7 Days': [
|
|
moment().utc().subtract(7, 'days').add(1, 'seconds'),
|
|
moment().utc()
|
|
],
|
|
'This Month': [
|
|
moment().utc().startOf('month'),
|
|
moment().utc()
|
|
],
|
|
'Last Month': [
|
|
moment().utc().subtract(1, 'month').startOf('month'),
|
|
moment().utc().subtract(1, 'month').endOf('month'),
|
|
]
|
|
};
|
|
}
|
|
|
|
var dateRangePicker_options = {
|
|
"showDropdowns": true,
|
|
"timePicker": true,
|
|
"timePicker24Hour": true,
|
|
"timePickerSeconds": true,
|
|
timePickerIncrement: 1,
|
|
"autoApply": false,
|
|
singleDatePicker: false,
|
|
alwaysShowCalendars: true,
|
|
"ranges": computeRangeObject(moment().utc().startOf('day'), moment().utc()),
|
|
locale: {
|
|
format: 'YYYY-MM-DD'
|
|
},
|
|
enableOnReadonly: false,
|
|
isInvalidDate: function(){return true}, // set all dates to invalid to prevent user from selecting something before daterange is fetched (because disabling doesn't work)
|
|
|
|
"drops": "up",
|
|
'opens':"center",
|
|
autoUpdateInput: false
|
|
};
|
|
$('#control-timerange').daterangepicker(dateRangePicker_options);
|
|
|
|
var onDaterangepickerApply = function(ev, picker) {
|
|
// daterangepicker does not have utc support: https://github.com/dangrossman/bootstrap-daterangepicker/issues/343
|
|
var startDateOffset = picker.startDate.utcOffset()
|
|
var utcStartDate = moment.utc(picker.startDate).utcOffset(-startDateOffset, true).utc()
|
|
|
|
var endDateOffset = picker.endDate.utcOffset()
|
|
var utcEndDate = moment.utc(picker.endDate).utcOffset(-endDateOffset, true).utc()
|
|
|
|
playbackChangeState(false); // if playback is running, make it stop since the range may have changed
|
|
|
|
// get daterange in secs
|
|
//var startSecsUnix = Math.floor(($('#control-timerange').data('daterangepicker').startDate - startDateOffset) / 1000);
|
|
//var endSecsUnix = Math.floor(($('#control-timerange').data('daterangepicker').endDate -endDateOffset) / 1000);
|
|
var startSecsUnix = utcStartDate.unix();
|
|
var endSecsUnix = utcEndDate.unix();
|
|
//a date was picked
|
|
/*console.log(utcStartDate.toISOString(true), startSecsUnix);
|
|
console.log(utcEndDate.toISOString(true), endSecsUnix);*/
|
|
|
|
// set slider boundaries
|
|
slider.setAttribute('min', startSecsUnix);
|
|
slider.setAttribute('max', endSecsUnix);
|
|
slider.setValue(startSecsUnix);
|
|
slider.enable(); // enable slider because date was chosen
|
|
|
|
// display initial situation for first second in range
|
|
nextTimestep(false, false) // just run refresh of data once, don't iterate, don't loop
|
|
.then(function(){
|
|
updateSliderTooltipMode(slider, 'show'); // set default mode to show tooltip on hover
|
|
$('.control-staticdownload').prop('disabled', false); //allow staticdownload button
|
|
}
|
|
);
|
|
// save selected date to Localstorage
|
|
try {
|
|
localStorage.setItem("ol_history_selectedrange.from",startSecsUnix);
|
|
localStorage.setItem("ol_history_selectedrange.to",endSecsUnix);
|
|
} catch (e){}
|
|
|
|
|
|
|
|
var selectedLength = moment.duration(utcEndDate.diff(utcStartDate)).as('seconds');
|
|
var threshold = 60
|
|
var tdiff = selectedLength - threshold
|
|
if(tdiff < 0){
|
|
rcToast("Attention: The timerange you selected comprises just "+selectedLength+" seconds")
|
|
}
|
|
|
|
};
|
|
|
|
// returns false if there was no date found in localstorage
|
|
var initializeDaterangePicker = function(ev, picker, chainPromise){
|
|
|
|
console.log("updating available daterange");
|
|
timeout(2000, fetch(rcd_globals.history_endpoint_prefix+'timelimits.json', customRequestOptions)
|
|
).then(function(data){
|
|
return data.json();
|
|
}).then(function(json) {
|
|
var lsDatePreset = false;
|
|
|
|
$('#control-timerange').data('daterangepicker').remove();
|
|
dateRangePicker_options["minDate"]= moment.unix(json.begin).utc();
|
|
dateRangePicker_options["maxDate"]= moment.unix(json.end).utc();
|
|
|
|
// get saved timerange from localstorage
|
|
try {
|
|
dateRangePicker_options["startDate"] = moment.unix(parseInt(localStorage.getItem("ol_history_selectedrange.from"))).utc();
|
|
dateRangePicker_options["endDate"] = moment.unix(parseInt(localStorage.getItem("ol_history_selectedrange.to"))).utc();
|
|
if(dateRangePicker_options["startDate"].isValid() && dateRangePicker_options["endDate"].isValid()){
|
|
lsDatePreset = true;
|
|
} else {
|
|
console.log(localStorage.getItem("ol_history_selectedrange.from"), dateRangePicker_options["startDate"]);
|
|
console.log(localStorage.getItem("ol_history_selectedrange.to"), dateRangePicker_options["endDate"]);
|
|
}
|
|
} catch (e){}
|
|
|
|
if(!dateRangePicker_options["startDate"]._i || !dateRangePicker_options["endDate"]._i){
|
|
console.log("setting default startenddate, localstorage not set");
|
|
dateRangePicker_options["startDate"]= moment.unix(json.end).utc().startOf("day");
|
|
dateRangePicker_options["endDate"]= moment.unix(json.end).utc().endOf("day");
|
|
}
|
|
|
|
dateRangePicker_options.isInvalidDate=undefined; // enable selecting dates again
|
|
|
|
|
|
dateRangePicker_options.ranges = computeRangeObject(moment.unix(json.begin).utc(), moment.unix(json.end).utc());
|
|
$("#control-timerange").daterangepicker(dateRangePicker_options);
|
|
if(chainPromise === undefined){ // if undefined, we're in automatic mode; else interactive mode
|
|
$('#control-timerange').trigger('click');
|
|
}
|
|
|
|
$("#control-timerange").on('show.daterangepicker', initializeDaterangePicker);
|
|
$('#control-timerange').on('apply.daterangepicker', onDaterangepickerApply);
|
|
|
|
return lsDatePreset;
|
|
|
|
}).catch(function(err) {
|
|
if(!err.message.includes("Failed to fetch")){
|
|
console.log("[timelimits] A problem occurred: "+err.message);
|
|
rcToast("Couldn't fetch available timerange from API: "+err.message);
|
|
$('#control-timerange').data('daterangepicker').hide();
|
|
}
|
|
return false;
|
|
}).then(chainPromise);
|
|
|
|
};
|
|
$('#control-timerange').on('show.daterangepicker', initializeDaterangePicker);
|
|
|
|
/*
|
|
TODOS:
|
|
playpause into one and replace pause button with reset button
|
|
*/
|
|
|
|
var iteratingLoopsActive = 0;
|
|
/*
|
|
increments the current timestep/range displayed and calls refreshData afterwards
|
|
incrementing the time and slider is inhibited if 'iterate' is set to false.
|
|
looping infinitely is controlled via the loop flag
|
|
default is: iterate: true; loop: true
|
|
*/
|
|
function nextTimestep(iterate, loop){
|
|
|
|
if(iterate == undefined) iterate = true;
|
|
if(loop == undefined) loop = true;
|
|
|
|
return new Promise(function(resolve, reject){
|
|
|
|
console.log("iterating: "+iterate+" loop:"+loop);
|
|
|
|
if((!playbackRunning && iterate) || iteratingLoopsActive > 1) {
|
|
console.log("nextTimestep bailout: pbrunning="+playbackRunning+" iter:"+iterate+" loopsactive:"+iteratingLoopsActive);
|
|
iteratingLoopsActive -= 1;
|
|
resolve(false); // stop playback
|
|
return;
|
|
}
|
|
|
|
// number of seconds to include in request (include more than one second so aircrafts are more likely to be in consecutive reqs)
|
|
var sampleWindowWidthSecs = 4;
|
|
|
|
|
|
var stepBackInHistory = false;
|
|
if(historyTimerangeCurrentlyDisplayed && historyTimerangeCurrentlyDisplayed.from > (slider.getValue()-sampleWindowWidthSecs/2)){
|
|
stepBackInHistory = true;
|
|
}
|
|
historyTimerangeCurrentlyDisplayed = {
|
|
from: slider.getValue()-sampleWindowWidthSecs/2,
|
|
to: slider.getValue()+sampleWindowWidthSecs/2
|
|
};
|
|
|
|
var begunAtMs, waitingTimeLeft;
|
|
// start refresh promise chain
|
|
resolve(
|
|
new Promise(function(resolve){
|
|
begunAtMs = new Date().getTime();
|
|
resolve(true);
|
|
})
|
|
.then(function(){return refreshData(null, {
|
|
from: historyTimerangeCurrentlyDisplayed.from,
|
|
to: historyTimerangeCurrentlyDisplayed.to
|
|
}, stepBackInHistory);})
|
|
.then(function(refreshedSuccessfully){
|
|
|
|
var nowMs = new Date().getTime();
|
|
var elapsed = nowMs - begunAtMs;
|
|
waitingTimeLeft = refreshIntervalSecs * 1000 - elapsed;
|
|
if(waitingTimeLeft < 0){
|
|
waitingTimeLeft = 0; // we have no time left, refresh ASAP
|
|
}
|
|
|
|
console.log("waitleft ",waitingTimeLeft);
|
|
|
|
// if refresh wasn't successful, pass flag to next timestep not to iterate but continue looping!
|
|
if(!refreshedSuccessfully){
|
|
setTimeout(function(){return nextTimestep(false /*iterate*/, true /*loop*/);}, waitingTimeLeft);
|
|
return;
|
|
}
|
|
|
|
// increment slider after choosing the range to fetch!
|
|
if (iterate){
|
|
var increment = playbackSpeedFactor * refreshIntervalSecs;
|
|
|
|
// check if next iteration will be out of slider boundary
|
|
if(iterate && (slider.getAttribute('max') < slider.getValue()+increment)){
|
|
console.log("Terminating playback");
|
|
loop = false;
|
|
playbackChangeState(false); // make this the last iteration
|
|
iteratingLoopsActive -= 1; // since there will be no further iteration, this loop ends
|
|
updateSliderTooltipMode(slider, 'show');
|
|
}
|
|
console.log("slider before increment "+slider.getValue());
|
|
slider.setValue(slider.getValue() + increment);
|
|
console.log("slider after increment "+slider.getValue());
|
|
}
|
|
|
|
if(loop) setTimeout(function(){return nextTimestep();}, waitingTimeLeft);
|
|
}, function(err){
|
|
console.log("Something failed when fetching: ", err, ";");
|
|
|
|
if(err.match(/.*[0-9]{3}.*/)){
|
|
console.log("retrying");
|
|
setTimeout(function(){return nextTimestep(false /*iterate*/, true /*loop*/);}, waitingTimeLeft);
|
|
}
|
|
})
|
|
);
|
|
}); // return promise
|
|
}
|
|
|
|
|
|
function updateSliderTooltipMode(slider, mode){
|
|
var oldSliderValue = slider.getValue(); // save slider value to restore it after re-rendering
|
|
|
|
// turn on tooltip display
|
|
slider.setAttribute('tooltip', mode);
|
|
// make sure tooltip is really rendered
|
|
slider.refresh();
|
|
slider.relayout();
|
|
|
|
slider.setValue(oldSliderValue); // restore slider state
|
|
// enable slider
|
|
slider.enable();
|
|
}
|
|
$('#control-play').on('click', function(){
|
|
if(!playbackRunning){
|
|
|
|
console.log("play clicked, continuing playback");
|
|
updateSliderTooltipMode(slider, 'always');
|
|
// start playback
|
|
playbackChangeState(true);
|
|
iteratingLoopsActive += 1;
|
|
nextTimestep();
|
|
}
|
|
});
|
|
$('#control-pause').on('click', function(){
|
|
playbackChangeState(false);
|
|
|
|
updateSliderTooltipMode(slider, 'show'); // show tooltip only on demand
|
|
});
|
|
|
|
function generateTargetUrl(prefix){
|
|
var targeturl = prefix;
|
|
if(targeturl.slice(-1) != "&" && targeturl.slice(-1) != "?") { targeturl += "&";}
|
|
|
|
targeturl += $.param( // append parameters to hash-part of url
|
|
// serialize filter and timerange parameters (latter from state of timeslider)
|
|
$.extend(
|
|
//icao,
|
|
filtersetToParams(Filters),
|
|
ol_getviewportExtent(),
|
|
{
|
|
'timeframe.from': slider.getAttribute('min'),
|
|
'timeframe.to': slider.getAttribute('max'),
|
|
'staticmode' : 1,
|
|
}
|
|
)
|
|
) + serializeFilterArrays();
|
|
return targeturl;
|
|
}
|
|
$('#control-staticdownload').on('click', function(){
|
|
window.open(generateTargetUrl(window.location.href), '_blank');
|
|
});
|
|
|
|
$('#control-staticdownload-raw').on('click', function(){
|
|
var prefix = rcd_globals.history_endpoint_prefix+"staticdata.json?";
|
|
console.log(generateTargetUrl(prefix))
|
|
window.open(generateTargetUrl(prefix), '_blank');
|
|
});
|
|
|
|
|
|
$('#control-persist').on('click', function(){
|
|
$(this).toggleClass("active");
|
|
persistPaths = $(this).hasClass("active");
|
|
try{
|
|
localStorage.setItem("ol_persistPaths", persistPaths);
|
|
}catch{}
|
|
|
|
if(getActiveDisplaystyle().drawAllTracks === true){
|
|
removeAllPlaneAndTrackFeatures();
|
|
} else {
|
|
cleanTracksFromOtherSrc(highlightedPlane, null);
|
|
}
|
|
|
|
//FIXME toggle off-> remove flightpaths
|
|
});
|
|
|
|
/* prevent play/pause buttons being activated by the keyboard since this is handled via a custom spacebar handler */
|
|
var preventFocusClick = function(event) {
|
|
if(event.keyCode == 9) return true; // allow tab-focussing across button
|
|
else event.preventDefault();
|
|
}
|
|
$('#control-play').on('keydown', preventFocusClick);
|
|
$('#control-pause').on('keydown', preventFocusClick);
|
|
|
|
function parseAndSetPlaybackspeed(str, savePermanently){
|
|
var parsedPlaybackSpeedFactor = parseInt(str, 10);
|
|
|
|
if(parsedPlaybackSpeedFactor != NaN && parsedPlaybackSpeedFactor>0){
|
|
playbackSpeedFactor = parsedPlaybackSpeedFactor; // only update speedfactor if it is valid globally, else stay with default init
|
|
}
|
|
|
|
// update label of dropdown button
|
|
$("#playbackspeed-button").html(playbackSpeedFactor+'x <span class="caret"></span>');
|
|
if(savePermanently){
|
|
try {
|
|
localStorage.setItem("ol_playbackspeed", playbackSpeedFactor);
|
|
} catch(e){}
|
|
}
|
|
}
|
|
$("#playbackspeed-dropdown li").click(function () {
|
|
parseAndSetPlaybackspeed(
|
|
$(this).attr('data-value'),
|
|
true // save to LocalStorage
|
|
);
|
|
});
|
|
|
|
var playbackWasRunningOnSlideStart = false;
|
|
$("#slider").on('slideStart', function(ev, picker){
|
|
if(playbackRunning) playbackWasRunningOnSlideStart = true;
|
|
else playbackWasRunningOnSlideStart = false;
|
|
playbackRunning=false;
|
|
console.log("pausing playback");
|
|
});
|
|
$("#slider").on('slideStop', function(ev, picker){
|
|
if(playbackWasRunningOnSlideStart){
|
|
playbackChangeState(true);
|
|
iteratingLoopsActive += 1;
|
|
nextTimestep();
|
|
console.log("continuing playback");
|
|
} else {
|
|
nextTimestep(false, false); // just run refresh of data once, don't iterate, don't loop
|
|
}
|
|
});
|
|
|
|
// bind spacebar to pause, resume
|
|
$(':root').on('keyup', function(event){
|
|
if(!event) var event = window.event; // cross-browser shenanigans
|
|
if(event.keyCode === 32) { // this is the spacebar
|
|
if(playbackRunning){
|
|
// pause
|
|
console.log("pausing playback");
|
|
playbackChangeState(false);
|
|
updateSliderTooltipMode(slider, 'show'); // show tooltip only on demand
|
|
} else {
|
|
console.log("continuing playback");
|
|
// resume
|
|
if($('#control-play').prop('disabled')){
|
|
return; // no daterange loaded, won't start
|
|
}
|
|
updateSliderTooltipMode(slider, 'always');
|
|
playbackChangeState(true);
|
|
iteratingLoopsActive += 1;
|
|
nextTimestep();
|
|
console.log("continuing playback");
|
|
}
|
|
}
|
|
return true; // treat all other keys normally;
|
|
});
|
|
|
|
// try to restore last entered daterange and jump to begin
|
|
initializeDaterangePicker(undefined, undefined,
|
|
function(dateRangePreset){
|
|
if(dateRangePreset === true){
|
|
onDaterangepickerApply(undefined, $('#control-timerange').data('daterangepicker'));
|
|
}
|
|
}
|
|
);
|
|
|
|
|
|
}
|
|
|
|
function zoomToHashExtent(map){
|
|
|
|
var hash = {};
|
|
location.hash.replace('#', '').split('&').forEach(function(item){
|
|
hash[item.split('=')[0]] = item.split('=')[1];
|
|
});
|
|
|
|
if(!(hash.vpn && hash.vpe && hash.vps && hash.vpw)){
|
|
return;
|
|
}
|
|
|
|
var rcExtent = { // extent definition, radarcape style
|
|
nelat: parseFloat(hash.vpn),
|
|
nelon: parseFloat(hash.vpe),
|
|
swlat: parseFloat(hash.vps),
|
|
swlon: parseFloat(hash.vpw)
|
|
}
|
|
|
|
wrapLon = function(value){
|
|
var worlds = Math.floor((value + 180) / 360);
|
|
return value - (worlds * 360);
|
|
}
|
|
|
|
var olExtent = [
|
|
wrapLon(rcExtent.swlon), // minx
|
|
rcExtent.swlat, //miny
|
|
wrapLon(rcExtent.nelon), // maxx
|
|
rcExtent.nelat // maxy
|
|
];
|
|
|
|
|
|
historyTimerangeCurrentlyDisplayed = {
|
|
from: parseInt(hash["timeframe.from"]),
|
|
to: parseInt(hash["timeframe.to"])
|
|
};
|
|
|
|
if(JSON.stringify(historyTimerangeCurrentlyDisplayed) == localStorage.getItem("ol-lastStaticTimerangeDisplayed")){
|
|
// the user is viewing the same timerange as before, so we may restore their center and zoomlevel
|
|
// (this is already done in default behaviour!)
|
|
return;
|
|
} else {
|
|
// user views another timerange, update last timerange displayed
|
|
localStorage.setItem("ol-lastStaticTimerangeDisplayed", JSON.stringify(historyTimerangeCurrentlyDisplayed));
|
|
}
|
|
|
|
console.log("zooming to hash extent "+olExtent);
|
|
|
|
var extent = ol.proj.transformExtent(olExtent, 'EPSG:4326', 'EPSG:3857');
|
|
map.getView().fit(extent, {duration: 3000, constrainResolution: false});
|
|
}
|