Posts tagged ‘AJAX’

June 15, 2011

Creating a Bing Maps v7 control Centred on an Address Rather than a known Latitude/Longitude

This was a question that came up at the cloudhack event last weekend – when you create a new instance of the Bing Maps AJAX control, you specify the centrepoint of the map in latitude and longitude coordinates, using the center property in the viewOptions passed to the constructor. For example:

var map = new Microsoft.Maps.Map( document.getElementById("mapDiv"),
                           { credentials: ENTERYOURBINGMAPSKEY,
                             center: new Microsoft.Maps.Location(54, -4),
                             mapTypeId: Microsoft.Maps.MapTypeId.aerial,
                             zoom: 12
                           });

(You can view more information on the viewOptions parameters here:  http://msdn.microsoft.com/en-us/library/gg427628.aspx )

However, what if you want to create a map centred on an address instead of a latitude/longitude coordinate? This seems like a fairly simple, common request, so I was a bit surprised to find that none of the Microsoft method reference guides nor any of the “interactive SDK” sites provide an example showing how to do this.

Fortunately, it’s not too tricky to do so, for the benefit of everyone other than those teams at the cloudhack event who asked me, I thought I’d write it up here ;)

First, you need to geocode the address into latitude and longitude coordinates (using the REST Locations API is the easiest way to do so direct from javascript). Then, in the jsonp callback function that is executed after the geocode service returns its results, create and centre the map on the returned coordinates. Here’s an example (note that you will need to replace ENTERYOURBINGMAPSKEY here with some valid credentials, or else the call to the REST service will fail and the map will not be created):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <title></title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
  <script type="text/javascript">
    var map = null;
    var credentials = "ENTERYOURBINGMAPSKEY";
    
    function GetMap() {
   
      // Define the address on which to centre the map
      var addressLine = "54 Chiswell Street"; // Street Address
      var locality = "London"; // City or town name
      var adminDistrict = ""; // County
      var country = "UK"; // Country, obviously
      var postalCode = "" //Postcode
       
      // Construct a request to the REST geocode service
      var geocodeRequest = "http://dev.virtualearth.net/REST/v1/Locations" 
                           + "?countryRegion=" + country
                           + "&addressLine=" + addressLine
                           + "&locality=" + locality
                           + "&adminDistrict=" + adminDistrict
                           + "&postalCode=" + postalCode
                           + "&key=" + credentials
                           + "&jsonp=GeocodeCallback"; // This function will be called after the geocode service returns its results
       
      // Call the service
      CallRestService(geocodeRequest);
    }

    function GeocodeCallback(result) {
      // Check that we have a valid response
      if (result && result.resourceSets && result.resourceSets.length > 0 && result.resourceSets[0].resources && result.resourceSets[0].resources.length > 0) {
       
        // Create a Location based on the geocoded coordinates
        var coords = result.resourceSets[0].resources[0].point.coordinates;
        centerPoint = new Microsoft.Maps.Location(coords[0], coords[1]);

        // Create a map centred on the location
        map = new Microsoft.Maps.Map(document.getElementById("mapDiv"),
                           { credentials: credentials,
                             center: centerPoint,
                             mapTypeId: Microsoft.Maps.MapTypeId.aerial,
                             zoom: 18
                           });
         
        // Add a pushpin as well
        var pushpin = new Microsoft.Maps.Pushpin(map.getCenter());
        map.entities.push(pushpin);
      }
    }

    // This is a generic function to call a REST service and insert the JSON
    // results into the head of the document
    function CallRestService(request) {
      var script = document.createElement("script");
      script.setAttribute("type", "text/javascript");
      script.setAttribute("src", request);
      var dochead = document.getElementsByTagName("head").item(0);
      dochead.appendChild(script);
    }      
  </script>
</head>
<body onload="GetMap();">
  <div id='mapDiv' style="position: relative; width: 400px; height: 600px;">
  </div>
</body>
</html>

Here’s the resulting map:

image

Tags: , ,
April 1, 2011

Displaying Open Street Map and ESRI tiles on Bing Maps AJAX v7

In a previous post, I explained how to replace the base tile layer in the Bing Maps Silverlight control with an ESRI tile layer. In this post, I’ll show how to do the same but using the Bing Maps AJAX v7 control. You can use this technique to use the Bing Maps AJAX control, but replace the Bing imagery with OSM tiles or the ESRI tile layers used in the previous Silverlight post, as well as many other tile sources.

The first step is to specify not to load the default Bing Maps tile layer. Do this by specifying the mercator MapTypeId in the options passed to the constructor when you first initialise the map:

mapTypeId: Microsoft.Maps.MapTypeId.mercator

The next step is to create a new TileSource. The uriConstructor of the TileSource must return the correct URI for a requested tile. If your tile provider names its tiles according to the default Bing Maps quadkey numbering system, then the uriConstructor can be a simple string using the {quadkey} placeholder. This will be replaced with the appropriate quadkey when the tile is requested:

var tileSource = new Microsoft.Maps.TileSource(
 {  uriConstructor: 'http://www.microsoft.com/maps/isdk/ajax/layers/lidar/{quadkey}.png' }
);

However, for OSM tiles, or any other tile providers that not follow the basic quadkey numbering system, we instead need a function to construct the appropriate URI for each tile. I’ll do this in a function called getTilePath, and I’ll specify this in the TileSource uriConstructor as follows:

var tileSource = new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });

The getTilePath function will return a string with the appropriate URI for the requested tile. For OSM tiles, a typical tile looks like http://tile.openstreetmap.org/zoom/x/y.png. The function to create this tile is therefore:

function getTilePath(tile) {
 return "http://tile.openstreetmap.org/" + tile.levelOfDetail + "/" + tile.x + "/" + tile.y + ".png";
}

Put this all together and your code should look like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html  xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
 function GetMap() {
 // Create a basic map
 var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"),
 { credentials: "YOURBINGMAPSKEYHERE",
 center: new Microsoft.Maps.Location(56, 2),
 zoom: 5,
 // Don't load the Bing base map tiles
 mapTypeId: Microsoft.Maps.MapTypeId.mercator
 });

// Create the tile source
 var tileSource = new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });

// Construct the layer using the tile source
 var tilelayer = new Microsoft.Maps.TileLayer({ mercator: tileSource, opacity: 1 });

// Push the tile layer to the map
 map.entities.push(tilelayer);
 }

function getTilePath(tile) {
 // Construct the URI path for an OSM tile based on tile zoom/x/y
 return "http://tile.openstreetmap.org/" + tile.levelOfDetail + "/" + tile.x + "/" + tile.y + ".png";
 }
</script>
</head>
<body onload="GetMap();">
 <div id='mapDiv' style="position:relative; width:640px; height:480px;"></div>
</body>
</html>

And here’s what it looks like:

image

If you want to try some other tile providers, replace the URI constructed by the getTilePath() function with some of the following:

DeLorme World Basemap

function getTilePath(tile) {
 return "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
 }

image

ESRI World Imagery

function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/" + tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}

image

January 7, 2011

Accessing a WMS Tile Server from Bing Maps v7

A recent question on the MSDN forums asked about how to create an overlay from a WMS tile server on Bing Maps. A short web search revealed that, although there are some existing articles on this topic, they’re either somewhat broken or very old.  So, I thought I’d write up how to do this using Bing Maps AJAX v7.0. If you want to see how to access a WMS server using the Bing Maps Silverlight control, please see my other post.

Bing Maps are created from sets of image tiles – each 256px wide by 256px high. Each tile is referred to by its quadkey – a unique string consisting of characters between 0 – 3, which describes the position and zoom level at which this tile should be placed.

The following tile, for example, has the quadkey 120200223312:

r120200223312

There’s an excellent MSDN article that explains the Bing Maps Tile System in more detail, so I won’t bother repeating it here.

WMS servers, in contrast, create map images of (potentially) variable height and width, covering the features in the geographic area specified by a supplied bounding box. The bounding box defines the minimum and maximum longitude and latitude extents of the image – in other words, the West, South, East, and North points of the image.

Since Bing Maps thinks in terms of quadkeys, and WMS servers in terms of bounding boxes, to add a WMS layer to Bing Maps we simply need to intercept any tile layer requests via an intermediary handler, convert the quadkey to the relevant WMS bounding box, and request and return the supplied image as a tile to Bing Maps.

To start with, let’s define a basic map, and add a tile layer to it. The source for the tile layer will point to an .ashx web handler, which we’ll use to intercept the tile requests and pass them on to the WMS server. The handler will be passed a single parameter, q, which is the quadkey of the requested tile.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
  <title>Displaying WMS Tile Layers on Bing Maps v7</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
  <script type="text/javascript">
    function GetMap() {
      // Create a default map
      var map = new Microsoft.Maps.Map(
        document.getElementById("mapDiv"),
        { credentials: "ENTERYOURBINGMAPSKEY",
          center: new Microsoft.Maps.Location(50.5, 10),
          mapTypeId: Microsoft.Maps.MapTypeId.birdseye,
          zoom: 6
        }
      );

      // Define the tile layer source
      var tileSource = new Microsoft.Maps.TileSource({ uriConstructor: 'WMSHandler.ashx?q={quadkey}' });

      // Construct the layer using the tile source
      var tilelayer = new Microsoft.Maps.TileLayer({ mercator: tileSource, opacity: 0.9 });

      // Push the tile layer to the map
      map.entities.push(tilelayer);
    }
  </script>
</head>
<body onload="GetMap();">
<div id='mapDiv' style="position:relative; width:600px; height:800px;"></div>
</body>
</html>

Now, we need to define the WMSHandler.ashx handler that will request an image from the WMS server for a given tile quadkey. For the following example, I’ve used a WMS server that displays various images of data of Germany. You can use any WMS server you want, so long as it supports bounding box coordinates supplied using EPSG:4326, and returns images in Spherical Mercator projection as used by Bing Maps. You can find a list of publicly-accessible WMS servers at http://www.ogc-services.net/
The maths required to convert from a quadkey to N/S/E/W coordinates is contained in the following QuadKeyToBBOX() function. For more information, check out the MSDN Bing Maps Tile System article mentioned earlier.

Having obtained the appropriate coordinates, these are substituted into the url template at the {0} placeholder following the BBOX parameter. The image is then requested from the WMS server and served back to the Bing Maps client.

Here’s the C# code for the tile source handler:

using System;
using System.Web;
using System.Drawing; // Bitmap
using System.Drawing.Imaging; // ImageFormat
using System.Net; // WebClient
using System.IO; // MemoryStream

public class WMSHandler : IHttpHandler
{
// Bing Maps tiles are always 256px x 256px
public const int TILE_HEIGHT = 256, TILE_WIDTH = 256;

// Returns the bounding box coordinates for a given tile quadkey
public string QuadKeyToBBox(string quadKey)
{
int zoom = quadKey.Length;
int x = 0, y = 0;

// Work out the x and y position of this tile
for (int i = zoom; i > 0; i--)
{
int mask = 1 << (i - 1);
switch (quadKey[zoom - i])
{
case '0':
break;
case '1':
x |= mask;
break;
case '2':
y |= mask;
break;
case '3':
x |= mask;
y |= mask;
break;
default:
throw new ArgumentException("Invalid QuadKey digit sequence.");
}
}

// From the grid position and zoom, work out the min and max Latitude / Longitude values of this tile
double W = (float)(x * TILE_WIDTH) * 360 / (float)(TILE_WIDTH * Math.Pow(2, zoom)) - 180;
double N = (float)Math.Asin((Math.Exp((0.5 - (y * TILE_HEIGHT) / (TILE_HEIGHT) / Math.Pow(2, zoom)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - (y * TILE_HEIGHT) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;
double E = (float)((x + 1) * TILE_WIDTH) * 360 / (float)(TILE_WIDTH * Math.Pow(2, zoom)) - 180;
double S = (float)Math.Asin((Math.Exp((0.5 - ((y + 1) * TILE_HEIGHT) / (TILE_HEIGHT) / Math.Pow(2, zoom)) * 4 * Math.PI) - 1) / (Math.Exp((0.5 - ((y + 1) * TILE_HEIGHT) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;
double[] bounds = new double[] { W, S, E, N };

// Return a comma-separated string of the bounding coordinates
return string.Join(",", Array.ConvertAll(bounds, s => s.ToString()));
}

public void ProcessRequest(HttpContext context)
{
// Retrieve the requested quadkey
string quadKey = context.Request.QueryString["q"];
string bbstr = QuadKeyToBBox(quadKey);

// Define the URL of the WMS service, with placeholder for bounding box
string urlTemplate = "http://wms1.ccgis.de/cgi-bin/mapserv?map=/data/umn/germany/germany.map&&VERSION=1.1.1&REQUEST=GetMap&SERVICE=WMS&SRS=EPSG%3A4326&BBOX={0}&WIDTH=256&HEIGHT=256&LAYERS=Topographie";

// Insert the bounding box coordinates into the URL
string url = string.Format(urlTemplate, bbstr);

// Retrieve the tile image from the WMS server
WebClient webClient = new WebClient();
byte[] bImage = webClient.DownloadData(url);
MemoryStream memoryStream = new MemoryStream(bImage);
Bitmap wmsBitmap = (Bitmap)Bitmap.FromStream(memoryStream);
memoryStream.Close();
// Convert image to stream
MemoryStream streamImage = new MemoryStream();
wmsBitmap.Save(streamImage, ImageFormat.Png);
wmsBitmap.Dispose();

// Send response back to the client
context.Response.ContentType = "image/png";
context.Response.AddHeader("content-length", System.Convert.ToString(streamImage.Length));
context.Response.BinaryWrite(streamImage.ToArray());

// Clean up
streamImage.Dispose();
}

public bool IsReusable
{
get
{
return false;
}
}
}

And here’s what it looks like when you view this WMS layer on Bing Maps, illustrating the topography of Germany:

image

using System;
using System.Web;
using System.Drawing; // Bitmap
using System.Drawing.Imaging; // ImageFormat
using System.Net; // WebClient
using System.IO; // MemoryStream 

public class WMSHandler : IHttpHandler
{
// Bing Maps tiles are always 256px x 256px
public const int TILE_HEIGHT = 256, TILE_WIDTH = 256;

// Returns the bounding box coordinates for a given tile quadkey
public string QuadKeyToBBox(string quadKey)
{
int zoom = quadKey.Length;
int x = 0, y = 0;

// Work out the x and y position of this tile
for (int i = zoom; i > 0; i–)
{
int mask = 1 << (i – 1);
switch (quadKey[zoom - i])
{
case ’0′:
break;
case ’1′:
x |= mask;
break;
case ’2′:
y |= mask;
break;
case ’3′:
x |= mask;
y |= mask;
break;
default:
throw new ArgumentException(“Invalid QuadKey digit sequence.”);
}
}

// From the grid position and zoom, work out the min and max Latitude / Longitude values of this tile
double W = (float)(x * TILE_WIDTH) * 360 / (float)(TILE_WIDTH * Math.Pow(2, zoom)) – 180;
double N = (float)Math.Asin((Math.Exp((0.5 – (y * TILE_HEIGHT) / (TILE_HEIGHT) / Math.Pow(2, zoom)) * 4 * Math.PI) – 1) / (Math.Exp((0.5 – (y * TILE_HEIGHT) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;
double E = (float)((x + 1) * TILE_WIDTH) * 360 / (float)(TILE_WIDTH * Math.Pow(2, zoom)) – 180;
double S = (float)Math.Asin((Math.Exp((0.5 – ((y + 1) * TILE_HEIGHT) / (TILE_HEIGHT) / Math.Pow(2, zoom)) * 4 * Math.PI) – 1) / (Math.Exp((0.5 – ((y + 1) * TILE_HEIGHT) / 256 / Math.Pow(2, zoom)) * 4 * Math.PI) + 1)) * 180 / (float)Math.PI;
double[] bounds = new double[] { W, S, E, N };

// Return a comma-separated string of the bounding coordinates
return string.Join(“,”, Array.ConvertAll(bounds, s => s.ToString()));
}

public void ProcessRequest(HttpContext context)
{
// Retrieve the requested quadkey
string quadKey = context.Request.QueryString["q"];
string bbstr = QuadKeyToBBox(quadKey);

// Define the URL of the WMS service, with placeholder for bounding box
string urlTemplate = “http://wms1.ccgis.de/cgi-bin/mapserv?map=/data/umn/germany/germany.map&&VERSION=1.1.1&REQUEST=GetMap&SERVICE=WMS&SRS=EPSG%3A4326&BBOX={0}&WIDTH=256&HEIGHT=256&LAYERS=Topographie”;

// Insert the bounding box coordinates into the URL
string url = string.Format(urlTemplate, bbstr);

// Retrieve the tile image from the WMS server
WebClient webClient = new WebClient();
byte[] bImage = webClient.DownloadData(url);
MemoryStream memoryStream = new MemoryStream(bImage);
Bitmap wmsBitmap = (Bitmap)Bitmap.FromStream(memoryStream);
memoryStream.Close();
// Convert image to stream
MemoryStream streamImage = new MemoryStream();
wmsBitmap.Save(streamImage, ImageFormat.Png);
wmsBitmap.Dispose();

// Send response back to the client
context.Response.ContentType = “image/png”;
context.Response.AddHeader(“content-length”, System.Convert.ToString(streamImage.Length));
context.Response.BinaryWrite(streamImage.ToArray());

// Clean up
streamImage.Dispose();
}

public bool IsReusable
{
get
{
return false;
}
}
}

Tags: ,
December 12, 2010

Pushpin Altitude and the Birdseye Map Style (Bing Maps v7)

For the inaugural post in my new blog, I thought I’d cover one of the (many) issues that seems to have been generating some confusion following the release of the new v7 AJAX API – namely how to set altitude on a pushpin location, and the effects of doing so.

Before I get into any code, it’s worth making a couple of important observations about altitude:

- Although it may seem obvious, changing altitude will have no effect on the display of a pushpin if you’re using a two-dimensional, top-down map style.

- In contrast, when displaying maps in three-dimensional view (including oblique “birdseye” modes), considering altitude becomes essential in order to correctly position a pushpin in the display.

Under the old v6.x map control, you only generally needed to worry about setting the altitude of your pushpins if you were explicitly using the birdseye or (now deprecated) 3d mode. If you didn’t use those modes, you could set the EnableBirdseyeMode and showSwitch map options to false to prevent your users from accessing the birdseye and 3d map styles respectively, and then use only the road and aerial “top-down” styles, comfortably ignoring any implications of altitude. Every pushpin therefore needed only a latitude and longitude coordinate.

In version 7, the situation is slightly different for two reasons: firstly, there does not currently appear to be any way of limiting the range of map styles available – any style can be chosen from the main menu bar by the end user, including birdseye. Not only that, but the default Automatic map style is now a hybrid style that smoothly transitions from two-dimensional top-down road and aerial imagery at distant zoom levels into “tilted” three-dimensional birdseye at close zoom levels. The effect of these changes is that all Bing Maps developers should now consider what effect altitude has on the way their pushpins are displayed using the different map styles in v7.

Now, for an example: the following code listing creates two pushpins. The first, whose location is specified using only latitude and longitude coordinates, is positioned at the default ground level, at the base of Nelson’s column in Trafalgar Square. The second is positioned exactly 50m above the first, and is specified using both the altitude and the altitudereference of the location.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

  <title></title>

  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

  <script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>

  <script type="text/javascript">

    function GetMap() {

      var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"),

        { credentials: "BINGMAPSKEY",

          center: new Microsoft.Maps.Location(51.5077007450027, -0.12793064117432637 ),

          zoom: 19

        });

      var NelsonsColumnBottom = new Microsoft.Maps.Pushpin(

        new Microsoft.Maps.Location(51.50772, -0.1279303)

      );

      var NelsonsColumnTop = new Microsoft.Maps.Pushpin(

        new Microsoft.Maps.Location(51.50772, -0.1279303, 50, Microsoft.Maps.AltitudeReference.ground)

      );

      map.entities.push(NelsonsColumnBottom);

      map.entities.push(NelsonsColumnTop);

    }

  </script>

</head>

<body onload="GetMap();">

  <div id='mapDiv' style="position: relative; width: 1024px; height: 768px;">

  </div>

</body>

</html>

 

When viewing this map at zoom level 16, the Automatic map style chooses the flat 2d road map view, so the two pushpins are completely superimposed:

automatic_16

However, zoom in one level and the Automatic map style changes to enhanced birdseye view, so the two pushpins diverge to show the bottom and top of the column:

automatic_17

Since we are viewing at an oblique angle from the south, while the bottom pushpin (placed on the ground) appears to have remained in the same place the “top” pushpin appears to have shifted north up the map (whereas, in fact, it is directly above the bottom pushpin). Notice that the image above is taken at zoom level 17, which, in v6.x would still only be capable of showing a flat aerial map (the old “birdseye” mode only being available at the very highest level of zoom).

The enhanced birdseye mode in v7 that replaces the old “aerial” mode still demonstrates perceivable 3d and distinction between these two pushpin locations even when significantly zoomed out. The following image demonstrates that there is still a slight perceivable difference between the location of the pushpins at the top and bottom of Nelson’s column when viewed in birdseye mode at zoom level 14:

automatic_14

Personally, I think the enhanced birdseye style is great since you get the best of both worlds: the traditional aerial imagery view combined with an added sense of depth perception from the slightly oblique angle. However, many developers used to dealing only with the purely top-down “aerial” view of 6.x might not have thought of some of the implications that the new map styles create on the clarity of pushpin location. The Bing Maps default, if no altitude is specified, is to place a pushpin at terrain height of that location (i.e. on the ground). However, there appear to be cases where the underlying terrain elevation data is incorrect, in which case pushpins are incorrectly placed above or below the correct ground height. When viewed obliquely, this can make it appear that they are then misplaced laterally…. to demonstrate what I mean by this, consider the following image:

nelsoncolumn_downingst

At first glance, this map might appear to be highlighting two street locations; one in Trafalgar Square and 10 Downing Street. However, it’s actually showing the same points as used earlier in this post – both placed at exactly the same latitude/longitude. The only difference is that this time the top point has been raised to an altitude of 500m. When viewed from this orientation, and combined with the expectation that both points lie on the same level, may lead a viewer to erroneously conclude that the two pushpins lie at different positions on the ground. So, if your pushpins appear to be displayed in the wrong place, be sure to check not only the latitude/longitude values, but also that you’ve specified the correct altitude.

Note: There is currently an error on the MSDN documentation concerning the location class. http://msdn.microsoft.com/en-us/library/gg427612.aspx should describe the property that determines the basis for stating an altitude of a location as altitudeReference, not altitudeMode.

Follow

Get every new post delivered to your Inbox.

Join 53 other followers