mirror of
https://github.com/netbymatt/ws4kp.git
synced 2026-04-18 17:49:31 -07:00
Compare commits
2 Commits
v6.1.10
...
marine-for
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c70d965347 | ||
|
|
1faef1b589 |
@@ -141,6 +141,7 @@ const parseForecast = async (data) => {
|
|||||||
const iceAccumulation = expand(data.iceAccumulation.values); // ice icon
|
const iceAccumulation = expand(data.iceAccumulation.values); // ice icon
|
||||||
const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon
|
const probabilityOfPrecipitation = expand(data.probabilityOfPrecipitation.values); // rain icon
|
||||||
const snowfallAmount = expand(data.snowfallAmount.values); // snow 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);
|
const icons = await determineIcon(skyCover, weather, iceAccumulation, probabilityOfPrecipitation, snowfallAmount, windSpeed);
|
||||||
|
|
||||||
@@ -152,6 +153,7 @@ const parseForecast = async (data) => {
|
|||||||
probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
|
probabilityOfPrecipitation: probabilityOfPrecipitation[idx],
|
||||||
skyCover: skyCover[idx],
|
skyCover: skyCover[idx],
|
||||||
icon: icons[idx],
|
icon: icons[idx],
|
||||||
|
waveHeight: waveHeight[idx],
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
139
server/scripts/modules/marineforecast.mjs
Normal file
139
server/scripts/modules/marineforecast.mjs
Normal 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 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 hourlyForecast = await getHourlyForecast(() => this.stillWaiting());
|
||||||
|
if (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 = 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 = {};
|
||||||
|
const waveHeight = Math.round(Day.waveHeight * 3.281);
|
||||||
|
fill.date = Day.dayName;
|
||||||
|
fill['wind-dir'] = Day.windDirection;
|
||||||
|
fill['wind-speed'] = '10 - 15kts';
|
||||||
|
fill['wave-height'] = `${waveHeight}'`;
|
||||||
|
fill['wave-desc'] = waveDesc(waveHeight);
|
||||||
|
|
||||||
|
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();
|
||||||
|
};
|
||||||
|
|
||||||
|
const waveDesc = (waveHeight) => {
|
||||||
|
if (waveHeight > 7) return 'ROUGH';
|
||||||
|
if (waveHeight > 4) return 'CHOPPY';
|
||||||
|
return 'LIGHT';
|
||||||
|
};
|
||||||
|
|
||||||
|
// register display
|
||||||
|
registerDisplay(new MarineForecast(11, 'marine-forecast'));
|
||||||
98
server/styles/scss/_marine-forecast.scss
Normal file
98
server/styles/scss/_marine-forecast.scss
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -11,4 +11,5 @@
|
|||||||
@import 'radar';
|
@import 'radar';
|
||||||
@import 'regional-forecast';
|
@import 'regional-forecast';
|
||||||
@import 'almanac';
|
@import 'almanac';
|
||||||
@import 'hazards';
|
@import 'hazards';
|
||||||
|
@import 'marine-forecast';
|
||||||
@@ -42,7 +42,7 @@
|
|||||||
<script type="module" src="scripts/modules/regionalforecast.mjs"></script>
|
<script type="module" src="scripts/modules/regionalforecast.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/travelforecast.mjs"></script>
|
<script type="module" src="scripts/modules/travelforecast.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/progress.mjs"></script>
|
<script type="module" src="scripts/modules/progress.mjs"></script>
|
||||||
<script type="module" src="scripts/modules/radar.mjs"></script>
|
<script type="module" src="scripts/modules/marineforecast.mjs"></script>
|
||||||
<script type="module" src="scripts/index.mjs"></script>
|
<script type="module" src="scripts/index.mjs"></script>
|
||||||
|
|
||||||
<!-- data -->
|
<!-- data -->
|
||||||
@@ -107,6 +107,9 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="almanac-html" class="weather-display">
|
<div id="almanac-html" class="weather-display">
|
||||||
<%- include('partials/almanac.ejs') %>
|
<%- include('partials/almanac.ejs') %>
|
||||||
|
</div>
|
||||||
|
<div id="marine-forecast-html" class="weather-display">
|
||||||
|
<%- include('partials/marine-forecast.ejs') %>
|
||||||
</div>
|
</div>
|
||||||
<div id="extended-forecast-html" class="weather-display">
|
<div id="extended-forecast-html" class="weather-display">
|
||||||
<%- include('partials/extended-forecast.ejs') %>
|
<%- include('partials/extended-forecast.ejs') %>
|
||||||
|
|||||||
23
views/partials/marine-forecast.ejs
Normal file
23
views/partials/marine-forecast.ejs
Normal 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') %>
|
||||||
@@ -51,5 +51,9 @@
|
|||||||
"editor.defaultFormatter": "j69.ejs-beautify"
|
"editor.defaultFormatter": "j69.ejs-beautify"
|
||||||
},
|
},
|
||||||
"files.exclude": {},
|
"files.exclude": {},
|
||||||
|
"editor.formatOnSave": true,
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll.eslint": "explicit"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user