marine hourly mjs

This commit is contained in:
Matt Walsh
2022-12-19 15:23:36 -06:00
parent 5d891fb38f
commit 1faef1b589
5 changed files with 264 additions and 1 deletions

View File

@@ -141,6 +141,7 @@ const parseForecast = async (data) => {
const iceAccumulation = expand(data.iceAccumulation.values); // ice icon
const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon
const snowfallAmount = expand(data.snowfallAmount.values); // snow icon
const waveHeight = expand(data.waveHeight.values);
const icons = await determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed);
@@ -152,6 +153,7 @@ const parseForecast = async (data) => {
probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
skyCover: skyCover[idx],
icon: icons[idx],
waveHeight: waveHeight[idx],
}));
};

View File

@@ -0,0 +1,139 @@
// display extended forecast graphically
// technically uses the same data as the local forecast, we'll let the browser do the caching of that
import STATUS from './status.mjs';
import WeatherDisplay from './weatherdisplay.mjs';
import { registerDisplay } from './navigation.mjs';
import getExtendedForecast from './extendedforecast.mjs';
import getHourlyForecast from './hourly.mjs';
class MarineForecast extends WeatherDisplay {
constructor(navId, elemId) {
super(navId, elemId, 'Marine Forecast', false);
// this.showOnProgress = false;
// set timings
this.timing.totalScreens = 1;
}
async getData() {
if (!super.getData()) return;
const [extendedForecast, hourlyForecast] = await Promise.all([
getExtendedForecast(() => this.stillWaiting()),
getHourlyForecast(() => this.stillWaiting()),
]);
if (extendedForecast === undefined || hourlyForecast === undefined) {
this.setStatus(STATUS.failed);
return;
}
// test for all wave heights = 0, no data for wave heights
if (hourlyForecast.every((value) => !value.waveHeight)) {
// total screens = 0 to skip this display
this.totalScreens = 0;
this.setStatus(STATUS.noData);
return;
}
this.data = {
extendedForecast,
hourlyForecast,
};
this.screenIndex = 0;
this.setStatus(STATUS.loaded);
}
async drawCanvas() {
super.drawCanvas();
// determine bounds
// grab the first three or second set of three array elements
const forecast = this.data.slice(0, 2);
// create each day template
const days = forecast.map((Day) => {
const fill = {};
fill.date = Day.dayName;
fill['wind-dir'] = 'NW';
fill['wind-speed'] = '10 - 15kts';
fill['wave-height'] = '1\'';
fill['wave-desc'] = '';
const { low } = Day;
if (low !== undefined) {
fill['value-lo'] = Math.round(low);
}
const { high } = Day;
fill['value-hi'] = Math.round(high);
fill.condition = Day.text;
// draw the icon
fill['wave-icon'] = { type: 'img', src: waveImage('') };
// return the filled template
return this.fillTemplate('day', fill);
});
// empty and update the container
const dayContainer = this.elem.querySelector('.day-container');
dayContainer.innerHTML = '';
dayContainer.append(...days);
this.finishDraw();
}
}
const waveImage = (conditions) => {
const color = 'rgb(172, 165, 251)';
const canvas = document.createElement('canvas');
canvas.width = 150;
canvas.height = 20;
const context = canvas.getContext('2d');
context.imageSmoothingEnabled = false;
let y = 0;
let r = 35;
let arc1 = Math.PI * 0.3;
let arc2 = Math.PI * 0.7;
switch (conditions) {
case 'CHOPPY':
y = -10;
arc1 = Math.PI * 0.2;
arc2 = Math.PI * 0.8;
r = 25;
break;
case 'ROUGH':
y = -5;
arc1 = Math.PI * 0.1;
arc2 = Math.PI * 0.9;
r = 20;
break;
case 'LIGHT':
default:
y = -20;
arc1 = Math.PI * 0.3;
arc2 = Math.PI * 0.7;
r = 35;
break;
}
context.beginPath();
context.arc(35, y, r, arc1, arc2);
context.strokeStyle = color;
context.lineWidth = 4;
context.stroke();
context.beginPath();
context.arc(75, y, r, arc1, arc2);
context.stroke();
context.beginPath();
context.arc(115, y, r, arc1, arc2);
context.stroke();
return canvas.toDataURL();
};
// register display
registerDisplay(new MarineForecast(11, 'marine-forecast'));

View File

@@ -0,0 +1,98 @@
@use 'shared/_colors'as c;
@use 'shared/_utils'as u;
#marine-forecast-html.weather-display {
background-image: url('../images/BackGround8_1.png');
}
.weather-display .main.marine-forecast {
font-family: 'Star4000';
font-size: 24pt;
@include u.text-shadow();
.advisory {
width: 100%;
height: 90px;
overflow: hidden;
.advisory-text {
border: 4px solid black;
width: 75%;
margin-left: auto;
margin-right: auto;
text-align: center;
margin-top: 40px;
;
}
}
.headers {
display: inline-block;
vertical-align: top;
.winds {
text-align: right;
width: 150px;
margin-top: 42px;
margin-bottom: 60px;
}
}
.day-container {
display: inline-block;
}
.day {
padding: 5px;
width: 165px;
display: inline-block;
margin: 0px 15px;
text-align: center;
.date {
color: c.$title-color;
}
.wave {
border: 4px solid #b09ffb;
.wave-icon {
height: 20px;
img {
display: block;
margin-left: auto;
margin-right: auto;
}
}
}
.temperatures {
width: 100%;
margin-top: 5px;
.temperature-block {
display: inline-block;
width: 44%;
vertical-align: top;
>div {
text-align: center;
}
.value {
font-family: 'Star4000 Large';
margin-top: 4px;
}
&.lo .label {
color: c.$extended-low;
}
&.hi .label {
color: c.$title-color;
}
}
}
}
}

View File

@@ -11,4 +11,5 @@
@import 'radar';
@import 'regional-forecast';
@import 'almanac';
@import 'hazards';
@import 'hazards';
@import 'marine-forecast';

View File

@@ -0,0 +1,23 @@
<%- include('header.ejs', { title: 'Marine Forecast' , hasTime: true }) %>
<div class="main has-scroll marine-forecast">
<div class="advisory">
<div class="advisory-text">Small Craft Advisory</div>
</div>
<div class="headers">
<div class="winds">WINDS:</div>
<div class="winds">WAVES:</div>
</div>
<div class="day-container">
<div class="day template">
<div class="date"></div>
<div class="wind-dir"></div>
<div class="wind-speed"></div>
<div class="wave">
<div class="wave-height"></div>
<div class="wave-icon"><img src="" /></div>
<div class="wave-desc"></div>
</div>
</div>
</div>
</div>
<%- include('scroll.ejs') %>