/**
* Maps a weather forecast description to an appropriate icon
* @see https://erikflowers.github.io/weather-icons/api-list.html
* @param {WeatherReading} reading - A weather forecast object
* @returns {string} A keyword matching a CSS class name in the icon set
*/
const fetchWeatherIcon = function (reading) {
const desc = reading.brief || '';
const hour = reading.currency;
const extended = reading.longRange || false;
if ((/(.*clear.*)/iu).test(desc) ||
(/(sun\w*)/iu).test(desc)) {
return hour > 6 && hour < 18 || extended ? 'sunny' : 'clear';
} else if ((/(.*cloud.*)/iu).test(desc) || (/(.*overcast.*)/iu).test(desc)) {
return (/(.*partly.*)/iu).test(desc) ||
(/(.*light.*)/iu).test(desc) ?
'partly-cloudy' :
'cloudy';
} else if ((/(.*rain.*)/iu).test(desc) || (/(.*shower.*)/iu).test(desc)) {
return (/.*light.*/iu).test(desc) ? 'sprinkle' : 'showers';
} else if ((/(.*snow.*)/iu).test(desc)) {
return 'snow';
} else if ((/.*sleet.*/iu).test(desc)) {
return 'rain-mix';
} else if ((/.*thunder.*/iu).test(desc)) {
return 'thunderstorm';
} else if ((/(fog)/iu).test(desc) || (/(mist)/iu).test(desc)) {
return 'fog';
}
return 'clear';
}
/**
* Prepares weather data for display
* @param {WeatherReading} reading
* @returns {WeatherReading} A formatted {@link WeatherReading}.
* @private
*/
const formatData = function (reading) {
const truncate = val => {
const parsedTemp = parseFloat(val).toFixed(0);
return Math.abs(parsedTemp) === 0 ? 0 : parsedTemp;
}
const celsiusToFahrenheit = val => {
const tempInFahrenheit = parseFloat(val) * 9 / 5 + 32;
return `${truncate(parseFloat(val) * 9 / 5 + 32)} \u00B0F`;
}
const millimetersToInches = val => {
return `${(parseFloat(val) * 0.039370).toFixed(2)} inches`;
}
const metersPerSecondToMilesPerHour = val => {
return `${truncate(parseFloat(val) * 3600 / 1609.344)} miles/hr`;
}
reading.brief = reading.brief.replace('_', ' ').toUpperCase();
reading.temp = `${truncate(reading.temp)} \u00B0C`;
reading.high = `${truncate(reading.high)} \u00B0C`;
reading.low = `${truncate(reading.low)} \u00B0C`;
reading.windSpeed = `${truncate(reading.windSpeed)} meters/sec`;
if (reading.rainAmount) {
reading.rainAmount = `${truncate(reading.rainAmount)} mm`;
}
if (reading.snowAmount) {
reading.snowAmount = `${truncate(reading.snowAmount)} mm`;
}
return reading;
}
/**
* Encapsulates weather forecast information
*/
class WeatherReading {
/**
* Initializes a new {@link WeatherReading}
* @param {object} init - The initial values of a new {@link WeatherReading}
* @param {string} init.city - The name of a city
* @param {number} init.latitude - GPS latitude of a city
* @param {number} init.longitude - GPS longitude of a city
* @param {number} init.currency - The hour portion of the forecast timestamp (24-hr format)
* @param {string} init.brief - A short weather description
* @param {number} init.temp - The current recorded temperature
* @param {number} init.low - The forecast high temperature
* @param {number} init.high - The forecast low temperature
* @param {number} init.windSpeed - The current recorded wind speed
* @param {number} init.windDir - The current recorded wind direction
* @param {number} [init.rainAmount] - The forecast rain accumulation
* @param {number} [init.snowAmount] - The forecast snow accumulation
* @param {boolean} init.longRange - Whether or not this {@link WeatherReading} is a long range forecast
*/
constructor(init = {}) {
/** @type {string} */
this.city = init.city;
/** @type {number} */
this.latitude = init.latitude;
/** @type {number} */
this.longitude = init.longitude;
/** @type {number} */
this.currency = init.currency;
/** @type {string} */
this.brief = init.brief;
/** @type {number} */
this.temp = init.temp;
/** @type {number} */
this.low = init.low;
/** @type {number} */
this.high = init.high;
/** @type {number} */
this.windSpeed = init.windSpeed;
/** @type {number} */
this.windDir = init.windDir;
/** @type {number} */
this.rainAmount = init.rainAmount;
/** @type {number} */
this.snowAmount = init.snowAmount;
/** @type {boolean} */
this.longRange = init.longRange;
}
}