Getting Elevation Data from a Bing Map





5.00/5 (1 vote)
Using HTTP with Bing Maps to get Elevation Data
Introduction
This article details a C# class that can be used to get elevation data from a Bing map.
Background
While developing a mapping project, we were looking for a way to get elevation information for a specified list of location(s). After exploring a few alternatives, we decided to get this information from Bing. However, the Bing map library does not seem to have a way to get elevation information for a specific location and an HTTP query is necessary.
Class Description
The image below shows the diagram for the BingMapElevation
class:
- The class constructor requires you to specify a Bing map key. This link describes how to get a key.
- The two
public
methods used to get elevation(s) are namedgetElevation
. One is used to get a single elevation, the other for getting a list of elevations. - The actual work is done by two
protected
, asynchronous methods:FormatURI
returns a formattedstring
based on the location or locations.GetElevationsFromHTTP
uses the formattedstring
to get a list of elevations.
- The earth model used for the elevations may be specified using the
EARTH_MODEL
enumerations. An explanation of these models can be found here.
The class will work with both UWP and .NET projects (Note: For the .NET project, we had to explicitly add the System.Net.Http
assembly).
Code Description
In the class, the protected FormatURI
and GetElevationsFromHTTP
methods work in tandem to download the elevations. The FormatURI
method generates a string
with the specified locations, the key, and the Earth Model.
// Format the URI from a list of locations.
protected string FormatURI(List<location> locList)
{
// The base URI string. Fill in:
// {0}: The lat/lon list, comma separated.
// {1}: The heights: datum string, lower case.
// {2}: The key.
const string BASE_URI_STRING =
"http://dev.virtualearth.net/REST/v1/Elevation/List?points={0}&heights={1}&key={2}";
string retVal = string.Empty;
string locString = string.Empty;
for (int ndx = 0; ndx < locList.Count; ++ndx)
{
if (ndx != 0)
{
locString += ",";
}
locString += locList[ndx].latitude.ToString() + "," + locList[ndx].longitude.ToString();
}
retVal = string.Format(BASE_URI_STRING, locString, Model.ToString().ToLower(), Key);
return retVal;
}
The GetElevationsFromHTTP
method uses the string
generated by FormatURI
to make an HTTP call and download the elevation information.
/// Format the URI from a list of locations.
protected async Task<List<double>> GetElevationsFromHTTP(string url)
{
List<double> retVal = new List<double>();
try
{
HttpClient httpClient = new HttpClient();
HttpResponseMessage msg = await httpClient.GetAsync(url);
//Check the status code. Throw an exception on error.
if (!msg.IsSuccessStatusCode)
{
string errMsg = "HTTP Response Error: [" + msg + "]";
throw new Exception(errMsg);
}
Stream inStream = await msg.Content.ReadAsStreamAsync();
using (StreamReader reader = new StreamReader(inStream))
{
//Get the string from the HTTP stream, find the index of the
//substring where the altitudes are enumerated.
string readString = reader.ReadToEnd();
int ndx = readString.IndexOf(targetString);
//Check to see if the substring has been found.
if (ndx >= 0)
{
string elevationListStr =
readString.Substring(ndx + targetString.Length);
ndx = elevationListStr.IndexOf(']');
if (ndx > 0)
{
elevationListStr = elevationListStr.Substring(0, ndx);
//Split the comma delimited list into doubles.
char[] parm = { ',' };
string[] result = elevationListStr.Split(parm);
//Add the strings to the list.
foreach (string dbl in result)
{
retVal.Add(double.Parse(dbl));
}
}
else
{
string errMsg = "Format Error: [" + readString + "]";
throw new Exception(errMsg);
}
}
else
{
string errMsg = "No elevations found in the return string: [" +
readString + "]";
throw new Exception(errMsg);
}
}
}
catch (Exception e)
{
throw new Exception(e.Message, e);
}
return retVal;
}
The two getElevation
methods are called to get elevations at specific locations.
An example of BingMapElevation
usage is shown here:
//Test Method
static async Task Test()
{
string key = "Your Bing Map Key Here";
BingMapElevation mapElevation = new BingMapElevation(key);
double lat = 35.89431;
double lon = -110.72522;
List<location>locList = new List<location>();
//Load 10 lat/lon values
for (int ndx = 0; ndx < 10; ++ndx)
{
locList.Add(new Location() { latitude = lat, longitude = lon });
lat += 0.1;
lon -= 0.1;
}
List<double> dList = await mapElevation.getElevation(locList);
Debug.WriteLine("List:");
foreach (double d in dList)
{
Debug.WriteLine(d.ToString());
}
Debug.WriteLine("Single Location Test:");
foreach (Location loc in locList)
{
Debug.WriteLine(loc.latitude.ToString() + "," + loc.longitude.ToString());
double dbl = await mapElevation.getElevation(loc.latitude, loc.longitude);
Debug.WriteLine(dbl.ToString());
}
}
Possible Extensions
The Bing Map HTTP interface supports at least two other functions (explained here):
- Get elevations at equally-spaced locations along a polyline path.
- Get elevations at equally-spaced locations within an area on the Earth defined as a bounding box.
This class can easily be extended to accommodate these functions.
History
- July 5th, 2018: Initial version posted