Weather Forecast Webapp Revisited

Demo Download

One year ago, I published a tutorial about building a weather forecasting web app, powered by Yahoo's APIs and the browser's built in geolocation capabilities. However, recently Yahoo discontinued these free APIs, so today we are going to convert the web app to a different service - OpenWeatherMap.

The OpenWeatherMap API

OpenWeatherMap is not only free to use, but it also returns the forecast for the next five days and does with a single API request what Yahoo could only do with two. The API directly takes a set of geographic coordinates and returns weather data (no need to first find the city). These features make it pain-free to use and I am only sorry that I didn't know about this service earlier. Here is an example response:

{
    "cod": "200",
    "message": 0.0074,
    "city": {
        "id": 726048,
        "name": "Varna",
        "coord": {
            "lon": 27.91667,
            "lat": 43.216671
        },
        "country": "BG",
        "population": 0
    },
    "cnt": 41,
    "list": [{
            "dt": 1369224000,
            "main": {
                "temp": 295.15,
                "temp_min": 293.713,
                "temp_max": 295.15,
                "pressure": 1017.5,
                "sea_level": 1023.54,
                "grnd_level": 1017.5,
                "humidity": 94,
                "temp_kf": 1.44
            },
            "weather": [{
                    "id": 800,
                    "main": "Clear",
                    "description": "sky is clear",
                    "icon": "02d"
                }
            ],
            "clouds": {
                "all": 8
            },
            "wind": {
                "speed": 5.11,
                "deg": 155.502
            },
            "sys": {
                "pod": "d"
            },
            "dt_txt": "2013-05-22 12:00:00"
        }

        // 40 more items here..

    ]
}

A single API call returns geographic information, city name, country code and detailed weather forecast. The weather predictions are returned in the list property as an array and are spaced three hours apart. In our code, we will have to loop through this list, and present the forecast as a series of slides. The good news is that a lot of the work we did in the previous tutorial can be reused.

jquery-geolocation-weather-app.jpg
Weather Forecast Web App

What needs to be changed

We won't be starting from scratch - we will be reusing the HTML and the design from the last tutorial. On the HTML part everything is nearly the same as in the original, with the exception that I've upgraded to the latest jQuery version, and have imported the moment.js (quick tip) date/time library that we will use to present the time of the forecasts.

index.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>Weather Forecast App Revisited | Tutorialzine Demo</title>

        <!-- The stylesheet -->
        <link rel="stylesheet" href="assets/css/styles.css" />

        <!-- Google Fonts -->
        <link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Playball|Open+Sans+Condensed:300,700" />

        <!--[if lt IE 9]>
          <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
    </head>

    <body>

        <header>
            <h1>Weather Forecast</h1>
        </header>

        <div id="weather">

            <ul id="scroller">
                <!-- The forecast items will go here -->
            </ul>

            <a href="#" class="arrow previous">Previous</a>
            <a href="#" class="arrow next">Next</a>

        </div>

        <p class="location"></p>

        <div id="clouds"></div>

        <!-- JavaScript includes - jQuery, moment.js and our own script.js -->
        <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
        <script src="//cdnjs.cloudflare.com/ajax/libs/moment.js/2.0.0/moment.min.js"></script>
        <script src="assets/js/script.js"></script>

    </body>
</html>

What needs to be rewritten though, is our JavaScript code. OpenWeatherMap is simpler to use and takes the coordinates from the geolocation api directly, so I have removed a lot of the old code. Another benefit is that there is no need to register for an API key on OpenWeatherMap, which means we can jump directly to the source:

assets/js/script.js

$(function(){

    /* Configuration */

    var DEG = 'c';  // c for celsius, f for fahrenheit

    var weatherDiv = $('#weather'),
        scroller = $('#scroller'),
        location = $('p.location');

    // Does this browser support geolocation?
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(locationSuccess, locationError);
    }
    else{
        showError("Your browser does not support Geolocation!");
    }

    // Get user's location, and use OpenWeatherMap
    // to get the location name and weather forecast

    function locationSuccess(position) {

        try{

            // Retrive the cache
            var cache = localStorage.weatherCache && JSON.parse(localStorage.weatherCache);

            var d = new Date();

            // If the cache is newer than 30 minutes, use the cache
            if(cache && cache.timestamp && cache.timestamp > d.getTime() - 30*60*1000){

                // Get the offset from UTC (turn the offset minutes into ms)
                var offset = d.getTimezoneOffset()*60*1000;
                var city = cache.data.city.name;
                var country = cache.data.city.country;

                $.each(cache.data.list, function(){
                    // "this" holds a forecast object

                    // Get the local time of this forecast (the api returns it in utc)
                    var localTime = new Date(this.dt*1000 - offset);

                    addWeather(
                        this.weather[0].icon,
                        moment(localTime).calendar(),   // We are using the moment.js library to format the date
                        this.weather[0].main + ' <b>' + convertTemperature(this.main.temp_min) + '°' + DEG +
                                                ' / ' + convertTemperature(this.main.temp_max) + '°' + DEG+'</b>'
                    );

                });

                // Add the location to the page
                location.html(city+', <b>'+country+'</b>');

                weatherDiv.addClass('loaded');

                // Set the slider to the first slide
                showSlide(0);

            }

            else{

                // If the cache is old or nonexistent, issue a new AJAX request

                var weatherAPI = 'http://api.openweathermap.org/data/2.5/forecast?lat='+position.coords.latitude+
                                    '&lon='+position.coords.longitude+'&callback=?'

                $.getJSON(weatherAPI, function(response){

                    // Store the cache
                    localStorage.weatherCache = JSON.stringify({
                        timestamp:(new Date()).getTime(),   // getTime() returns milliseconds
                        data: response
                    });

                    // Call the function again
                    locationSuccess(position);
                });
            }

        }
        catch(e){
            showError("We can't find information about your city!");
            window.console && console.error(e);
        }
    }

    function addWeather(icon, day, condition){

        var markup = '<li>'+
            '<img src="assets/img/icons/'+ icon +'.png" />'+
            ' <p class="day">'+ day +'</p> <p class="cond">'+ condition +
            '</p></li>';

        scroller.append(markup);
    }

    /* Handling the previous / next arrows */

    var currentSlide = 0;
    weatherDiv.find('a.previous').click(function(e){
        e.preventDefault();
        showSlide(currentSlide-1);
    });

    weatherDiv.find('a.next').click(function(e){
        e.preventDefault();
        showSlide(currentSlide+1);
    });

    // listen for arrow keys

    $(document).keydown(function(e){
        switch(e.keyCode){
            case 37: 
                weatherDiv.find('a.previous').click();
            break;
            case 39:
                weatherDiv.find('a.next').click();
            break;
        }
    });

    function showSlide(i){
        var items = scroller.find('li');

        if (i >= items.length || i < 0 || scroller.is(':animated')){
            return false;
        }

        weatherDiv.removeClass('first last');

        if(i == 0){
            weatherDiv.addClass('first');
        }
        else if (i == items.length-1){
            weatherDiv.addClass('last');
        }

        scroller.animate({left:(-i*100)+'%'}, function(){
            currentSlide = i;
        });
    }

    /* Error handling functions */

    function locationError(error){
        switch(error.code) {
            case error.TIMEOUT:
                showError("A timeout occured! Please try again!");
                break;
            case error.POSITION_UNAVAILABLE:
                showError('We can\'t detect your location. Sorry!');
                break;
            case error.PERMISSION_DENIED:
                showError('Please allow geolocation access for this to work.');
                break;
            case error.UNKNOWN_ERROR:
                showError('An unknown error occured!');
                break;
        }

    }

    function convertTemperature(kelvin){
        // Convert the temperature to either Celsius or Fahrenheit:
        return Math.round(DEG == 'c' ? (kelvin - 273.15) : (kelvin*9/5 - 459.67));
    }

    function showError(msg){
        weatherDiv.addClass('error').html(msg);
    }

});

The majority of the changes are to the locationSuccess() function, where we make a request to the API and call addWeather(). The latter also needed some changes, so that it uses the icon code contained in the weather data to present the correct image from the assets/img/icons folder. See the list of the icons (day and night versions) and weather codes in the OpenWeatherMap docs.

Another thing worth noting is the way I am using the persistent localStorage object to cache the result from the API for 30 minutes which limits the number of requests that go to OpenWeatherMap, so that everyone can get their fair share.

With this our Weather web app is ready! Have any questions? Hit the comment section below.

Bootstrap Studio

The revolutionary web design tool for creating responsive websites and apps.

Learn more

Related Articles

Nice! Something new.

I'm in Idaho and it detects as Nebraska. An input field for manual correction of geolocation would be nice to have added. Very nice work though.

Utilisatrice

Great, really !

As usual, this time also i loved the UI. I wish i could creat UI's like you :P :P

Thanks!

William Rouse

The date seems to start on Wednesday the 22nd even though it is Thursday the 23rd. Is there away to correct this?

William Rouse

Never mind, a moment of insight resolved the issue.

Can you tell me what you did to correct this, please?

Taylor Hunt

I was just trying to fina a workaround for the yahoo issue the other day! Thank you for this!

Adam Huddleston

This is brilliant. I have been looking for a Open Source Weather API for ages. Martin you've come through with the goods again! :) Thanks!

Excellent but when i write this the text don't appears in French

var weatherAPI = 'http://api.openweathermap.org/data/2.5/forecast?&lang=fr&lat='+position.coords.latitude+'&lon='+position.coords.longitude+'&callback=?'

can you help me please

Martin Angelov

I am afraid I haven't tried it with different languages. If the API supports it, it should be explained in the OpenWeatherMap docs.

Hey Martin,

Thanks for such a nice script. Loved it ....
However can you add couple of features

  1. Allowing user to select between Celsius / Fahrenheit

  2. Allowing user to search / change location.

  3. If geolocation is not allowed then a searchbox for location.

  4. Option to change view to Daily data for next 5/10 (extended) days in same page

Just my two cents ...

:0

Thanks

Sid

Opening the page by clicking on the html doc produces nothing but a spinning wheel. The page never fully loads

Martin Angelov

You will have to open it through a locally running web server like XAMPP/MAMP etc (and access it through http://localhost), as browsers impose security restrictions on directly-opened local files.

Keeps loading forever

Used it for http://clouddesk.koding.com

Martin, excellent script.

I'm trying to migrate accross from the old Yahoo one which was running fine.

I only want the current weather icon and temperature to appear, not the slider.

Any help on this?

Jermaine

Could you tell me how or where to input direct location instead of having it auto generate?

Michael Brooks

Hi, I've taken a look at this and think it's fantastic. I would like to know though, how would I go about hard coding the coordinates? I am trying to use this for a Holiday website, so it would be good if they knew the weather forecast for that particular part of the country.

Stefan Wilhelm

Great Tutorial, but looks like OpenWeatherMap.org now requires an API Key.

Is there a way to do this but instead of using Geolocation, input a Latitude and Longitude? I'm trying to establish several weather check ins for several remote locations.