ws4kp/server/scripts/modules/navigation.js

295 lines
8.9 KiB
JavaScript
Raw Normal View History

2020-09-04 18:02:20 +00:00
'use strict';
// navigation handles progress, next/previous and initial load messages from the parent frame
2020-09-08 03:48:10 +00:00
/* globals utils, _StationInfo, STATUS, draw */
2020-09-04 22:03:03 +00:00
/* globals CurrentWeather, LatestObservations, TravelForecast, RegionalForecast, LocalForecast, ExtendedForecast, Almanac, Radar */
2020-09-04 18:02:20 +00:00
document.addEventListener('DOMContentLoaded', () => {
navigation.init();
});
const UNITS = {
english: Symbol('english'),
metric: Symbol('metric'),
};
const navigation = (() => {
let weatherParameters = {};
let displays = [];
let initialLoadDone = false;
let currentUnits = UNITS.english;
let playing = false;
2020-09-08 03:48:10 +00:00
let backgroundImage;
2020-09-04 18:02:20 +00:00
2020-09-08 03:48:10 +00:00
const init = async () => {
2020-09-04 18:02:20 +00:00
// set up message receive and dispatch accordingly
window.addEventListener('message', (event) => {
// test for trust
if (!event.isTrusted) return;
// get the data
const data = JSON.parse(event.data);
// dispatch event
if (!data.type) return;
switch (data.type) {
case 'latLon':
getWeather(data.message);
break;
case 'units':
setUnits(data.message);
break;
case 'navButton':
handleNavButton(data.message);
break;
default:
console.error(`Unknown event ${data.type}`);
}
}, false);
2020-09-08 03:48:10 +00:00
backgroundImage = await utils.image.load('images/BackGround1_1.png');
drawProgressCanvas();
2020-09-04 18:02:20 +00:00
};
const postMessage = (type, message = {}) => {
const parent = window.parent;
parent.postMessage(JSON.stringify({type, message}, window.location.origin));
};
const getWeather = async (latLon) => {
// reset statuses
initialLoadDone = false;
// get initial weather data
const point = await utils.weather.getPoint(latLon.lat, latLon.lon);
// get stations
const stations = await $.ajax({
type: 'GET',
url: point.properties.observationStations,
dataType: 'json',
crossDomain: true,
});
const StationId = stations.features[0].properties.stationIdentifier;
let city = point.properties.relativeLocation.properties.city;
if (StationId in _StationInfo) {
city = _StationInfo[StationId].City;
city = city.split('/')[0];
}
// populate the weather parameters
weatherParameters.latitude = latLon.lat;
weatherParameters.longitude = latLon.lon;
weatherParameters.zoneId = point.properties.forecastZone.substr(-6);
weatherParameters.radarId = point.properties.radarStation.substr(-3);
weatherParameters.stationId = StationId;
weatherParameters.weatherOffice = point.properties.cwa;
weatherParameters.city = city;
weatherParameters.state = point.properties.relativeLocation.properties.state;
weatherParameters.timeZone = point.properties.relativeLocation.properties.timeZone;
weatherParameters.forecast = point.properties.forecast;
weatherParameters.stations = stations.features;
// update the main process for display purposes
postMessage('weatherParameters', weatherParameters);
// start loading canvases if necessary
if (displays.length === 0) {
displays = [
new CurrentWeather(0,'currentWeather', weatherParameters),
new LatestObservations(1, 'latestObservations', weatherParameters),
new TravelForecast(2, 'travelForecast', weatherParameters),
// Regional Forecast: 0 = regional conditions, 1 = today, 2 = tomorrow
new RegionalForecast(3, 'regionalForecast0', weatherParameters, 0),
new RegionalForecast(4, 'regionalForecast1', weatherParameters, 1),
new RegionalForecast(5, 'regionalForecast2', weatherParameters, 2),
new LocalForecast(6, 'localForecast', weatherParameters),
new ExtendedForecast(7, 'extendedForecast', weatherParameters),
2020-09-06 01:01:13 +00:00
new Almanac(8, 'almanac', weatherParameters),
2020-09-04 22:03:03 +00:00
new Radar(8, 'radar', weatherParameters),
2020-09-04 18:02:20 +00:00
];
} else {
// or just call for new data if the canvases already exist
displays.forEach(display => display.getData(weatherParameters));
}
// GetMonthPrecipitation(this.weatherParameters);
// GetAirQuality3(this.weatherParameters);
// ShowDopplerMap(this.weatherParameters);
// GetWeatherHazards3(this.weatherParameters);
};
// receive a status update from a module {id, value}
const updateStatus = (value) => {
// skip if initial load
if (initialLoadDone) return;
// test for loaded status
if (value.status !== STATUS.loaded) return;
2020-09-08 03:48:10 +00:00
drawProgressCanvas();
2020-09-04 18:02:20 +00:00
// send loaded messaged to parent
2020-09-08 03:48:10 +00:00
if (countLoadedCanvases < displays.length) return;
2020-09-04 18:02:20 +00:00
postMessage('loaded');
// store the display number
};
2020-09-08 03:48:10 +00:00
const countLoadedCanvases = () => displays.reduce((acc, display) => {
if (display.status !== STATUS.loading) return acc+1;
return acc;
},0);
2020-09-04 18:02:20 +00:00
const hideAllCanvases = () => {
displays.forEach(display => display.hideCanvas());
};
const units = () => currentUnits;
const setUnits = (_unit) => {
const unit = _unit.toLowerCase();
if (unit === 'english') {
currentUnits = UNITS.english;
} else {
currentUnits = UNITS.metric;
}
// TODO: refresh current screen
};
// is playing interface
const isPlaying = () => playing;
// navigation message constants
const msg = {
response: { // display to navigation
previous: Symbol('previous'), // already at first frame, calling function should switch to previous canvas
inProgress: Symbol('inProgress'), // have data to display, calling function should do nothing
next: Symbol('next'), // end of frames reached, calling function should switch to next canvas
},
command: { // navigation to display
firstFrame: Symbol('firstFrame'),
previousFrame: Symbol('previousFrame'),
nextFrame: Symbol('nextFrame'),
lastFrame: Symbol('lastFrame'), // used when navigating backwards from the begining of the next canvas
},
};
// receive naivgation messages from displays
const displayNavMessage = (message) => {
if (message.type === msg.response.previous) loadDisplay(-1);
if (message.type === msg.response.next) loadDisplay(1);
};
// navigate to next or previous
const navTo = (direction) => {
if (direction === msg.command.nextFrame) currentDisplay().navNext();
if (direction === msg.command.previousFrame) currentDisplay().navPrev();
};
// find the next or previous available display
const loadDisplay = (direction) => {
const totalDisplays = displays.length;
const curIdx = currentDisplayIndex();
let idx;
for (let i = 0; i < totalDisplays; i++) {
// convert form simple 0-10 to start at current display index +/-1 and wrap
idx = utils.calc.wrap(curIdx+(i+1)*direction,totalDisplays);
if (displays[idx].status === STATUS.loaded) break;
}
const newDisplay = displays[idx];
// hide all displays
hideAllCanvases();
// show the new display and navigate to an appropriate display
if (direction < 0) newDisplay.showCanvas(msg.command.lastFrame);
if (direction > 0) newDisplay.showCanvas(msg.command.firstFrame);
};
// get the current display index or value
const currentDisplayIndex = () => {
const index = displays.findIndex(display=>display.isActive());
if (index === undefined) console.error('No active display');
return index;
};
const currentDisplay = () => {
return displays[currentDisplayIndex()];
};
const setPlaying = (newValue) => {
playing = newValue;
postMessage('isPlaying', playing);
};
// handle all navigation buttons
const handleNavButton = (button) => {
switch (button) {
case 'playToggle':
setPlaying(!playing);
break;
case 'next':
setPlaying(false);
navTo(msg.command.nextFrame);
break;
case 'previous':
setPlaying(false);
navTo(msg.command.previousFrame);
break;
default:
console.error(`Unknown navButton ${button}`);
}
};
2020-09-08 03:48:10 +00:00
const drawProgressCanvas = () => {
const canvas = document.getElementById('progressCanvas');
const context = canvas.getContext('2d');
context.drawImage(backgroundImage, 0, 100, 640, 300, 0, 100, 640, 300);
draw.horizontalGradientSingle(context, 0, 90, 52, 399, draw.sideColor1, draw.sideColor2);
draw.horizontalGradientSingle(context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2);
displays.forEach((display, idx) => {
const y = 120 + idx*29;
const dots = Array(120 - Math.floor(display.name.length * 2.5)).join('.');
draw.text(context, 'Star4000 Extended', '19pt', '#ffffff', 70, y, display.name + dots, 2);
// Erase any dots that spill into the status text.
context.drawImage(backgroundImage, 475, y - 20, 165, 30, 475, y - 20, 165, 30);
draw.horizontalGradientSingle(context, 584, 90, 640, 399, draw.sideColor1, draw.sideColor2);
let statusText;
let statusColor;
switch (display.status) {
case STATUS.loading:
statusText = 'Loading';
statusColor = '#ffff00';
break;
case STATUS.loaded:
statusText = 'Press Here';
statusColor = '#00ff00';
break;
case STATUS.failed:
statusText = 'Failed';
statusColor = '#ff0000';
break;
case STATUS.noData:
statusText = 'No Data';
statusColor = '#C0C0C0';
draw.box(context, 'rgb(33, 40, 90)', 475, y - 15, 75, 15);
break;
default:
}
draw.text(context, 'Star4000 Extended', '19pt', statusColor, 565, y, statusText, 2, 'end');
});
};
2020-09-04 18:02:20 +00:00
return {
init,
updateStatus,
units,
isPlaying,
displayNavMessage,
msg,
};
})();