I wanted to create a program that answers the question “What should you do tomorrow?” based on the weather forecast. The goal was to help people come up with creative things to do tomorrow and plan out their day. Often, we fall back on the same activities or, if it's a nice day, find out we're too late to book something. Then we have regrets.
This was one of the first multi-layered coding projects I’d ever tackled, but it’s pretty simple with the right API and Azure Functions. In this tutorial blog, I’ve split the work into three parts: the webpage, the HTTP trigger, and the JavaScript.
What you’ll need:
- A Microsoft Azure subscription (you can get one for free for a limited time)
- An account at openweather.org (this is totally free for the API we’re using unless you want to upgrade)
- Visual Studio Code (and to make things easier, install the Live Server extension – for testing your webpage along the way – the Azure Functions extension, and the Azure App Service extension)
Part #1: The Webpage
This is the easiest part because the webpage is relatively simple. The most important section is creating the div
and form
elements for the submission form. You need one overall div
(I used the id “container”
), inside of which is a hidden div (id “hidden-weather”
) and a form element (id “zipcode-form”
).
<div id="container">
<div id="hidden-weather" type="hidden"></div>
<form id="zipcode-form" onsubmit="handle(event)">
</form>
</div>
Leave the onsubmit
part for later – that comes with the JS, which is Part #3.
Inside the form
element add two input
tags. The first creates the zip code input box, and the second creates the submit button, which activates the whole process, with the HTTP trigger function and the API.
<input type="text" name="zipcode" id="zipcode-input" accept="5" placeholder="Enter zip code">
<input size="100" type="submit" value="Get recommendations!" id="submit-button-style"></input>
The rest of the code in this section formats the webpage. The code below would be placed inside the div
with the id "container"
.
<div id="weather-result">
<center>
<h3>Weather Forecast for Tomorrow:</h3>
</center>
</div>
<br>
<br>
<div id="recommendations-result">
<center>
<h3>Recommendations:</h3>
</center>
</div>
Now that we have the user interface complete, let's code the Azure function the user will trigger.
Part #2: Azure Functions
This part can get a little complicated because you’re dealing with a lot of things at the same time. I’m just going to cover the most important parts.
Create a Function App resource – once the resource is deployed, create a new HTTP trigger function. Don’t open the trigger yet, though, because you need to install an npm
package in the console. Under Development Tools, open the console and install node-fetch
. This makes calling the API a lot simpler.
Go back to your HTTP trigger and open the Code + Test tab. Create a const
outside of the async function called fetch
.
const fetch = require('node-fetch');
This lets you use the npm
package that was installed earlier.
We'll then define three constants: the first deals with the zip code, while the next one calls the API, and the final one formats the forecast data as a JSON file.
module.exports = async function (context, req) {
context.log('JavaScript HTTP trigger function processed a request.');
const zipcode = (req.query.zipcode || (req.body && req.body.zipcode));
const apiResult = "";
const jsonResult = await apiResult.json();
context.res = {
// status: 200, /* Defaults to 200 */
body: jsonResult
};
}
Let's take a look at the apiResult
constant a bit more closely because that's the most important one.
In your Open Weather account, go to the free 5 day, 3-hour forecast API documentation page here: https://openweathermap.org/forecast5#zip. Go to the “by ZIP code” section and copy the posted link. This link calls the 5 day, 3-hour forecast API.
api.openweathermap.org/data/2.5/forecast?zip={zip code},{country code}&appid={API key}
Back in the HTTP trigger, let's modify our apiResult
using this information, with await fetch ()
and the URL you just copied.
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip={zip code}");
Then, let's make sure it's actually going to use the zip code entered when calling the API, by replacing the placeholder {zip code}
with our variable zipcode
:
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip=" + zipcode);
Next, let's add a few parameters to limit the hours to a full day (i.e. cnt
(or "count") as 8
, for 8 3-hour segments) and use imperial units (instead of scientific ones, like Kelvin).
const apiResult = await fetch ("https://api.openweathermap.org/data/2.5/forecast?zip=" + zipcode + "&cnt=8&units=imperial");
Finally, insert your Open Weather API key at the end, by tacking on &appid=
, followed by your API key.
Part #3: The JavaScript
I’m not going to explain all of the JS either, but I am going to describe how to access certain information and call the trigger function.
async function handle(event) {
event.preventDefault();
var zipcode = document.getElementById("zipcode-input").value;
console.log(zipcode);
const resp = await fetch(
"https://weatherapifunction.azurewebsites.net/api/WeatherAPI?zipcode=" +
zipcode,
{
method: "POST",
}
);
var data = await resp.json();
console.log(data);
const weatherForecastForTommorowNoon = data.list[6];
const weatherForecastCity = data.city;
var output;
// Abbreviated algorithm
if(weatherForecastForTommorowNoon.pop >= .01){
output = "string of rainy day activities";
} else if((weatherForecastForTommorowNoon.pop >= .01) && (weatherForecastForTommorowNoon.weather[0].description == "snow")){
output = "string of snowy day activities";
}
var weatherRegular = `
<!Forecast data from API>
`;
var recommendations = `
<p>${output}</p>
`;
$("#weather-result").html(weatherRegular);
$("#recommendations-result").html(recommendations);
}
The most important section of the code above is the resp
constant. It calls the Azure trigger function using the trigger’s URL and sends the zip code entered on the website to the trigger function (by accessing the form
element “zipcode-input”
created earlier using document.getElementById
).
async function handle(event) {
event.preventDefault();
var zipcode = document.getElementById("zipcode-input").value;
console.log(zipcode);
const resp = await fetch(
"https://weatherapifunction.azurewebsites.net/api/WeatherAPI?zipcode=" +
zipcode,
{
method: "POST",
}
);
Now, the entered zip code is run through the trigger function and used when the API is called. It uses POST
rather than GET
method, as the zip code is being sent to the Azure trigger function.
Notice console.log(data)
– now that the data is logged in the console, we can access it using the next two constants. Const weatherForecastForTomorrowNoon
accesses tomorrow's three-hour forecast data from 1 pm to 4 pm. The next constant accesses the city outputted in the console – it was only used in displaying the general forecast on the website, not in generating activity recommendations.
console.log(data);
const weatherForecastForTommorowNoon = data.list[6];
const weatherForecastCity = data.city;
Next comes the algorithm – basically, create a list of activities, and then match weather to those activities (you need snow for sledding and skiing, sun and wind for sailing and flying a kite). Create if/else statements for these conditions and activities – if weatherForecastForTomorrowNoon.{enterWeatherConditionHere}
is <
, >
, or =
a certain temperature or measurement, make the variable output
equal a string with the activities that are recommended based on the weather conditions.
// Abbreviated algorithm
if(weatherForecastForTommorowNoon.pop >= .01){
output = "string of rainy day activities";
} else if((weatherForecastForTommorowNoon.pop >= .01) && (weatherForecastForTommorowNoon.weather[0].description == "snow")){
output = "string of snowy day activities";
}
After you have created all of these conditions (based on humidity, temperature, wind speed, and precipitation) create four solely weather-based if/else statements that cover all temperatures possible (but no other weather conditions). There may be a day without wind or precipitation, but there will always be temperature.
else if(weatherForecastForTommorowNoon.main.temp <= 30){
output = "string of cold activities";
} else if((weatherForecastForTommorowNoon.main.temp >= 31 && weatherForecastForTommorowNoon.main.feels_like <= 60) && weatherForecastForTommorowNoon.pop == 0){
output = "string of chilly day activities";
}
//Add more conditionals to the end
Now there are no holes in the algorithm. Add these temperature-based statements to the end of your conditionals so that the forecast data is run through these last (because these are the most general statements, and conditionals should always be ordered with the most specific statement first and the most general statement last).
Final Result
Your project should now look something like this: https://github.com/TheDirector23/come-rain-or-shine
Of course, you can always add improvements! The recommendations could be customized based on user inputs (location, personality questions, preferred activities). The recommendations could also be linked to local businesses based on the entered zip code as well (a local marina might be linked to a recommendation to go sailing). Other locations and recommendations could be offered if the conditions aren't great.
That's about it! Hopefully now you're ahead of the weather when it comes to planning for tomorrow.
Top comments (0)