Webová aplikace Předpověď počasí byla znovu navštívena

Před rokem jsem publikoval tutoriál o vytvoření webové aplikace pro předpověď počasí, která využívá rozhraní API Yahoo a vestavěné funkce geolokace v prohlížeči. Nedávno však společnost Yahoo přestala používat tato bezplatná rozhraní API, takže dnes převedeme webovou aplikaci na jinou službu – OpenWeatherMap.

Rozhraní OpenWeatherMap API

OpenWeatherMap je nejen zdarma k použití, ale také vrací předpověď na dalších pět dní a dělá s jediným požadavkem API to, co Yahoo dokázalo jen se dvěma. Rozhraní API přímo bere sadu geografických souřadnic a vrací data o počasí (není nutné nejprve najít město). Díky těmto funkcím je používání bezbolestné a jen mě mrzí, že jsem o této službě nevěděl dříve. Zde je příklad odpovědi:

{
    "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..

    ]
}

Jediné volání API vrátí geografické informace, název města, kód země a podrobnou předpověď počasí. Předpovědi počasí jsou vráceny v list vlastnost jako pole a jsou od sebe vzdáleny tři hodiny. V našem kódu budeme muset procházet tento seznam a prezentovat předpověď jako sérii snímků. Dobrou zprávou je, že spoustu práce, kterou jsme udělali v předchozím tutoriálu, lze znovu použít.

Co je třeba změnit

Nebudeme začínat od nuly – znovu použijeme HTML a design z posledního tutoriálu. V části HTML je vše téměř stejné jako v originále, s výjimkou toho, že jsem upgradoval na nejnovější verzi jQuery a importoval jsem knihovnu data/času moment.js (rychlý tip), kterou použijeme k prezentaci čas předpovědí.

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>

Co je však potřeba přepsat, je náš JavaScript kód. OpenWeatherMap je jednodušší na použití a bere souřadnice přímo z geolokačního API, takže jsem odstranil spoustu starého kódu. Další výhodou je, že není potřeba registrovat API klíč na OpenWeatherMap, což znamená, že můžeme přejít přímo ke zdroji:

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);
    }

});

Většina změn se týká locationSuccess() funkce, kde uděláme požadavek na API a zavoláme addWeather() . Ten také potřeboval nějaké změny, takže používá kód ikony obsažený v datech počasí k zobrazení správného obrázku ze složky aktiv/img/ikony. Podívejte se na seznam ikon (denní a noční verze) a kódů počasí v dokumentech OpenWeatherMap.

Další věc, která stojí za zmínku, je způsob, jakým používám perzistentní localStorage objekt uložit do mezipaměti výsledek z API po dobu 30 minut, což omezuje počet požadavků, které jdou do OpenWeatherMap, takže každý může získat svůj spravedlivý podíl.

Tímto je naše webová aplikace Počasí připravena! Máte nějaké otázky? Klikněte na sekci komentářů níže.