There was always snow at Christmas – Retrieving and processing historical weather data with Forecast IO

The weather was unusually mild this winter and many people seemed to remember back to the days of their childhood. In our memories, there was always snow at Christmas, but is that actually true? The curious among us know what to do, so lets see what the Internet has to offer for verifying our hypothesis. Unfortunately there are hardly any open weather data sources allowing to retrieve the weather conditions for any historical date., but in this short blog post I would like to show you how you can combine a few APIs and a little Java programming in order to retrieve the appropriate data. The complete project can be downloaded at GitHub.

What we need

We will use the following technologies to get what we want:

  • Google Geo-Coding API to retrieve longitude and latitude coordinates
  • Forecast IO to retrieve the weather data for a specific location
  • MySQL database to store the data
  • Hibernate
  • Maven

What we want

We want to retrieve weather data for a specific location at a given time. This data should be stored in a relational database which allows processing the data conveniently.

How to get started

Recently I switched from Eclipse to IntelliJ and I am quite happy with it. What ever IDE you prefer, create a new Maven project and copy the following dependencies into the pom.xml in order to retrieve the libraries.

<dependencies>
<!-- Forecast IO API Wrapper -->
<dependency>
<groupId>com.github.dvdme</groupId>
<artifactId>ForecastIOLib</artifactId>
<version>1.5.1</version>
</dependency>

<!-- Google Geo API -->
<dependency>
<groupId>com.google.code.geocoder-java</groupId>
<artifactId>geocoder-java</artifactId>
<version>0.16</version>
</dependency>

<!--. Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.3.4.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-envers</artifactId>
<version>4.3.5.Final</version>
</dependency>

<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>
</dependencies>

Setup the Backend

In order to store the data we want to retrieve, we will create a Java POJO mapping the data structure from the server response. We also need to setup MySQL and Hibernate. The structure of the project is shown in the following image:

Project structure

Create a new database in MySQL and assign user rights. You could also consider using an embedded database if you do not already have a running MySQL instance. The Hibernate configuration file is shown below:


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>

<property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
<property name="show_sql">false</property>
<!--  drop the tables every time: create-drop -->
<!--  use auto or update or create-drop -->
<property name="hibernate.hbm2ddl.auto">validate</property>

<property name="hibernate.current_session_context_class">thread</property>
<property name="format_sql">true</property>
<property name="use_sql_comments">false</property>

<property name="hibernate.connection.autocommit">true</property>

<property name="hibernate.jdbc.batch_size">20</property>

<mapping class="WeatherData.WeatherData"/>

</session-factory>

</hibernate-configuration>

The Hibernate session management is controlled with the following class. As you can see, sensitive information is read from Java property files.


public class HibernateUtil {
private static SessionFactory sessionFactory;
private static ServiceRegistry serviceRegistry;

static {
try {

String filename = "db.properties";
Properties prop = null;

prop = Helpers.readPropertyFile(filename);

String dbhost=prop.getProperty("dbhost");
String dbport=prop.getProperty("dbport");
String dbname=prop.getProperty("dbname");
String dbuser=prop.getProperty("dbuser");
String dbpw=prop.getProperty("dbpassword");

String mysqlString = "jdbc:mysql://" + dbhost + ":"+ dbport+ "/"+ dbname;
System.out.println("db string_ " + mysqlString);
Properties extraProperties=new Properties();
extraProperties.setProperty("hibernate.connection.url",mysqlString);
extraProperties.setProperty("hibernate.connection.username",dbuser);
extraProperties.setProperty("hibernate.connection.password",dbpw);

Configuration configuration = new Configuration();
configuration=configuration.configure("hibernate.cfg.xml");
configuration=configuration.addProperties(extraProperties);

configuration.configure();

serviceRegistry = new StandardServiceRegistryBuilder().applySettings(configuration.getProperties()).build();
sessionFactory = configuration.buildSessionFactory(serviceRegistry);

} catch (HibernateException he) {
System.err.println("Error creating Session: " + he);
throw new ExceptionInInitializerError(he);
}
}

public static SessionFactory getSessionFactory() {
return sessionFactory;
}
}

The database access information will be read during the runtime from the file db.properties, this gives you more flexibility an easier access to the sensitive data. We will use the same technique for the API key that you need to use the forecast API (see below). The property file for the database access simply contains this data:


# host address
dbhost=localhost
# port
dbport=3306
# database name
dbname=<<databasename>>
#user name
dbuser=<<username>>
# password
dbpassword=<<password>>

The Hibernate snippet for storing the data is very simple, it opens a connection and stores the data, which is a Weather Data object.


this.session = HibernateUtil.getSessionFactory().openSession();
this.session.beginTransaction();
this.session.save(weatherData);
this.session.getTransaction().commit();
this.session.flush();
this.session.close();

Google Geo Coder API

Google offers a convenient API which provides you the geo coordinates from any specific address that you provide. The following snippet retrieves the longitude and latitude coordinates. The following snippet shoes you how to get the data:


final Geocoder geocoder = new Geocoder();
GeocoderRequest geocoderRequest = new GeocoderRequestBuilder().setAddress(locationAddress).setLanguage("en").getGeocoderRequest();
GeocodeResponse geocoderResponse = null;

try {
geocoderResponse = geocoder.geocode(geocoderRequest);
} catch (IOException e) {
e.printStackTrace();
}

List<GeocoderResult> geoResultList = geocoderResponse.getResults();

System.out.println("Retrieved geo");
for (GeocoderResult result : geoResultList) {
address = result.getFormattedAddress();
GeocoderGeometry geometry = result.getGeometry();
latitude = String.valueOf(geometry.getLocation().getLat());
longitude = String.valueOf(geometry.getLocation().getLng());

System.out.println(result.getFormattedAddress() + " lat: " + latitude + " long: " + longitude);

}

Now that we have the coordinates, we can pass this data to the Forecast API.


Powered by Forecast IO

Forecast IO is a service which offers a REST API which you can call for retrieving historical weather data for any specific location. You need to register if you want to call the API, which gives you 1000 calls per day for free. The API is very well described and simple to use. We will use the Forecast IO API Wrapper in order to call the API from within Java. The API key is also stored in a property file called api.properties. Copy your code in this file.


forecast-key=<<YOUR API CODE>>

In Java we read teh key with the following snippet:


// set the API key
Helpers helper;
helper = new Helpers();
String filename = "api.properties";
Properties prop = null;
prop = Helpers.readPropertyFile(filename);
this.APIKEY = prop.getProperty("forecast-key");

Now you can access the API with the wrapper library. The code snippet retrieves the hourly weather data for a specified date.

        ForecastIO fio = new ForecastIO(this.APIKEY); //instantiate the class with the API key.
        fio.setUnits(ForecastIO.UNITS_SI);             //sets the units as SI - optional

        fio.setLang(ForecastIO.LANG_ENGLISH);

        fio.setTime(dateString);

        fio.getForecast(latitude, longitude);
      //  System.out.println("Latitude: " + fio.getLatitude());
      //  System.out.println("Longitude: " + fio.getLongitude());
      //  System.out.println("Timezone: " + fio.getTimezone());

        String key ="";
        String value ="";

        FIOHourly hourly = new FIOHourly(fio);

We then need to store the data in a Java object in order to persist it in the database.

        for (int i = 0; i < hourly.hours(); i++) {
            String[] h = hourly.getHour(i).getFieldsArray();
            String hour = String.valueOf(i + 1);
            System.out.println("Hour #" +hour);

            /**
             * Populate the map of data values
             */
            weatherDataHashMap.clear();
            weatherDataHashMap.put("Hour",hour);
            for (int j = 0; j < h.length; j++){

                key = h[j];
                value = hourly.getHour(i).getByKey(h[j]);
                if (value == null){
                    System.out.println("value war NULL");
                    value="";
                }
                System.out.println(key + ": " + value);
                System.out.println("\n");

                weatherDataHashMap.put(key,value);

            }</pre>
<pre>

In a last step we need to populate the Java object and persist it in the database.


/**
* Create the weather object
*/
WeatherData hourData;
hourData = new WeatherData();
System.out.println("---------- " + weatherDataHashMap.get("Hour"));
hourData.setHour(this.parseIntValue(weatherDataHashMap.get("Hour")));
hourData.setSummary(weatherDataHashMap.get("summary"));
hourData.setIcon(weatherDataHashMap.get("icon"));
hourData.setWindspeed(this.parseDoubleValue(weatherDataHashMap.get("windSpeed")));
Date measureData = this.getDateFromString(weatherDataHashMap.get("time"));
hourData.setWeather_timestamp(measureData);
hourData.setHumidity(this.parseDoubleValue(weatherDataHashMap.get("humidity")));
hourData.setVisibility(this.parseDoubleValue(weatherDataHashMap.get("visibility")));
hourData.setWindBearing(this.parseIntValue(weatherDataHashMap.get("windBearing")));
hourData.setApparentTemperature(this.parseDoubleValue(weatherDataHashMap.get("apparentTemperature")));
hourData.setWindBearing(this.parseIntValue(weatherDataHashMap.get("windBearing")));
hourData.setPrecipProbability(this.parseDoubleValue(weatherDataHashMap.get("precipProbability")));
hourData.setPrecipIntensity(this.parseDoubleValue(weatherDataHashMap.get("precipIntensity")));
hourData.setDewPoint(this.parseDoubleValue(weatherDataHashMap.get("dewPoint")));
hourData.setTemperature(this.parseDoubleValue(weatherDataHashMap.get("temperature")));
hourData.setPrecipType(this.removeQuotes(weatherDataHashMap.get("precipType")));
hourData.setAddress(address);
hourData.setLatitude(latitude);
hourData.setLongitude(longitude);

this.persist(hourData);

If you loop over the data you can retrieve a lot of interesting weather data from the services and perform your analysis. You can use the MySQL Workbench directly or export the data for instance into CSV.

Results

The Chart

Now that we have the data, we can use a visualization API such as Google Charts for displaying and interpreting the data. All you need to do is export the data into an appropriate format. The following SQL statement retrieves all measurements at 11 p.m. for the available Christmas days and transforms them into the format which is expected by the Google Charts API.


SELECT DISTINCT CONCAT("[new Date (",YEAR(weather_timestamp),").12.24,",temperature,"],")
FROM weatherDB.WeatherData WHERE hour=23 ORDER BY weather_timestamp DESC;

Then you can use the following HTML and JavaScript code for rendering the data.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<meta name="generator" content=
"HTML Tidy for Linux (vers 25 March 2009), see www.w3.org">
<!--Load the AJAX API-->

<script type="text/javascript" src="https://www.google.com/jsapi">
</script>
<script type="text/javascript">

      // Load the Visualization API and the piechart package.
      google.load('visualization', '1.0', {'packages':['corechart']});

      // Set a callback to run when the Google Visualization API is loaded.
      google.setOnLoadCallback(drawChart);

      // Callback that creates and populates a data table,
      // instantiates the pie chart, passes in the data and
      // draws it.
      function drawChart() {

        // Create the data table.
        var data = new google.visualization.DataTable();
        data.addColumn('date', 'Year');
        data.addColumn('number', 'Degree Celsius');
        data.addRows([
            [new Date (2012,12,24),3.74],
            ....
            [new Date (1943,12,24),-1.81]

        ]);

    // Set chart options
        var options = {'title':'Temperatures at Christmas in Reutte/Tirol',
                       'width':1024,
                       'height':800};

       var formatter = new google.visualization.BarFormat({width: 120});
       formatter.format(data, 1); // Apply formatter to second column

    // Instantiate and draw our chart, passing in some options.    
        var chart = new google.visualization.BarChart(document.getElementById('chart_div'));
        chart.draw(data, options);
     }
</script>
<title></title>
</head>
<body>
<!--Div that will hold the pie chart-->
<div id="chart_div"></div>
</body>
</html>

When you open this HTML page in a browser, you will see a chart similar to the following one.
The Weather in Reutte at Christmas
Obviously you could also automate the creation of the chart as well. The complete example shown in this post can be downloaded from my GitHub page.
 

 

 

Leave a Reply

Your e-mail address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.