Fixing problem with Yahoo geolocation service

Recently two Pebble watchfaces I’ve developed based on Paul Joel designes: Clean&Smart and Cobblestyle began to experience weather update issues – as in “weather was not updated at all”. Looking into the issue I found a weird thing: If you set your location manually – weather worked. But automatic weather based on phone location did not.

The way automatic location works is pretty straighforward. First this function is called to determine location:

function getLocation() {
  navigator.geolocation.getCurrentPosition(
    locationSuccess,
    locationError,
    {timeout: 15000, maximumAge: 60000}
  );
}

It uses phone location services (GPS, if its available, otherwise network-based location). In case of success it calls locationSuccess function. And in my tests it always called it, so location was determined successfully.

locationSuccess function is used to determine WOEID value based on location latitude and longitude, and then that WOEID was used to actually get the weather for the location:

function locationSuccess(pos) {
  var woeid;

  var query = 'select * from geo.placefinder where text="' +
      pos.coords.latitude + ',' + pos.coords.longitude + '" and gflags="R"';

  var url = 'https://query.yahooapis.com/v1/public/yql?q=' + 
      encodeURIComponent(query) + '&format=json';

  var xhr = new XMLHttpRequest();
  xhr.onload = function () {
     var json = JSON.parse(this.responseText);
     woeid = json.query.results.Result.woeid;
     getWeather(woeid);
  };
  xhr.open('GET', url);
  xhr.send();

}

Again, this is pretty straightforward. Lines 04-05 construct a YQL query to get WOEID based on coordinates, Lines 07-08 add the query to API URL, and Lines 10-17 create and and run XHR request and retrieve the result.

The problem is – that no longer worked. Response was coming back with “service unavailable” message. After digging for a while I found a substitution: Instead of “geo.placefinder” table we can use “geo.places” table. Here’s slightly modified version of the above function:

function locationSuccess(pos) {
  var woeid;

  var query = 'select woeid from geo.places where text="(' + 
       pos.coords.latitude + ',' + pos.coords.longitude + ')" limit 1';
  
  var url = 'https://query.yahooapis.com/v1/public/yql?q=' + 
       encodeURIComponent(query) + '&format=json';

  var xhr = new XMLHttpRequest();
  xhr.onload = function () {
    var json = JSON.parse(this.responseText);
    woeid = json.query.results.place.woeid;
    getWeather(woeid);
  };
  xhr.open('GET', url);
  xhr.send();

}

Note the differences: Query in Lines 04-05 now uses “geo.places” (also text with coordinates is enclosed in parentheses and LIMIT 1 added). Another difference – in Line 13 we changed format of result retrieval (since we’re querying a different table now).

And that was it. WOEID is correctly determined based on latitude and longitude provided by phone, and weather service can provide weather based on WOEID.

CREDITS: Issue discussion on jQuery.simpleWeather repo.


UPDATE (2016-01-29): The above approach may sometimes yield a more generic location than desired (county, neighboring town in close proximity). To get more precise location the above query needs to be modified slightly
Line 04 becomes var query = 'select locality1 from geo.places where text="(' + (note use of “locality1”), and respectfully Line 13 becomes woeid = json.query.results.place.locality1.woeid;.

One reply

  1. Cool, thanks! Now my “Gradient Weather” works again!

Leave a Reply

Your email address will not be published. Required fields are marked *