Runkeeper - Inject Weather

On a runkeeper activity page, downloads GPX file to get LAT + LON, then uses weather underground to do geolookup and weather history. To live within constraints of dev accounts saves data to cookie to reuse later. Requires an API KEY from weather underground which you can get info on and for free from http://www.wunderground.com/weather/api

As of 24/05/2014. See the latest version.

// ==UserScript==
// @name        Runkeeper - Inject Weather
// @namespace   com.krozy.gm.runkeeper.weather
// @description On a runkeeper activity page, downloads GPX file to get LAT + LON, then uses weather underground to do geolookup and weather history. To live within constraints of dev accounts saves data to cookie to reuse later.  Requires an API KEY from weather underground which you can get info on and for free from http://www.wunderground.com/weather/api
// @include     http://runkeeper.com/user/*/activity/*
// @version     2
// @grant       none
// ==/UserScript==

// IMPORTANT!!!! -- THIS VARIABLE MUST BE CHANGED TO YOUR API KEY VALUE TO SUCCESSFULLY USE THIS SCRIPT!
// Get your weather underground API Key at http://www.wunderground.com/weather/api
// You want the Developer Plan, WITH the History Add-On. The price is $0. You can do up to 10 calls per minute so don't review more then 5 of your activities in the span of a minute.
var WEATHER_UNDERGROUND_API_KEY = '1234567890abcdef';


function parseGPX4Weather(GPX4document)
{
    // With GPX file retrieved, lets get the first data point
    $trkpt = $(GPX4document).find("trkseg").find("trkpt").filter(":first");
    // With that we can get the location...
    $lat = $trkpt.attr('lat');             // decimal degrees
    $lon = $trkpt.attr('lon');             // decimal degrees
    // And the date and time...
    $ptime = $trkpt.find("time").text();   // ISO format 2014-05-21T18:19:59Z << note that runkeeper is treating local as UTC
    
    // Format the weather date and time
    var weatherdate = $ptime.split('T')[0];
    var weatherhour = $ptime.split('T')[1].split(':')[0]; 
    weatherdate = weatherdate.split('-')[0] + weatherdate.split('-')[1] + weatherdate.split('-')[2];

    // Using the location, lookup nearest location from weather underground
    var wuggeo = 'http://api.wunderground.com/api/' + WEATHER_UNDERGROUND_API_KEY + '/geolookup/q/' + $lat + ',' + $lon + '.json';
    var wughist = 'http://api.wunderground.com/api/' + WEATHER_UNDERGROUND_API_KEY + '/history_' + weatherdate + '/q/'
    $.ajax({url : wuggeo, dataType : "jsonp", success : function(parsed_geo) {
       // With the response, find the request url which is going to be used to build the history request
       var requesturl = parsed_geo['location']['requesturl'];
       var prefix = "US/";
       var suffix = ".html";
       var rpart = requesturl.substring(prefix.length,requesturl.length - suffix.length);
       
       // Finish out the url for the history and retrieve it...
       wughist += rpart + '.json';      
       $.ajax({url: wughist, dataType : "jsonp", success : function(parsed_hist){
          // Look at the history observations
          var observations = parsed_hist['history']['observations'];
          var found = false;
          for (idx in observations)
          {
             if(!found)
             {
                var observation = observations[idx];
                 
                if(observation['date']['hour'] == weatherhour)
                {
                    found = true;
                    saveWeather(activityid, observation);
                    displayWeather(observation, false);                    
                }
             }
          }
       }});
       
    }});

}
function getCookieName(activityid)
{
    return "aidw" + activityid;
}
function saveWeather(activityid, observation)
{
    var smallobservation = {
       "tempi":observation['tempi'],
       "hum":observation['hum'],
       "wspdi":observation['wspdi'],
       "wdire":observation['wdire'],
       "conds":observation['conds']
    };
    
    var cookiename = getCookieName(activityid);
    var cookievalue = JSON.stringify(smallobservation);
    var cookieexp = "Wed, 31 Dec 2014 12:00:00 GMT";
    var cookiestring = cookiename + "=" + cookievalue + "; expires=" + cookieexp + "; path=/";
    document.cookie=cookiestring;
}
function loadWeather(activityid)
{
    var cookiename = getCookieName(activityid);
    var cookievalue = getCookie(cookiename);
    if(cookievalue == "") return null;
    var observation = JSON.parse(cookievalue);
    return observation;
}
function setCookie(cname,cvalue,exdays)
{
    var d = new Date();
    d.setTime(d.getTime()+(exdays*24*60*60*1000));
    var expires = "expires="+d.toGMTString();
    document.cookie = cname + "=" + cvalue + "; " + expires + "; path=/";
} 
function getCookie(cname)
{
    var name = cname + "=";
    var ca = document.cookie.split(';');
    for(var i=0; i<ca.length; i++)
    {
       var c = ca[i].trim();
       if (c.indexOf(name)==0) return c.substring(name.length,c.length);
    }
    return "";
} 
function displayWeather(observation, isFromCookie)
{
    var temp = observation['tempi'];
    var humidity = observation['hum'];
    var windspeed = observation['wspdi'];
    var winddir = observation['wdire'];
    var conditions = observation['conds'];
    
    var weatherelement = "<div class=\"mainColumnPadding clearfix\">";
    weatherelement += "<a href=\"http://www.wunderground.com/?apiref=1ef1f4a0ad522a43\">";
    weatherelement += "<img src=\"http://www.wunderground.com/logos/images/wundergroundLogo_4c.jpg\" border=\"0\" />";
    weatherelement += "</a>";
    //weatherelement += "<h4>Weather</h4>";
    weatherelement += "<p>Temperature: " + temp + "&deg; Fahrenheit</p>";
    weatherelement += "<p>Humidity: " + humidity + " %</p>";
    weatherelement += "<p>Conditions: " + conditions + "</p>";
    weatherelement += "<p>Wind: " + winddir + " " + windspeed + "</p>";
    if(isFromCookie)
    {
        weatherelement += "<p align=\"center\">(loaded from cookie)</p>";
    }
    weatherelement += "</div>";
    
    // This is where we want to insert the data!!!
    $("#splitsBox").prepend(weatherelement);
}

// Look at url to get activity number
var activityid = location.pathname.split('/')[4];
// Load it from cookie if previously captured
var observation = loadWeather(activityid);
// If we have an observation from the cookie...
if(observation != null)
{
    // Show it! Saves 3 GET calls!
    displayWeather(observation, true);
}
else
{
    // We dont have it yet, fetch the GPX, process and hit up weather underground.

    // can only do this if we have export options (which is our own page)
    if ($('#exportOptions').length) 
    {  
        // Now form the link to the GPX file that has LAT + LON
        var gpxfile = 'http://runkeeper.com/download/activity?activityId=' + activityid + '&downloadType=gpx';

        // Use the existing jQuery framework pulled into the page to retrieve the file
        $.ajax({
           url: gpxfile, 
           dataType: "xml", 
           success: parseGPX4Weather, 
           error: function(){alert("Error: Unable to download GPX file to lookup location and time information!");}
        });    
    }
}