2020-09-04 18:02:20 +00:00
|
|
|
// current weather conditions display
|
2022-11-22 22:19:10 +00:00
|
|
|
import { distance as calcDistance, directionToNSEW } from './utils/calc.mjs';
|
|
|
|
import { json } from './utils/fetch.mjs';
|
|
|
|
import STATUS from './status.mjs';
|
|
|
|
import { locationCleanup } from './utils/string.mjs';
|
2022-12-06 22:25:28 +00:00
|
|
|
import { celsiusToFahrenheit, kphToMph } from './utils/units.mjs';
|
2022-11-22 22:29:10 +00:00
|
|
|
import WeatherDisplay from './weatherdisplay.mjs';
|
2022-12-06 22:14:56 +00:00
|
|
|
import { registerDisplay } from './navigation.mjs';
|
2020-09-04 18:02:20 +00:00
|
|
|
|
|
|
|
class LatestObservations extends WeatherDisplay {
|
2020-10-29 21:44:28 +00:00
|
|
|
constructor(navId, elemId) {
|
2022-11-22 03:50:22 +00:00
|
|
|
super(navId, elemId, 'Latest Observations', true);
|
2020-09-04 18:02:20 +00:00
|
|
|
|
|
|
|
// constants
|
|
|
|
this.MaximumRegionalStations = 7;
|
|
|
|
}
|
|
|
|
|
2020-10-29 21:44:28 +00:00
|
|
|
async getData(_weatherParameters) {
|
2022-12-07 17:02:51 +00:00
|
|
|
if (!super.getData(_weatherParameters)) return;
|
2020-10-29 21:44:28 +00:00
|
|
|
const weatherParameters = _weatherParameters ?? this.weatherParameters;
|
2020-09-25 14:55:29 +00:00
|
|
|
|
2020-09-04 18:02:20 +00:00
|
|
|
// calculate distance to each station
|
2020-10-29 21:44:28 +00:00
|
|
|
const stationsByDistance = Object.keys(StationInfo).map((key) => {
|
|
|
|
const station = StationInfo[key];
|
2022-11-22 22:19:10 +00:00
|
|
|
const distance = calcDistance(station.lat, station.lon, weatherParameters.latitude, weatherParameters.longitude);
|
2020-10-29 21:44:28 +00:00
|
|
|
return { ...station, distance };
|
2020-09-04 18:02:20 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
// sort the stations by distance
|
2020-10-29 21:44:28 +00:00
|
|
|
const sortedStations = stationsByDistance.sort((a, b) => a.distance - b.distance);
|
2020-09-04 18:02:20 +00:00
|
|
|
// try up to 30 regional stations
|
2020-10-29 21:44:28 +00:00
|
|
|
const regionalStations = sortedStations.slice(0, 30);
|
2020-09-04 18:02:20 +00:00
|
|
|
|
|
|
|
// get data for regional stations
|
2022-12-22 20:46:58 +00:00
|
|
|
// get first 7 stations
|
|
|
|
const actualConditions = [];
|
|
|
|
let lastStation = Math.min(regionalStations.length, 7);
|
|
|
|
let firstStation = 0;
|
|
|
|
while (actualConditions.length < 7 && (lastStation) <= regionalStations.length) {
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
const someStations = await getStations(regionalStations.slice(firstStation, lastStation));
|
|
|
|
|
|
|
|
actualConditions.push(...someStations);
|
|
|
|
// update counters
|
|
|
|
firstStation += lastStation;
|
|
|
|
lastStation = Math.min(regionalStations.length + 1, firstStation + 7 - actualConditions.length);
|
|
|
|
}
|
|
|
|
|
2020-09-04 18:02:20 +00:00
|
|
|
// cut down to the maximum of 7
|
2020-10-29 21:44:28 +00:00
|
|
|
this.data = actualConditions.slice(0, this.MaximumRegionalStations);
|
2020-09-04 18:02:20 +00:00
|
|
|
|
2020-09-09 20:23:19 +00:00
|
|
|
// test for at least one station
|
2023-01-06 20:39:39 +00:00
|
|
|
if (this.data.length === 0) {
|
2020-09-09 20:23:19 +00:00
|
|
|
this.setStatus(STATUS.noData);
|
|
|
|
return;
|
|
|
|
}
|
2020-09-17 21:34:38 +00:00
|
|
|
this.setStatus(STATUS.loaded);
|
2020-09-04 18:02:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
async drawCanvas() {
|
|
|
|
super.drawCanvas();
|
|
|
|
const conditions = this.data;
|
|
|
|
|
|
|
|
// sort array by station name
|
2020-10-29 21:44:28 +00:00
|
|
|
const sortedConditions = conditions.sort((a, b) => ((a.Name < b.Name) ? -1 : 1));
|
2020-09-04 18:02:20 +00:00
|
|
|
|
2022-12-06 22:25:28 +00:00
|
|
|
this.elem.querySelector('.column-headers .temp.english').classList.add('show');
|
|
|
|
this.elem.querySelector('.column-headers .temp.metric').classList.remove('show');
|
2020-09-04 18:02:20 +00:00
|
|
|
|
2022-08-04 17:49:04 +00:00
|
|
|
const lines = sortedConditions.map((condition) => {
|
2022-11-22 22:19:10 +00:00
|
|
|
const windDirection = directionToNSEW(condition.windDirection.value);
|
2020-09-04 18:02:20 +00:00
|
|
|
|
2022-12-06 22:25:28 +00:00
|
|
|
const Temperature = Math.round(celsiusToFahrenheit(condition.temperature.value));
|
|
|
|
const WindSpeed = Math.round(kphToMph(condition.windSpeed.value));
|
2020-09-04 18:02:20 +00:00
|
|
|
|
2023-01-06 20:39:39 +00:00
|
|
|
const fill = {
|
|
|
|
location: locationCleanup(condition.city).substr(0, 14),
|
|
|
|
temp: Temperature,
|
|
|
|
weather: shortenCurrentConditions(condition.textDescription).substr(0, 9),
|
|
|
|
};
|
|
|
|
|
2020-09-04 18:02:20 +00:00
|
|
|
if (WindSpeed > 0) {
|
2022-08-04 17:49:04 +00:00
|
|
|
fill.wind = windDirection + (Array(6 - windDirection.length - WindSpeed.toString().length).join(' ')) + WindSpeed.toString();
|
2020-09-04 18:02:20 +00:00
|
|
|
} else if (WindSpeed === 'NA') {
|
2022-08-04 17:49:04 +00:00
|
|
|
fill.wind = 'NA';
|
2020-09-04 18:02:20 +00:00
|
|
|
} else {
|
2022-08-04 17:49:04 +00:00
|
|
|
fill.wind = 'Calm';
|
2020-09-04 18:02:20 +00:00
|
|
|
}
|
|
|
|
|
2022-08-04 17:49:04 +00:00
|
|
|
return this.fillTemplate('observation-row', fill);
|
2020-09-04 18:02:20 +00:00
|
|
|
});
|
2022-08-04 17:49:04 +00:00
|
|
|
|
|
|
|
const linesContainer = this.elem.querySelector('.observation-lines');
|
|
|
|
linesContainer.innerHTML = '';
|
|
|
|
linesContainer.append(...lines);
|
|
|
|
|
2020-09-04 18:02:20 +00:00
|
|
|
this.finishDraw();
|
|
|
|
}
|
2020-10-29 21:44:28 +00:00
|
|
|
}
|
2022-12-09 19:51:51 +00:00
|
|
|
const shortenCurrentConditions = (_condition) => {
|
|
|
|
let condition = _condition;
|
|
|
|
condition = condition.replace(/Light/, 'L');
|
|
|
|
condition = condition.replace(/Heavy/, 'H');
|
|
|
|
condition = condition.replace(/Partly/, 'P');
|
|
|
|
condition = condition.replace(/Mostly/, 'M');
|
|
|
|
condition = condition.replace(/Few/, 'F');
|
|
|
|
condition = condition.replace(/Thunderstorm/, 'T\'storm');
|
|
|
|
condition = condition.replace(/ in /, '');
|
|
|
|
condition = condition.replace(/Vicinity/, '');
|
|
|
|
condition = condition.replace(/ and /, ' ');
|
|
|
|
condition = condition.replace(/Freezing Rain/, 'Frz Rn');
|
|
|
|
condition = condition.replace(/Freezing/, 'Frz');
|
|
|
|
condition = condition.replace(/Unknown Precip/, '');
|
|
|
|
condition = condition.replace(/L Snow Fog/, 'L Snw/Fog');
|
|
|
|
condition = condition.replace(/ with /, '/');
|
|
|
|
return condition;
|
|
|
|
};
|
2022-12-22 20:46:58 +00:00
|
|
|
|
|
|
|
const getStations = async (stations) => {
|
|
|
|
const stationData = await Promise.all(stations.map(async (station) => {
|
|
|
|
try {
|
|
|
|
const data = await json(`https://api.weather.gov/stations/${station.id}/observations/latest`, { retryCount: 1, stillWaiting: () => this.stillWaiting() });
|
|
|
|
// test for temperature, weather and wind values present
|
|
|
|
if (data.properties.temperature.value === null
|
|
|
|
|| data.properties.textDescription === ''
|
|
|
|
|| data.properties.windSpeed.value === null) return false;
|
|
|
|
// format the return values
|
|
|
|
return {
|
|
|
|
...data.properties,
|
|
|
|
StationId: station.id,
|
|
|
|
city: station.city,
|
|
|
|
};
|
2023-01-06 20:39:39 +00:00
|
|
|
} catch (error) {
|
2022-12-22 20:46:58 +00:00
|
|
|
console.log(`Unable to get latest observations for ${station.id}`);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
// filter false (no data or other error)
|
|
|
|
return stationData.filter((d) => d);
|
|
|
|
};
|
2022-12-06 22:14:56 +00:00
|
|
|
// register display
|
2022-12-14 22:28:33 +00:00
|
|
|
registerDisplay(new LatestObservations(2, 'latest-observations'));
|