Reputation: 21
I'm building this weatherapp for a project and the api is returning the wind direction in degrees and i want to convert it into to text Nort,northeast etc.
This is the code that im using
const api = {
key: "",
base: "https://api.openweathermap.org/data/2.5/"
}
const searchbox = document.querySelector('.search-box');
searchbox.addEventListener('keypress', setQuery);
function setQuery(evt) {
if (evt.keyCode == 13) {
getResults(searchbox.value);
}
}
function getResults (query) {
fetch(`${api.base}weather?q=${query}&units=metric&APPID=${api.key}`)
.then(weather => {
return weather.json();
}).then(displayResults);
}
function displayResults (weather) {
console.log(weather);
let city = document.querySelector('.location .city');
city.innerText = `${weather.name}, ${weather.sys.country}`;
let now = new Date();
let date = document.querySelector('.location .date');
date.innerText = dateBuilder(now);
let temp = document.querySelector('.current .temp');
temp.innerHTML = `${Math.round(weather.main.temp)}<span>°c</span>`;
let weather_el = document.querySelector('.current .weather');
weather_el.innerText = weather.weather[0].main;
let hilow = document.querySelector('.hi-low');
hilow.innerText = `${Math.round(weather.main.temp_min)}°c / ${Math.round(weather.main.temp_max)}°c`;
let pressure = document.querySelector('.current .pressure');
pressure.innerHTML =`${weather.main.pressure}<span> </span><span>PSi</span>`;
let wind = document.querySelector('.current .wind');
wind.innerHTML= `${weather.wind.speed} m/s`;
let deg = document.querySelector('.current .deg')
deg.innerText=`${weather.wind.deg}`;
}
function dateBuilder (d) {
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
let days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"];
let day = days[d.getDay()];
let date = d.getDate();
let month = months[d.getMonth()];
let year = d.getFullYear();
return `${day} ${date} ${month} ${year}`;
}
This line is returning the degrees
let deg = document.querySelector('.current .deg')
deg.innerText=`${weather.wind.deg}`;
Is there an easy way to convert it?
Upvotes: 2
Views: 2676
Reputation: 1
This 'C' function returns text for degrees in 8 point or 16 point notation. Parameters:
bearing
set between 0 and 360, andm16
set to 1
for 16 point notation or 0
for 8 point notation.char *bearing_text(int bearing, int m16) {
int v;
static char* points[17] = {
"NNE"," NE","ENE"," E","ESE"," SE","SSE"," S",
"SSW"," SW","WSW"," W","WNW"," NW","NNW"," N", " "
};
if (bearing >= 0) {
double d = (double)bearing / 11.25;
v = (int)floor(d);
if (v==0 || (v==1 && m16==0)) v = 32; // always North
v = m16 ? (v-1)/2 : (((v-2)/4)*2)+1;
} else v = 16; // yields 3 spaces for negative bearings
return(points[v]);
}
Upvotes: 0
Reputation: 43920
Cardinals()
FunctionThis function has a 2D array containing 3 sub-arrays:
/*
| Intercardinal - sub08 has 8 compass points
| Meteorological - sub16 has 8 compass points
| Mariner - sub32 has 16 compass points
*/
const sub08 = mtx[0] = ["n", "ne", "e",... "nw"];
const sub16 = mtx[1] = ["n-ne", "e-ne", "e-se",... "n-nw"];
const sub32 = mtx[2] = ["n by e", "ne by n", "ne by e",... "n by "w"];
Simply pass either 0, 1, or 2 and it will return an array of 8, 16, or 32 compass points. This function will be called within the primary function compass()
.
compass()
FunctionThis function will convert a given number in the range of 0 to 359 to a compass point from an array of 8, 16, or 32 compass points provided by function cardinals()
. In Demo A it has two parameters (deg
and pts
) that come from user input via an <input>
and <select>
. In Demo B it has a single parameter (deg
) which comes from the OpenWeather API. The second value pts
is predetermined by the developer by assigning 0, 1, or 2 to an object (cfg.compass
). Other than those differences they are identical. The following are the expressions used to calculate the index number that corresponds to the given number of degrees (deg
).
/**
* Call cardinal() function pts = 0, 1, or 2 returns an array of
* 8, 16, or 32 compass points.
*/
const arr = cardinals(pts);
// 45, 22.5, or 11.25 degrees per compass point
const den = 360 / arr.length;
// Determine the index number of the array of compass points
let idx = Math.round(deg / den);
const pnt = arr[idx];
The rest of the function formats the compass point into a human readable string. In addition, it rotates the arrow icon that proceeds the compass point string when it's displayed by <output id="wind">
.
Standalone
const app = document.forms.app;
const io = app.elements;
const cardinals = (pts) => {
pts = pts >= 0 && pts <= 2 ? pts : 1;
const mtx = [
["n", "ne", "e", "se", "s", "sw", "w", "nw"],
["n-ne", "e-ne", "e-se", "s-se", "s-sw", "w-sw", "w-nw", "n-nw"],
["n by e", "ne by n", "ne by e", "e by n", "e by s", "se by e", "se by s", "s by e", "s by w", "sw by s", "sw by w", "w by s", "w by n", "nw by w", "nw by n", "n by w"]
].slice(0, pts + 1);
if (mtx.length === 1) return mtx.flat();
return mtx.reduce((zip, sub) => {
return zip.map((c, i) => [c, sub[i]]).flat();
});
};
const compass = (deg, pts) => {
const crd = {
n: "north",
e: "east",
s: "south",
w: "west"
};
deg = deg >= 0 && deg <= 359 ? deg : 0;
const arr = cardinals(pts);
const den = 360 / arr.length;
let idx = Math.round(deg / den);
idx = idx >= 0 && idx < arr.length ? idx : 0;
const pnt = arr[idx];
const res = pnt.split("").map(chr => crd[chr] ? crd[chr] : chr).join("");
const ico = io.wind.style;
ico.setProperty("--rot", (deg + 180) + "deg");
return res[0].toUpperCase() + res.slice(1);
};
app.addEventListener("input", (e) => {
if (e.target.name === "cmp") {
res = compass(io.deg.valueAsNumber, Number(io.car.value));
io.wind.value = res;
}
if (io.deg.value === "360") {
io.deg.value = "0";
} else if (io.deg.value === "-1") {
io.deg.value = "359";
}
});
app.addEventListener("submit", (e) => e.preventDefault());
:root {
font: 2ch/1.2 "Segoe UI"
}
fieldset {
display: flex;
flex-flow: column nowrap;
align-items: center;
width: max-content;
padding: 0 1.5rem 1rem;
border-radius: 6px;
}
legend {
margin-bottom: 0.5rem;
font-size: 1.15rem;
}
label {
width: max-content;
margin-top: 0.5rem;
}
input,
select {
margin-top: 0.5rem;
padding: 3px;
border-radius: 4px;
font: inherit;
}
select {
text-align: center;
}
#wind {
width: 90%;
}
#wind::before {
content: "\0021ee";
display: inline-block;
height: 1.5rem;
margin-right: 1rem;
font-size: 2rem;
line-height: 0.6;
translate: 0 0.25rem;
rotate: var(--rot);
}
#wind::after {
content: "\00feff";
display: inline-block;
}
#deg {
width: 3rem;
text-align: right;
}
<form id="app">
<fieldset>
<legend>Wind Direction</legend>
<output id="wind"></output>
<label>
<input id="deg" name="cmp" type="number" min="-1" max="360" value="0"> Degrees
</label>
<label for="car">Compass Points</label>
<select id="car" name="cmp">
<option value="0">8 points Intercardinal</option>
<option value="1" selected>16 points Meteorlogical</option>
<option value="2">32 points Mariner</option>
</select>
</fieldset>
Since I'm not posting an API key the function getWeather()
is bypassed and the json (weather
) is hard coded. Just click the Search because <input id="find">
doesn't function because it provides the value for getWeather()
. The object cfg
is used to configure some internal values predetermined by the developer (e.g. you). View in Full page mode.
Integrated with Weather App
const weather = {"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":300,"main":"Drizzle","description":"light intensity drizzle","icon":"09d"}],"base":"stations","main":{"temp":7.17,"pressure":1012,"humidity":81,"temp_min":6,"temp_max":8},"visibility":10000,"wind":{"speed":1.83286,"deg":318},"clouds":{"all":90},"dt":1485789600,"sys":{"type":1,"id":5091,"message":0.0103,"country":"GB","sunrise":1485762037,"sunset":1485794875},"id":2643743,"name":"London","cod":200};
let cfg = {
base: "https://api.openweathermap.org/data/2.5/weather?q=",
key: "&appid=",
locale: "en-GB",
format: {dateStyle: "full"},
compass: 1 // 0 = 8 points, 1 = 16 points, 2 = 32 points
};
const modal = document.querySelector("dialog");
const app = document.forms.app;
const io = app.elements;
const formatDate = (date) => {
return new Intl.DateTimeFormat(cfg.locale, cfg.format).format(new Date(date));
};
const cardinals = (pts) => {
pts = pts >= 0 && pts <= 2 ? pts : 1;
const mtx = [
["n", "ne", "e", "se", "s", "sw", "w", "nw"],
["n-ne", "e-ne", "e-se", "s-se", "s-sw", "w-sw", "w-nw", "n-nw"],
["n by e", "ne by n", "ne by e", "e by n", "e by s", "se by e", "se by s", "s by e", "s by w", "sw by s", "sw by w", "w by s", "w by n", "nw by w", "nw by n", "n by w"]
].slice(0, pts + 1);
if (mtx.length === 1) return mtx.flat();
return mtx.reduce((zip, sub) => {
return zip.map((c, i) => [c, sub[i]]).flat();
});
};
const compass = (deg) => {
const crd = {n: "north", e: "east", s: "south", w: "west"};
deg = deg >= 0 && deg <= 359 ? deg : 0;
const arr = cardinals(cfg.compass);
const den = 360 / arr.length;
let idx = Math.round(deg / den);
idx = idx >= 0 && idx < arr.length ? idx : 0;
const pnt = arr[idx];
const res = pnt.split("").map(chr => crd[chr] ? crd[chr] : chr).join("");
const ico = io.wind.style;
ico.setProperty("--rot", (deg + 180) + "deg");
return res[0].toUpperCase() + res.slice(1);
};
const setWeather = (json, unit = false) => {
const uni = {
i: ["°F", "miles/hr"],
m: ["°C", "meters/sec"]
};
let u = unit ? uni.i : uni.m;
io.city.value = `${json.name}, ${json.sys.country}`;
io.date.value = formatDate(new Date());
io.temp.value = `${Math.round(json.main.temp)} ${u[0]}`;
io.cond.value = json.weather[0].main;
io.hilo.value = `${Math.round(json.main.temp_max)} / ${Math.round(json.main.temp_min)} ${u[0]}`;
io.xatm.value = `${json.main.pressure} hPa`;
io.wind.value = `${compass(json.wind.deg)} at ${json.wind.speed.toFixed(2)} ${u[1]}`;
};
const getWeather = async (qry) => {
let imp = /-us|-lr|-mm/i.test(cfg.locale);
let uni = imp ? "imperial" : "metric";
const url = `${api.base}${qry}&units=${uni}${key}`;
const resp = await fetch(url);
const data = await resp.json();
return setWeather(data, imp);
};
app.addEventListener("submit", (e) => {
e.preventDefault();
io.find.value;
// getWeather(io.find.value);
setWeather(weather);
modal.showModal();
});
app.addEventListener("reset", (e) => {
modal.close();
});
:root {
font: 2ch/1.2 "Segoe UI"
}
dialog {
padding: 0;
border: 0;
background: transparent;
}
form,
fieldset {
border: 1.51515px outset rgb(128 128 128);
border-radius: 6px;
background: #fff;
}
form {
width: max-content;
padding: 0.5rem 0.5rem 1rem;
box-shadow: rgb(38, 57, 77) 0px 20px 30px -10px;
}
fieldset {
display: flex;
flex-flow: column nowrap;
width: max-content;
padding: 0 1.5rem 1rem;
}
legend {
margin-bottom: 0.5rem;
font-size: 1.15rem;
}
label {
margin-top: 0.5rem;
}
input,
button {
padding: 3px;
border-radius: 4px;
font: inherit;
}
input {
border: 1.51515px inset rgb(128 128 128);
}
button {
margin-top: 0.75rem;
cursor: pointer
}
.split {
display: flex;
justify-content: space-between;
}
.block {
margin-bottom: 0.5rem;
}
dialog fieldset {
box-shadow: rgba(0, 0, 0, 0.25) 0px 14px 28px, rgba(0, 0, 0, 0.22) 0px 10px 10px;
}
#wind::before {
content: "\0021ee";
display: inline-block;
height: 1.5rem;
margin-right: 1rem;
font-size: 2rem;
line-height: 0.6;
translate: 0 0.25rem;
rotate: var(--rot);
}
<fieldset form="app">
<legend>Weather</legend>
<input id="find" type="search" form="app">
<button form="app">Search</button>
</fieldset>
<dialog>
<form id="app">
<fieldset>
<legend>
<output id="city"></output>
</legend>
<output id="date"></output>
<label class="split">
<output id="cond"></output>
<output id="temp"></output>
</label>
<label class="split">
High / Low <output id="hilo"></output>
</label>
<label class="split">
Barometric Pressure <output id="xatm"></output>
</label>
<label><div class="block">Wind Direction & Speed</div>
<output id="wind"></output>
</label>
<button type="reset">Close</button>
</fieldset>
</form>
</dialog>
Upvotes: 0
Reputation: 345
You can get the amount of degrees and turn it into the index of all directions. Basically turning 0-360 into 0-8, rounding to ensure a whole number. Then you have an index to use in an array of all directions.
// Insert the amount of degrees here
degrees = 10;
// Define array of directions
directions = ['north', 'northeast', 'east', 'southeast', 'south', 'southwest', 'west', 'northwest'];
// Split into the 8 directions
degrees = degrees * 8 / 360;
// round to nearest integer.
degrees = Math.round(degrees, 0);
// Ensure it's within 0-7
degrees = (degrees + 8) % 8
console.log(directions[degrees])
Upvotes: 7