Which Three Birdies?™©®

So, what’s this all about?

Essentially, Which Three Birdies?™©® is a chance to play with some nice APIs and stitch the results together. Xeno-Canto have recordings of birdsong from all around the world. Wikidata has pictures of all sorts of places and things. And 51Degrees can geolocate a point into a detailed native-language description of where that point is. Glue all those together and you’ve got... well, you’ve got a system for making a “birdday” card, right?

(The illusion of the single authorial "I" breaks for a moment here, where Stuart blames Bruce for the name and bangs his head frustratedly on the fourth wall. OK, no more asides to the audience. Back to the code.)

  1. Geolocating a point to an address with 51Degrees
  2. Finding an image for an address with Wikidata
  3. Finding birds local to a location with Xeno Canto
  4. Finding pictures of birds
  5. Gluing it all together

(geo l-)O Captain, my captain! — geolocating a point

Step 1 is to find a picture of a place. Well, we haven’t got a place; what we’ve got is some coordinates for a spot on the earth, latitude and longitude. So step zero is to find which place that is. Looking up a geographical point and giving it a name is the task of geolocation, and it is not as easy as you might think. Sometimes it’s simple enough: Big Ben is definitely in London, the Forbidden City is definitely in Beijing, and (as John Donne memorably wrote) Stormont is in Ireland. But which town is this field you’ve chosen closest to? What about the spot at the summit of Mount Kilimanjaro? If it’s not near a town, which region is it in? Which language should the answer be in? So we have someone else do the heavy lifting on that particular point.

Let’s try things for this place here: in the middle of the Green Mountain National Forest in the US state of Vermont. The birthplace (roughly) of Rachel Brooks Gleason, born this day in 1820, fourth woman to earn a medical degree in the United States, and anti-slavery activist: latitude 43.156702, longitude -72.914178.

A map showing a marker in Vermont, USA, in the midst of the Green Mountain National Forest, near to the town of Winhall

First, a little code. This is PHP, but obviously it could be in JS or Python or C# or Java instead.


$settings = array("resourceKey" => $resourceKey,
    "locationProvider" => "fiftyonedegrees");
$builder = new GeoLocationPipelineBuilder($settings);
$pipeline = $builder->build();
$flowData = $pipeline->createFlowData();
$flowData->evidence->set("query.51D_Pos_latitude", $lat);
$flowData->evidence->set("query.51D_Pos_longitude", $lon);

$result = $flowData->process();

That will populate our $flowData object with some or all of a town, a region, a state, and a country corresponding to the latitude ($lat) and longitude ($lon) we have. (You need an API key, what 51Degrees calls a “resource key”. See the 51Degrees API docs for details on the above functions.)

Now, not all of those things — town, region, state, country — are actually available for any given point. Some countries don’t really have “regions” or “states”; some points just aren’t very near an actual town at all. So we need to check whether each of those things are populated. The API does this in two ways; location->whatever might not be present at all, and if it is present it may have no value. So we check for each, and then we’ll have $town, $region, $state, and $country all either a string or null.


$town = null; $region = null; $state = null; $country = null;
try { $town = $flowData->location->town; } catch(Exception $e) {}
try { $region = $flowData->location->region; } catch(Exception $e) {}
try { $state = $flowData->location->state; } catch(Exception $e) {}
try { $country = $flowData->location->country; } catch(Exception $e) {}

if (!$town->hasValue) $town = null;
if (!$region->hasValue) $region = null;
if (!$state->hasValue) $state = null;
if (!$country->hasValue) $country = null;

In step 2 (spoilers!) we’ll be trying to look up this place name to get a picture for it. Even if we have all of town, region, state, and country, it’s possible that there is no picture available for that town; there are a lot of towns, after all. So we construct a series of locations, in decreasing order of specificity. If the place we have is “Anytown, Some Region, Thestate, Freedonia”, then we want to make a set of locations to look up as follows:

  1. Anytown, Some Region, Thestate, Freedonia (town, region, state, country)
  2. Anytown, Some Region, Freedonia (town, region, country)
  3. Anytown, Thestate, Freedonia (town, state, country)
  4. Some Region, Thestate, Freedonia (region, state, country)
  5. Anytown, Freedonia (town, country)
  6. Some Region, Freedonia (region, country)
  7. Thestate, Freedonia (state, country)
  8. Freedonia (country)

(We do it in this order specifically because we’re looking for pictures of this place. A picture of the town you’re in is best, but a picture of the region is still relevant, and a picture of the country as a whole isn’t too bad. This is why "town + country" is lower down the list than you might expect; there are an awful lot of countries which have two towns with the same name, and picking the wrong one isn’t ideal here.)

Our chosen location returns the following:

Town
Winhall
Region
null
State
Vermont
Country
United States of America

and our list of geolocations to search for is therefore

  1. Winhall, Vermont, United States of America
  2. Winhall, United States of America
  3. Vermont, United States of America
  4. United States of America

And with those in place, it’s on to step 2.

A thousand words — picturing an address

Wikidata is to structured data what Wikipedia is to human-readable knowledge: it’s got everything. In particular, you can search for pretty much anything you can think of and then find all the knowledge that Wikidata has about that thing, carefully characterised by type. For our purposes, you can search for a place, and then look for a record of type “P18” attached to it, which in Wikidata-speak means “a picture of this thing” (well, “image of relevant illustration of the subject”). This means that we can search for our address from step 1, look at the result we get back, and if it contains a P18 record then we have a picture of that address. This is why we needed to look up the latitude and longitude to get a human-readable address — it gives us something to search for.

First, search Wikidata for the address in question by constructing a search URL that returns JSON, which looks like:

https://www.wikidata.org/w/api.php? action=query &list=search &srsearch=Winhall%2C+Vermont%2C+United+States+of+America &format=json.

That does indeed return us a result! The relevant part is this:

    {
        "title":"Q8025343",
        "pageid":7971580,
        "timestamp":"2020-01-11T18:30:56Z"
    }

and the key part there is the title, "Q8025343", which is an “entity-id”, Wikidata-speak for a unique ID for a thing. This, usefully, lets us directly construct a URL to get all the data about that thing:

https://www.wikidata.org/wiki/ Special:EntityData/Q8025343.json

and that has all the info we need. In particular, there’s a claims.P18 record:

    "P18": [
        {
            "mainsnak": {
                "snaktype": "value",
                "property": "P18",
                "datavalue": {
                    "value": "Winhall River, West River Trail.jpg",
                    "type":"string"
                },
                "datatype": "commonsMedia"
            },
            "type": "statement",
            "id": "Q8025343$727DE3D5-AF2E-486F-9FAD-EBDEF3606970",
            "rank":"normal"
        }
    ]

and the value there can be used to construct a URL for an image on Wikimedia with a specific width:

http://commons.wikimedia.org/wiki/ Special:FilePath/ Winhall%20River%2C%20West%20River%20Trail.jpg ?width=1000

The Winhall River in Vermont, in winter, looking pretty chilly if I’m honest

Four and twenty blackbirds baked in an API — finding birds for a location

Xeno-Canto bills itself as “Sharing bird sounds from around the world”, and it’s a really comprehensive crowd-sourced database of birdsong and bird calls from everywhere on Earth, searchable by latitude and longitude. So we can use it to find details of birds and their calls from our chosen point! They have an excellent API which lets us do this programmatically, so let’s dive in.

https://www.xeno-canto.org/ api/2/recordings?query= lat:43.156702 lon:-72.914178

This returns comprehensive JSON listing recordings from the area: the first of which is of the Canada Goose, branta canadensis. The Latin will be relevant in a moment. This first record has relevant parts that look like this:

{
    "id":"72752",
    "gen":"Branta",
    "sp":"canadensis",
    "en":"Canada Goose",
    "rec":"Ezekiel S. Jakub",
    "loc":"Turners Falls Canal, Massachusetts",
    "lat":"42.593",
    "lng":"-72.579",
    "type":"Call",
    "url":"\/\/www.xeno-canto.org\/72752",
    "file":"\/\/www.xeno-canto.org\/72752\/download",
}

so thank you Ezekiel S. Jakub for recording this Canada goose in Turners Falls. Here we have a link to the sound recording directly, and also Xeno Canto’s page about this recording of the Canada goose which present all the details in human-readable form.

See bird fly — finding pictures of birds

We can of course use the same Wikidata trick we used for the town to find pictures of the birds as well. This is what the Latin classification is useful for: “Canada goose” might match other things, but branta canadensis will fairly unambiguously lead us to Wikidata’s page on the Canada Goose, which has pictures.

The thing and the whole of the thing — gluing it all together

What remains is to pull all this imagery and sound together into the classic of modern web design that you see before you to make a "birdday card". I’m not the person to advise on how to do that — you can see from the design that I am no designer. You’ll find PHP’s imagecopy() function useful, though. The results are also cached; images once created are saved, and the results from API calls are also cached. This is important when you’re using third-party APIs; you want to minimise the number of times you call them when you can, firstly because retrieving results you’ve already got is faster, and secondly because (especially with freely-provided data APIs such as xeno-canto) an increase in popularity on your side should not result in a crushing increase in load for them. So: cache your API calls!

That’s the birdday card, and how to pull together disparate API data into one thing on the web. Thank you to xeno-canto, wikimedia, and 51Degrees for providing the data required to do this, and to Rachel Brooks Gleason for setting up a sanitarium which catered to upper-class women with what Wikipedia inexplicably refers to as “lady troubles”, the quotes being theirs. And for fighting against slavery, obvs. All of the trademark symbols are lies, too, as if you couldn't guess.

Rachel Brooks Gleason’s birdday card, showing the Winhall river in Vermont and the Canada goose, the mute swan, and the wood duck.
happy birdday Rachel from Stuart and Bruce! xxx