Forecast.io Project

Using Node.js, I created a program run on the command line that accepts a zip code argument. Then, it does the following:

  1. Uses a GET request to the Geonames.org API
  2. Returns the latitude and longitude for the postal code provided
  3. Uses a second GET request to the Forecast.io API
  4. Returns the forecast summary for the given latitude and longitude
  5. Logs out the forecast to the command line

After downloading the project files and installing Node.js, you can run the program like this:

node request.js 33811

The program accepts an unlimited number of zip codes so you could run:

node request.js 33811 33803 80005 80301

You can see the full project files up on GitHub. I’ll walk through my thought processes and the challenges below.

In scoping out the project, one issue became painfully obvious – The Forecast.io API didn’t accept postal code arguments; it only accepts latitude and longitude arguments. Luckily, with a quick Google search, it turns out that Geonames.org has an API that takes a zip code argument and returns the latitude and longitude for that location. Success!

The format for the requests to both Forecast.io and Geonames.org were pretty similar. Here was the GET function for the Geonames.org piece:

[code language=”javascript”]
// Create get function that connects to API and returns data
function get(zipCode) {
// Connect to Geonames API
var request = http.get("http://api.geonames.org/postalCodeSearchJSON?postalcode=" + zipCode + "&maxRows=1&username=jeremey", function (response){
var body = "";
// Read the data
response.on(‘data’, function (chunk) {
// Add the data to a chunk
body += chunk;
});

// Tell the function what to do on end
response.on(‘end’, function() {
if(response.statusCode == 200) {
// Parse the data
try {
// Set the variable zipInfo to the JSON data
var zipInfo = JSON.parse(body);
if (zipInfo.postalCodes[0] === undefined) {
printError({message: "There was an error getting info for " + zipCode + "."})
} else {
// Assign variables for zipCode, latitude, and longitude
// Using index of 0 because I only want the first option for a given postal code
zipCode = zipInfo.postalCodes[0].postalCode;
latitude = zipInfo.postalCodes[0].lat;
longitude = zipInfo.postalCodes[0].lng;
// Get the forecast for the latitude and longitude
forecast.get(latitude,longitude,zipCode);
}
} catch(error) {
// Parse error
printError(error);
}
} else {
// Status code error
printError({message: "There was an error getting info for " + zipCode + "."})
}
});
});
request.on(‘error’, printError);
};
[/code]

In sudo code, this function takes a zip code argument and feeds that zip code into an API call. As it receives the response, it adds data to the variable “body.” Once the response is finished, it parses the JSON data. Then, it sets variables for zipCode, latitude, and longitude based on the JSON data and runs that data through the Forecast.io call. In doing this project, I learned that the same five digit zip code can refer to multiple places. I chose to always reference the first item (index 0), which is why you see postCodes[0] being referenced.

The Forecast.io call was practically identical:

[code language=”javascript”]
// Create get function that connects to API and returns data
function get(latitude,longitude,zipCode) {
// Connect to Forecast.io API
var request = https.get("https://api.forecast.io/forecast/585f3d6f248d7de626a49f83350e3c79/" + latitude + "," + longitude, function (response){
var body = "";
// Read the data
response.on(‘data’, function (chunk) {
// Add the data to a chunk
body += chunk;
});

// Tell the function what to do on end
response.on(‘end’, function() {
if(response.statusCode == 200) {
// Parse the data
try {
var forecast = JSON.parse(body);
// Log out the weather summary
console.log("For the postal code " + zipCode +", the weekly forecast is: " + forecast.daily.summary);
} catch(error) {
// Parse error
printError(error);
}
} else {
// Status code error
printError({message: "There was an error getting info for " + latitude +" ," + longitude + "."})
}
});
});
request.on(‘error’, printError);
};
[/code]

The only difference here is that it prints the daily summary out to the console after it parses the data. These GET calls are executed through a third file I named request.js. Here’s the basic format:

[code language=”javascript”]
for(var i=2; i<process.argv.length; i++) {
geonames.get(process.argv[i])
};
[/code]

Assuming you pass in a postal code, process.argv will always return at least three elements:

  • Node
  • The name of the JavaScript file
  • Any additional command line arguments

Therefore, I can use a for loop setting the counter to 2 to leave out the first two arguments and only run the additional commands (zip codes) through the Geonames GET call.

Here’s a GIF of the finished project in action:

A demo of the forecast command line application.