Thursday, November 28, 2019

A convenient API for retrieving accurate elevation values

The elevation values for the sites in this atlas were recently updated from Google Earth values to values from the Digital Elevation Model (DEM) called ASTER_DGEMV3 which samples 1 arc second squares (30 m).

I've been wanting to do this for a long time but wasn't able to understand all the gyrations involved.  Happily I stumbled across the site called elevationapi.com which was created by Xavier Fischer.

This site is an interface to any one of several DEMs including my ASTER_DGEMV3.  It eliminates an enormous amount of work for those of us who aren't quite up to speed on Digital Elevation Models or map projections but really need a source of accurate elevation data.

I began by using this interface in a very simple and naive way.  This page  provides a simple web call that returns the elevation of the Eiffel Tower in Paris (you should experiment with this.  Copy and paste it into your browser URL window and hit 'return'.):

https://api.elevationapi.com/api/Elevation?lat=48.854624&lon=2.3436793&dataSet=SRTM_GL3 

or

https://api.elevationapi.com/api/Elevation?
lat=48.854624
&lon=2.3436793
&dataSet=SRTM_GL3

As you see it requires three arguments, lat, lon, and DEM name.  For DEM name I used: ASTER_GDEMV3 instead of SRTM_GL3/

https://api.elevationapi.com/api/Elevation?lat=48.854624&lon=2.3436793&dataSet=ASTER_GDEMV3

This call returns a JSON string with the elevation embedded (it's in the first line):

{"message":"OK","geoPoints"[{"latitude":48.854624,"longitude":2.3436793,"elevation":28.353599548339844}],"resultCount":1,"dataSet":{"name":"ASTER_GDEMV3","description":"ASTER Global Digital Elevation Model 1 arc second (30m)","publicUrl":"https://lpdaac.usgs.gov/products/astgtmv003",
"resolutionMeters":30,"noDataValue":-9999,"fileFormat":{"name":"GeoTiff file","fileExtension":".tif","type":1,"registration":0},"attribution":
{"text":"NASA/METI/AIST/Japan Spacesystems, and U.S./Japan ASTER Science Team. ASTER Global Digital Elevation Model V003. 2018,
distributed by NASA EOSDIS Land Processes DAAC, https://doi.org/10.5067/ASTER/ASTGTM.003"},"dataSource":{"indexFilePath":"ASTGTM.003.json",
"collectionId":"C1575726572-LPDAAC_ECS","isGlobalFile":false,"dataSourceType":3},"pointsPerDegree":3600}}

So ASTER_GDEMV3 returns an elevation of 28.35 m. where Google Earth returns either 33 or 34 m. depending on exactly where you put the cursor.

How do we handle the JSON?  All you really want is the elevation value.  But since we've been given a URL we're home free.  We can capture the return string using cURL and then use json_decode on the string.  I quickly wrote a program in PHP using cURL that incorporated the URL from elevationapi.com    Here's the nucleus of it:

~~~~~~~~~~~~~
$URL = "https://api.elevationapi.com/api/Elevation?lat=$lat&lon=$lon&dataSet=ASTER_GDEMV3";       

// This ^ is the URL I want to send.  You will have had to have already filled in $lat and $lon with the desired values.

$ch1= curl_init();                                        // All of this is just  cURL stuff.  I changed nothing.
curl_setopt ($ch1, CURLOPT_URL, $URL );
curl_setopt($ch1, CURLOPT_HEADER, 0);
curl_setopt($ch1,CURLOPT_VERBOSE,0);
curl_setopt($ch1, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)');
curl_setopt ($ch1, CURLOPT_REFERER,'http://www.google.com');  //just a fake referer
curl_setopt($ch1, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch1,CURLOPT_POST,0);
curl_setopt($ch1, CURLOPT_FOLLOWLOCATION, 20);

$htmlContent= curl_exec($ch1);        // this puts the entire returned string into a variable named $htmlContent

$ar = json_decode($htmlContent);                           // JSON decode

$newElev = $ar->geoPoints[0]->elevation;   // Fetch the elevation field

$newElev = round($newElev);                // Round the value (if you like)

~~~~~~~~~~~~~

That's all you need to do.  You can put this section in a loop which retrieves the lat/lon pairs from each DB.  You can follow it up with an insert back into the DB of the new elevation value.
I simply generate a sql statement and write it to a .sql file for execution later.  That code looks like this:

~~~~~~~~~~~~~

$diff = $newElev - $oelev;     // I subtracted the old elevation from the newly returned one.  This is the 'error'.

if ($diff != 0)
{
echo "update site set ALT = $newElev where pk = '".$pk."' limit 1;";          // this generates a SQL update statement that I pipe to an external .sql file.    $pk ('place key') identifies the specific site
}

~~~~~~~~~~~~~

In this section $oelev is the old elevation value from the DB which I'm now going to replace with $newElev.

It looked to me as though the average error was on the order of 10 m.  And by looking at my own old data for elevation I discovered a few gross errors caused by me and not Google Earth.  Those are now corrected.

It always helps to review your data.

I'm very grateful to Mr. Fischer for building this site and then promptly and helpfully following up my questions on Twitter (@ElevationApi).  There is much more on this site including the ability to create 3D models of various terrains.

I know that I'm going to learn a great deal by scrutinizing this site carefully along with Mr. Fischer's helpful links.