Export (0) Print
Expand All
10 out of 18 rated this helpful - Rate this topic

Integrating OGC Web Mapping Services

Cc161076.853e305c-ff8c-4cfb-ac3d-457e77ade1cd(en-us,MSDN.10).jpg

Bing Maps is Microsoft’s entry into the mapping realm. Even though Bing Maps is relatively new (it became a public beta in July 2005) Microsoft itself is not new to the mapping business. Microsoft has been involved in mapping for more than 12 years and the experience from these years was a prerequisite to build the service.

Cc161076.59a3fb3c-7f64-42bb-b51e-0eefa5956a14(en-us,MSDN.10).jpg

Figure 1. Bing Maps provides major road networks worldwide and street-level coverage in 68 regions

Bing Maps not only provides a highly interactive user interface to the traditional form of roadmaps but it also grants developers access to ortho-images, oblique aerial images (so called birdseye imagery) as well as to 3D terrain and city models. Unlike most of the competitive products Bing Maps can do all of this on the Web and it can be automated through one API.

Cc161076.8950d66a-0af9-486d-84d6-38f20115870f(en-us,MSDN.10).jpg

Figure 2. Birdseye images are currently available in the US, UK, FR, MC, IT, DE, NL, ES, CH, and NO

Cc161076.53f2bc8e-a23d-4c8c-b96b-fb0dc6ffe0cd(en-us,MSDN.10).jpg

Figure 3. 3D city models are currently available in the US and UK

The latest beta release of Bing Maps contains standard features like geocoding, proximity search and routing, but it is the extensibility and the ability to integrate in different system landscapes that makes Bing Maps so valuable.

Cc161076.1d0735bd-40a5-400e-a0cc-b9148a2e1d1c(en-us,MSDN.10).jpg

Figure 4. Ortho images are currently available in the US, UK, CA, JP, IT, FR, and AU

As part of the API there are already all the necessary methods to create layers and add individual points, polylines and polygons to a layer. There are also methods which allow you to add GeoRSS-feeds or Collections which you or someone else created in Bing Maps. These are excellent means to integrate communities and have near real-time data available. It is also very simple to create AJAX calls to other Web services like the MapPoint Web Serviceor just one which accesses your database to overlay specific information for this location. In addition, you can also overlay your own raster data on top of Bing Maps. This technique is covered in more detail later in this article.

You can further enhance the user experience by handling events that are fired whenever you zoom or pan the map, change the style or the mode of the map, press a key on your keyboard, or click a mouse button or move the mouse.

If you want to get started with Bing Maps you can do that today. Access to the API is not restricted and is currently free for non-commercial use. If you discover you have an appetite for Bing Maps, look at the topic. The Interactive SDK is a great and simple way to build your first Bing Maps application in minutes. However, if you don’t find what you need, don’t give up and have a look at the SDK on the Microsoft Developer Network (MSDN).

Overlay your own Raster-Data on Top of Bing Maps

As mentioned previously there are various types of layers which you can add to Bing Maps. The document discusses the tile layer.

Cc161076.de556d87-6c80-437d-96b4-4a5b43ba241c(en-us,MSDN.10).jpg

Figure 5. The Bing Maps Tile System

Whenever you load the Bing Maps AJAX Control 6.3 it retrieves tiles from the Microsoft data centers and stitches them together. But how does it know which tiles to retrieve for a specific location?

Joe Schwarz wrote an excellent article about the Bing Maps Tile System which is mentioned in . For now it is important to understand that the Bing Maps tiles are indexed with a quad-key and that every tile has a unique name which determines its position, its neighbors and the zoom-level. Each of these tiles has a size of 256 x 256 pixels.

The Bing Maps AJAX Control 6.3 API likes it simple. If you want to overlay your own tiles on Bing Maps, you point to a URL for a virtual directory which includes your own tiles. Bing Maps then looks for the tiles with the same name as those which are currently used in the Bing Maps and overlay them with the opacity value and at the z-index that you determine. A sample function is shown as follows.

function AddTileLayer(layer, maxlat, maxlon, minlat, minlon, url, minlvl, maxlvl, opac) 
{ 
   var bounds = [new VELatLongRectangle( 
      new VELatLong(maxlat, maxlon), 
      new VELatLong(minlat, minlon))]; 
   var tileSourceSpec = new VETileSourceSpecification(layer, url); 
   tileSourceSpec.Bounds = bounds; 
   tileSourceSpec.MinZoomLevel = minlvl; 
   tileSourceSpec.MaxZoomLevel = maxlvl; 
   tileSourceSpec.Opacity = opac; 
   map.AddTileLayer(tileSourceSpec); 
}

Listing 1. Adding a Tile Layer

In the url we pass the parameter %4.png. %4 passes the quad-key for the current tile (yes, it is inevitable to make a request for each tile which is on the current map view). bounds defines the bounding box in which you want to show the overlay. Together with the MinZoomLevel and MaxZoomLevel it can be used to optimize performance. Also note that even though Bing Maps has a maximum zoom-level of 19 (~30 cm/pixel), we can use higher zoom-levels for our own tile layers. A list of Bing Maps zoom-levels can be found in .

How do we get to the tiles in the first place?

A very nice tool to create your own tile layers for Bing Maps is MapCruncher. This tool was developed by Microsoft Research, is available for free download, and makes it very easy to create tiles.

Cc161076.d4fc030f-91c5-4936-841b-e657da8c7f78(en-us,MSDN.10).jpg

Figure 6. MapCruncher

You can manually geo-reference any kind of image or PDF document by moving a point in the image below the crosshair in the left window and the corresponding Bing Maps location below the crosshair in the right window. Once you lock the view the Mercator projection is applied to the image. You can then define up to which zoom-level you want to make this image available and have MapCruncher cut it down into pyramid layers and tiles for each layer. MapCruncher will take care for naming the tiles as required by the Bing Maps quad-key structure. Once this is done, configure a virtual directory on your web server and point it to the folder with the rendered tiles.

MapCruncher even allows you to render images and PDFs from the command line so that you could easily set up jobs and create new tile sources whenever you have a new image, for example, for a weather-radar that is updated every 15 minutes. Some examples for the usage of MapCruncher are as follows.

Cc161076.3132556f-d628-445b-9b60-dd9ac2543567(en-us,MSDN.10).jpg

Figure 7. Using MapCruncher to create a floor plan

Cc161076.b178e124-5088-4a33-a29f-bb3d4c23fd32(en-us,MSDN.10).jpg

Figure 8. Using MapCruncher to create your own imagery

Cc161076.9ca87fc9-7914-4feb-95de-e9c6407b04a7(en-us,MSDN.10).jpg

Figure 9. Using MapCruncher to create coverage maps

Cc161076.916aed16-2497-4ab8-86f2-76cb3ecd5994(en-us,MSDN.10).jpg

Figure 10. Using MapCruncher to create super-high resolution images

MapCruncher is a great tool, but it only creates a static result: you always have to pre-render the tiles. Conversely there are an increasing number of Web Mapping Services (WMS). This is a standard which has been defined by the Open Geospatial Consortium (OGC) that enables sharing geospatial data between different entities. The result is returned as an image. Although Bing Maps cannot be used as a WMS server without violating the terms of use, it can be used as a WMS client. Using Bing Maps as a WMS client is the focus of the remainder of this article.

Accessing a Web Mapping Service

The first step in accessing a Web mapping service is to find out what capabilities the service offers. This is accomplished by a GetCapabilities call, as follows:

http://server?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities

Listing 2. WMS GetCapabilities request

The URL contains the WMS server (server), parameters for the type of service, the version and the request-type. In this case the request is of type GetCapabilities. The WMS returns an XML-document in the HTTP-response. An example for such an XML-document is shown in Result of a WMS GetCapabilities Request.

A WMS can provide one or more layers and information about the layers. The information we need includes:

  • Name

  • Spatial reference system (SRS)

  • Bounding box

  • Format of map

If we examine the following sample layer, we see the name is Geologische_Karte, the SRS is EPSG:4326, the four sides of the bounding box are8.1048153789, 46.9109504263, 14.1624073557, and 50.6062640122, and the format of the map is image/gif.

<Layer queryable="0">
   <Name>Geologische_Karte</Name>
   <Title>Geologische Karte 1:500 000</Title>
   <SRS>EPSG:4326</SRS>
   <LatLonBoundingBox minx="8.1048153789" miny="46.9109504263"
                      maxx="14.1624073557" maxy="50.6062640122"/>
   <ScaleHint min="0" max="1870"/>
   <Style>
      <Name/>
      <Title>Geologische Karte 1:500.000</Title>
      <LegendURL width="1300" height="4615">
      <Format>image/gif</Format>
      <OnlineResource xmlns:xlink=http://www.w3.org/1999/xlink
                      xlink:href="http://www.bis.bayern.de/bis/clientdata/legenden/gk500_leg/gk500_leg.gif" 
                      xlink:type="simple"/>
      </LegendURL>
   </Style>
</Layer>

Listing 3. WMS layer information from a GetCapabilities request

Bing Maps can only support EPSG:4326 (geographic 4326) SRS directly. The LatLonBoundingBox tells you for which areas this service is available. It is a good idea to call the VEMap.SetMapView method with these values.

Let's create a sample for this service.

http://www.bis.bayern.de/wms/gla/gk500_wms ?VERSION=1.1.1
   &SERVICE=WMS
   &REQUEST=GetMap
   &SRS=EPSG:4326
   &FORMAT=image/png
   &LAYERS=Geologische_Karte
   &WIDTH=256
   &HEIGHT=256
   &BBOX=10.89843,48.45926,11.24862,48.69096

Listing 4. A WMS GetMap request

The GetMap request uses information from the GetCapabilities request. The width and height of the image to retrieve is always 256 x 256 pixels as this is the size of a Bing Maps tile. Finally the request includes values for the bounding box. The next task is to use Bing Maps to determine the latitudes and longitudes of the upper left and lower right corners of each tile. The next section describes how to accomplish this task. Figure 11 displays the result of this GetMap request.

Cc161076.8b950f09-0705-47a0-b7d8-d7422b752916(en-us,MSDN.10).jpg

Figure 11. Result of the WMS GetMap request

Architecture

To summarize what has been discussed so far, to use a Bing Maps tile layer there is a HTTP-request made for each tile that is retrieved from the tile source, and the request uses a quad-key parameter. If MapCruncher was used to create a tile source, requests are directed to the virtual directory which contains the pre-rendered tiles with corresponding names.

However, retrieving tiles from a WMS requires a different approach. The HTTP-request is sent to a Web service. The Web service acts as a proxy to the WMS-server, retrieving the quad-key from the request-parameter and determining the latitudes and longitudes of the upper left and lower right corner of the tile. Since the Bing Maps Tile System is structured with quad-keys, if the latitude, longitude, and a zoom-level are known, the tile which includes this point can be computed. Similarly, given a tile, the latitude and longitude of the upper left and lower right corner can be computed. Once the latitudes and longitudes of the bounding box are computed, the WMS GetMap-request can be created and it returns the image in the HTTP-response to the Bing Maps AJAX Control 6.3. The Bing Maps AJAX Control 6.3 ensures that this process is repeated for each tile that is currently shown on the control.

Cc161076.d983db52-7a46-4f9b-a26f-f798df8dd2d1(en-us,MSDN.10).png

Figure 12. How AddTileLayer is mapped to a WMS GetMap request

The process is simple, but keep in mind that the tiles are in a flat Mercator-projection while the coordinates are in a spherical WGS 84 system. The mathematics are more complex and are explained in .

Creating a Web Page with a Bing Maps AJAX Control 6.3

Creating a Web page with a Bing Maps AJAX Control 6.3 is straightforward. The <body> element of the page contains a <div> element that specifies the size of the map and a reference to an onload event handler function, GetMap, that is called when the Web page is loaded. This JavaScript function loads the map with an optionally defined center-point, an optional zoom-level and an optional MapStyle, which in this example is VEMapStyle.Hybrid, which is a road-network overlaid on aerial image).

The buttons below the map allow add the tile layer from the WMS by executing the JavaScript AddTileLayer function. The ID of the Bing Maps tile and the name of the WMS-layer are added to the URL which calls the Web service. The boundaries of the layer and the minimum and maximum zoom-level are also defined. The z-index as well as the opacity can also be defined, if desired. Additional buttons call functions to show or hide the tile layer and to delete it.

Note that the only difference between a pre-rendered tile source, such as those created using MapCruncher, and the WMS tile source is that the former uses a virtual directory and the latter a Web service.

<!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>OGC-Compliant WMS</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=6.3"></script>
      <script type="text/javascript">
         var map = null;
         var Location = new VELatLong(48.13493370228959, 11.578216552734378);

         function GetMap()
         {
            map = new VEMap('myMap');
            map.LoadMap(Location, 10, VEMapStyle.Hybrid, false);
         }

         function AddTileLayer()
         {
            var bounds = [new VELatLongRectangle( 
               new VELatLong(50.50643983210549,9.173583984374993), 
               new VELatLong(47.27177506640826,13.765869140624984))];
            
            // WMSTileServer.ashx is the Web service
            var tileSourceSpec = new VETileSourceSpecification( 
                'WMSBavariaGeo',
                'WMSTileServer.ashx?id=%4&layer=Geologische_Karte');
            
            tileSourceSpec.Bounds       = bounds;
            tileSourceSpec.MinZoomLevel = 8;
            tileSourceSpec.MaxZoomLevel = 12;
            tileSourceSpec.Opacity      = 1;

            map.AddTileLayer(tileSourceSpec);
         }
         
         function HideTileLayer()
         {
            map.HideTileLayer('WMSBavariaGeo');
         }
         
         function ShowTileLayer()
         {
            map.ShowTileLayer('WMSBavariaGeo');
         }
         
         function DelTileLayer()
         {
            map.DeleteTileLayer('WMSBavariaGeo');
         }
      </script>
   </head>
   <body onload="GetMap();">
      <div id='myMap' style="position:relative; width:600px; height:400px;"></div>

      <input id="btnAdd" type="button" value="Add TileLayer" onclick="AddTileLayer()" style="width: 147px" />
      <input id="btnHide" type="button" value="Hide TileLayer" onclick="HideTileLayer()" style="width: 147px" />
      <input id="btnShow" type="button" value="Show TileLayer" onclick="ShowTileLayer()" style="width: 147px" />
      <input id="btnDel" type="button" value="Delete TileLayer" onclick="DelTileLayer()" style="width: 147px" />
   </body>
</html>

Listing 5. A Web site with the Bing Maps AJAX Control 6.3 as a WMS client

Creating the Web Service

The Web service requires two namespace references for image-processing, as follows.

using System.Drawing;
using System.IO;

The ProcessRequest method fetches the URL-parameters for the quad-key and the WMS-layer, as follows:

// Fetch URL-parameters
string tileIdParam = Context.Request.Params("ID");
string layerParam  = Context.Request.Params("layer");

The zoom-level relates directly to the length of the quad-key. If the quad-key is 120202113, the length is 9 digits and thus the zoom-level for this tile is 9.

// Get zoom level
int zoomLevel = requestParam.Length;

The area covered by this tile can be determined by calculating the binary value of the quad-key. Given the quad-key 120202113, the binary representation is as follows:

 1  2  0  2  0  2  1  1  3
01 10 00 10 00 10 01 01 11

The conversion code is very simple. It just takes each character in the quad-key and translates that into a two-character output code, as follows.

// Convert Quadkey to binary value
string myQuadKeyBin = "";
char [] myKeyCharArray = requestParam.ToCharArray();

for (int i = 0; i < zoomLevel; i++)
{
   switch (myKeyCharArray[i])
   {
      case '0': myQuadKeyBin += "00";
                break;
      case '1': myQuadKeyBin += "01";
                break;
      case '2': myQuadKeyBin += "10";
                break;
      default:  myQuadKeyBin += "11";
                break;
   }
}

Now that the binary quad-key value is converted to binary, the next step is to determine the binary X and Y numbers for the tile. Note that these values are not the pixel X and Y of the tile yet but the X and Y number of the whole tile itself in the coordinate system.

Cc161076.4cd95c7b-99ea-4f1d-8dd0-72449a16cb85(en-us,MSDN.10).png

Figure 13. Bing Maps coordinates at zoom level 3

Each tile is given an XY coordinates ranging from (0, 0) in the upper left to (2ZoomLevel - 1, 2ZoomLevel - 1) in the lower right. For example, at level 3 the tile coordinates range from (0, 0) to (7, 7) as shown in Figure 13. For further reference, see the article on the Bing Maps Tile System, listed in .

The first digit of the binary quad-key is the first digit of the binary TileY-coordinate, the second digit of the quad-key is the first digit of the binary TileX-coordinate and so on. Therefore, the example this would resolve as follows.

Quad-key:         1  2  0  2  0  2  1  1  3
Binary Quad-key: 01 10 00 10 00 10 01 01 11
Binary TileY:    0  1  0  1  0  1  0  0  1
Binary TileX:     1  0  0  0  0  0  1  1  1

The easiest way to accomplish this is to modify the previous for loop to create these values, as follows.

// Convert Quadkey to binary values
int zoomLevel = tileIdParam.Length;

string myQuadKeyBin = "";
string myXBinValues = "";
string myYBinValues = "";

char [] myKeyCharArray = tileIdParam.ToCharArray();

for (int i = 0; i < zoomLevel; i++)
{
   switch (myKeyCharArray[i])
   {
      case '0': myQuadKeyBin += "00";
                myXBinValues += "0";
                myYBinValues += "0";
                break;
      case '1': myQuadKeyBin += "01";
                myYBinValues += "0";
                myXBinValues += "1";
                break;
      case '2': myQuadKeyBin += "10";
                myYBinValues += "1";
                myXBinValues += "0";
                break;
      default:  myQuadKeyBin += "11";
                myYBinValues += "1";
                myXBinValues += "1";
                break;
   }
}

The next step is to convert the binary TileX and TileY to a decimal TileX and TileY coordinate using the following formula.

Binary TileY: 010101001

Decimal TileY: 0∙2ZoomLevel−1 + 1∙2ZoomLevel−2 + ⋯ + 1∙20 = 169

Binary TileX: 100000111

Decimal TileY: 1∙2ZoomLevel−1 + 0∙2ZoomLevel−2 + ⋯ + 1∙20 = 263

Once again, the easiest way to accomplish this is to modify the previous for loop to create these values, as follows.

// Convert Quadkey to binary values
int zoomLevel = tileIdParam.Length;

string myQuadKeyBin = "";
string myYBinValues = "";
string myXBinValues = "";

int myXDecValue = 0;
int myYDecValue = 0;

char [] myKeyCharArray = tileIdParam.ToCharArray();

for (int i = 0; i < zoomLevel; i++)
{
   int tmpVal = (int)Math.Pow(2, zoomLevel - (i + 1)); 

   switch (myKeyCharArray[i])
   {
      case '0': myQuadKeyBin += "00";
                myYBinValues += "0";
                myXBinValues += "0";
                break;
      case '1': myQuadKeyBin += "01";
                myYBinValues += "0";
                myXBinValues += "1";
                myXDecValue += tmpVal;
                break;
      case '2': myQuadKeyBin += "10";
                myYBinValues += "1";
                myXBinValues += "0";
                myYDecValue += tmpVal;
                break;
      default: myQuadKeyBin += "11";
               myYBinValues += "1";
               myXBinValues += "1";
               myXDecValue += tmpVal;
               myYDecValue += tmpVal;
               break;
   }
}

To determine the latitude and longitude of the upper left and lower right corner of this tile, PixelX and PixelY coordinates of the tile must first be calculated. Since each tile has a size of 256 x 256 pixels and we the decimal TileX and TileY coordinates are known, this is a simple calculation. For the upper left corner, the formula is as follows:

PixelYMin: TileYdec ∙ 256 = 169 ∙ 256 = 43264

PixelXMin: TileXdec ∙ 256 = 263 ∙ 256 = 67328

PixelYMax: (TileYdec + 1) ∙ 256 − 1 = (170 ∙ 256) − 1 = 43519

PixelXMax: TileXdec + 1 ∙ 256 − 1 = 264 ∙ 256 − 1 = 67583

Here is the code to make these calculations.

// Convert to pixels
int PixelXMin = myXDecValue * 256;
int PixelXMax = ((myXDecValue + 1) * 256) - 1;
int PixelYMin = myYDecValue * 256;
int PixelYMax = ((myYDecValue + 1) * 256) - 1;

The next step is to calculate the latitude and longitude of the upper left and the lower right corner. The mathematics is a bit complicated since the tile is a flat object but the latitude and longitude are in the WGS 84. The formula to calculate the longitude is as follows.

Longitude = ((PixelX * 360) / (256 * 2ZoomLevel)) - 180

In the example, these become:

LongMin = ((67328 * 360) / (256 * 29)) - 180 ~= 4.921875

LongMax = ((67583 * 360) / (256 * 29)) - 180 ~= 5.622253

Before the latitude is calculated, here some intermediate calculations to help refactor the code.

denom = 256 * 2ZoomLevel

efactor = e(0.5 - ((PixelY) / (denom) )* 4 * pi

The latitude is then calculated as the following.

Latitude = asin((efactor - 1) / (efactor + 1)) * (180 / pi)

The example becomes:

denom = 256 * 29 = 131072

efactormin = e(0.5 - ((43264) / (denom) )* 4 * pi ~= 8.459595

efactormax = e(0.5 - ((43519) / (denom) )* 4 * pi ~= 8.255283

Latitudemin = asin((efactor - 1) / (efactor + 1)) * (180 / pi) ~= 52.052490

Latitudemax = asin((efactor - 1) / (efactor + 1)) * (180 / pi) ~= 51.619721

The code to do all of this looks like this:

// Convert to latitude/longitude
// longitude
float LongMin = (float)(((PixelXMin * 360) / (256 * Math.Pow(2, zoomLevel))) - 180);
float LongMax = (float)(((PixelXMax * 360) / (256 * Math.Pow(2, zoomLevel))) - 180);

// Latitude
float denom = (float)(256 * Math.Pow(2, zoomLevel));

float eMinNum = (float)(PixelYMin / denom);
float eMinFactor = (float)(Math.Pow(Math.E, (0.5 - eMinNum) * 4 * Math.PI));
float minLat = (float)(Math.Asin((eMinFactor - 1) / (eMinFactor + 1)) * (180 / Math.PI));

float eMaxNum = (float)(PixelYMax / denom);
float eMaxFactor = (float)(Math.Pow(Math.E, (0.5 - eMaxNum) * 4 * Math.PI));
float maxLat = (float)(Math.Asin((eMaxFactor - 1) / (eMaxFactor + 1)) * (180 / Math.PI));

All the information is now available to prepare and execute the request to the WMS-server. The following code builds the URL, executes the request, and returns the response to the Bing Maps AJAX Control 6.3.

// URL to WMS
string myURL = "http://www.bis.bayern.de/wms/gla/gk500_wms?REQUEST=GetMap&SERVICE=WMS"
   + "&VERSION=1.1.1&SRS=EPSG:4326&FORMAT=image/png&LAYERS=" 
   + layerParam + "&WIDTH=256&HEIGHT=256&BBOX=" 
   + LongMin + ","
   + maxLat + "," 
   + LongMax + "," 
   + minLat;

System.Net.WebRequest myRequest = System.Net.WebRequest.Create(myURL);
System.Net.WebResponse myResponse = myRequest.GetResponse();

Bitmap myImage = new Bitmap(Image.FromStream(myResponse.GetResponseStream()));

WritePngToStream(myImage, Context.Response.OutputStream);

The WritePngToStream method looks like the following.

void WritePngToStream(Bitmap image, Stream outStream)
{
   MemoryStream writeStream = new MemoryStream();
   image.Save(writeStream, System.Drawing.Imaging.ImageFormat.Png);
   writeStream.WriteTo(outStream);
   image.Dispose();
}

If you have been paying attention, you probably have noticed that we do not use the variables myQuadKeyBin, myYBinValues, and myXBinValues after the switch statement. Therefore, the for loop should be modified as follows.

// Convert Quadkey to binary values
int zoomLevel = tileIdParam.Length;

int myXDecValue = 0;
int myYDecValue = 0;

char [] myKeyCharArray = tileIdParam.ToCharArray();

for (int i = 0; i < zoomLevel; i++)
{
   int tmpVal = (int)Math.Pow(2, zoomLevel - (i + 1)); 

   switch (myKeyCharArray[i])
   {
      case '0': break;
      case '1': myXDecValue += tmpVal;
                break;
      case '2': myYDecValue += tmpVal;
                break;
      default: myXDecValue += tmpVal;
               myYDecValue += tmpVal;
               break;
   }
}

Here are a few examples of the resulting tile layers.

Cc161076.f2111f63-274b-44a5-9694-d209d1f5a3c2(en-us,MSDN.10).png

Figure 14. WMS tile layer on top of Bing Maps

Cc161076.b7776a24-13b2-45c1-93b6-98b6756eeff5(en-us,MSDN.10).png

Figure 15. Ortho-Images as WMS tile layer on top of Bing Maps

Cc161076.64f0e06a-1924-44d9-803d-7015c46e2993(en-us,MSDN.10).png

Figure 16. Development plan as WMS tile layer on top of Bing Maps

Cc161076.be01a8ab-08b7-4b81-bd42-ef3ec1cdf9ed(en-us,MSDN.10).png

Figure 17. Maps as WMS tile layer on top of Bing Maps

Performance Considerations

Performance depends largely on the performance of the WMS server. The approach described in this article executes a WMS GetMap request for each Bing Maps tile that is shown in the Bing Maps AJAX Control 6.3. This results in number of roundtrips to the WMS-server. It may be worth considering a solution where only one image, which is as big as all tiles that are shown in the Bing Maps AJAX Control 6.3, is retrieved from the WMS server. This image would need to be separated into a number of tiles that matches the Bing Maps tiles in the Bing Maps AJAX Control 6.3. For further reference on this approach, see the paper Creating Bing Maps Tile-Layers on the Fly.

About the Author

Johannes Kebeck is a Microsoft Bing Maps technology specialist based in the UK.

Resources

  1. Bing Maps powered by Bing Maps, http://bing.com. There are some market-specific features. To discover the full power of Bing Maps go to http://bing.com

  2. Streetside Technology Preview, http://preview.local.live.com

  3. Interactive SDK, http://www.microsoft.com/maps/isdk/ajax

  4. Reference SDK, http://msdn2.microsoft.com/en-us/library/bb429619.aspx

  5. Bing Maps Tile System, http://msdn2.microsoft.com/en-us/library/bb259689.aspx

  6. Bing Maps on MSDN

  7. Forum, http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=537&SiteID=1

  8. Via Bing Maps, http://www.viavirtualearth.com

  9. Coverage with Roadmaps, http://www.microsoft.com/virtualearth/products/regional.mspx

  10. MapCruncher, http://go.microsoft.com/fwlink/?LinkID=99819&clcid=0x409

  11. Terms of use for the free API, http://www.microsoft.com/virtualearth/control/terms.mspx

Zoom Levels

Zoom Level Meters/Pixel

1

78,271.52

2

39,135.76

3

19,567.88

4

9,783.94

5

4,891.97

6

2,445.98

7

1,222.99

8

611.50

9

305.75

10

152.87

11

76.87

12

38.22

13

19.11

14

9.55

15

4.78

16

2.39

17

1.19

18

0.60

19

0.30

The Mathematical Details

This is not a true scale, but more of a resolution for imagery and graphics. So, in order to calculate the scale it is a bit more complex. The scale is actually based on the settings of the user's monitor and calculated as such. The resolution varies with latitude, as follows.

Map resolution = 156543.04 meters/pixel * cos(latitude) / (2 ^ Qlevel)

To convert the map resolution into scale, you need to know (or assume) the screen resolution. Then the formula becomes as follows.

Map scale = 1 : (ScreenRes pixels/inch * 39.37 inches/meter
* 156543.04 meters/pixel * cos(latitude * pi/180) / (2 ^ Qlevel))

For example, assuming a screen resolution of 100 pixels/inch, the map scale at level 10 and latitude 40 degrees is as follows.

Map scale = 1 : (100 pixels/inch * 39.37 inches/meter * 156543.04 meters/pixel * cos(40 * pi/180) / (2 ^ 10))
Map scale = 1 : (100 * 39.37 * 156543.04 * 0.766 / 1024)
Map scale = 1 : 461028.73

Result of a WMS GetCapabilities Request

Given the following request:

http://www.bis.bayern.de/wms/gla/gk500_wms?SERVICE=WMS&VERSION=1.1.1&REQUEST=GetCapabilities

The response should look something like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE WMT_MS_Capabilities SYSTEM "http://schemas.opengis.net/wms/1.1.1/WMS_MS_Capabilities.dtd">
<WMT_MS_Capabilities version="1.1.1">
<Service>
   <Name>OGC:WMS</Name>
   <Title>Web Map Service gk500_wms</Title>
   <Abstract>gk500_wms Web Map Service des Bayerischen Landesamt fuer Umwelt (LfU)</Abstract>
   <KeywordList>
      <Keyword>Bayerisches Landesamt fuer Umwelt (LfU)</Keyword>
      <Keyword>Geologie</Keyword> <Keyword>Boden</Keyword>
      <Keyword>BIS</Keyword>
   </KeywordList>
   <OnlineResource xmlns:xlink=http://www.w3.org/1999/xlink
                   xlink:href=http://www.bis.bayern.de/wms/gla/gk500_wms?
                   xlink:type="simple"/> 
   <ContactInformation> 
      <ContactPersonPrimary> 
         <ContactPerson/> 
         <ContactOrganization>Bayerisches Landesamt fuer Umwelt (LfU)</ContactOrganization> 
      </ContactPersonPrimary> 
      <ContactPosition/> 
      <ContactAddress> 
         <AddressType>postal</AddressType> 
         <Address>Lazarettstr. 67</Address> 
         <City>Muenchen</City>
         <StateOrProvince>Bayern</StateOrProvince>
         <PostCode>80636</PostCode> 
         <Country>Deutschland</Country> 
      </ContactAddress> <ContactVoiceTelephone/>
      <ContactFacsimileTelephone/> 
      <ContactElectronicMailAddress>
         bisgfa@lfu.bayern.de
      </ContactElectronicMailAddress> 
   </ContactInformation> 
   <Fees>none</Fees> 
   <AccessConstraints>none</AccessConstraints> 
</Service> 
<Capability> 
   <Request> 
      <GetCapabilities> 
         <Format>application/vnd.ogc.wms_xml</Format> 
         <DCPType> 
            <HTTP> 
               <Get> 
                  <OnlineResource xmlns:xlink="http://www.w3.org/1999/xlink" 
                                  xlink:href=http://www.bis.bayern.de/wms/gla/gk500_wms?
                                  xlink:type="simple"/> 
               </Get> 
            </HTTP> 
         </DCPType> 
      </GetCapabilities> 
      <GetMap> 
         <Format>image/png</Format> 
         <Format>image/jpeg</Format> 
         <Format>image/gif</Format> 
         <DCPType> 
            <HTTP> 
               <Get> 
                  <OnlineResource xmlns:xlink=http://www.w3.org/1999/xlink
                                  xlink:href=http://www.bis.bayern.de/wms/gla/gk500_wms?
                                  xlink:type="simple"/>
               </Get> 
            </HTTP> 
         </DCPType> 
      </GetMap> 
      <GetFeatureInfo> 
         <Format>text/html</Format> 
         <Format>application/vnd.ogc.wms_xml</Format> 
         <Format>text/xml</Format> 
         <Format>text/plain</Format> 
         <DCPType> 
            <HTTP> 
               <Get> 
                  <OnlineResource xmlns:xlink=http://www.w3.org/1999/xlink
                                  xlink:href=http://www.bis.bayern.de/wms/gla/gk500_wms?
                                  xlink:type="simple"/> 
               </Get> 
            </HTTP> 
         </DCPType>
      </GetFeatureInfo> 
   </Request> 
   <Exception> 
      <Format>application/vnd.ogc.se_xml</Format> 
      <Format>application/vnd.ogc.se_inimage</Format> 
      <Format>application/vnd.ogc.se_blank</Format> 
   </Exception> 
   <Layer queryable="0" opaque="0" noSubsets="0"> 
      <Title>gk500_wms</Title> 
      <SRS>EPSG:4326</SRS> 
      <SRS>EPSG:31468</SRS> 
      <SRS>EPSG:31467</SRS> 
      <SRS>EPSG:31469</SRS> 
      <LatLonBoundingBox minx="8.1048153789" miny="46.9109504263"
                         maxx="14.1624073557" maxy="50.6062640122"/>
      <BoundingBox SRS="EPSG:31467" 
                   maxx="3881348.6569" maxy="6141610.1915" 
                   minx="3255546.0547" miny="5183321.9371"/> 
      <BoundingBox SRS="EPSG:31468" 
                   minx="4224070.5" miny="5203193" 
                   maxx="4653051.37947922" maxy="5609942.05465011"/> 
      <BoundingBox SRS="EPSG:31469" 
                   minx="5068763.048817" miny="5414823.816382" 
                   maxx="5247928.922649" maxy="5614593.749362"/> 
      <Layer queryable="0"> 
         <Name>Geologische_Karte</Name> 
         <Title>Geologische Karte 1:500 000</Title> 
         <SRS>EPSG:4326</SRS> 
         <LatLonBoundingBox minx="8.1048153789" miny="46.9109504263" 
                            maxx="14.1624073557" maxy="50.6062640122"/> 
         <ScaleHint min="0" max="1870"/> 
         <Style> 
            <Name/> 
            <Title>Geologische Karte 1:500.000</Title> 
            <LegendURL width="1300" height="4615"> 
               <Format>image/gif</Format> 
               <OnlineResource xmlns:xlink=http://www.w3.org/1999/xlink
                               xlink:href="http://www.bis.bayern.de/bis/clientdata/legenden/gk500_leg/gk500_leg.gif" 
                               xlink:type="simple"/> 
            </LegendURL> 
         </Style> 
      </Layer> 
      <Layer queryable="1"> 
         <Name>Bayerngrenze</Name> 
         <Title>Bayerngrenze</Title> 
         <SRS>EPSG:4326</SRS> 
         <LatLonBoundingBox minx="8.9448687314" miny="47.2493919475" 
                            maxx="13.9102580841" maxy="50.5652993873"/> 
      </Layer> 
   </Layer> 
</Capability> 
</WMT_MS_Capabilities>
Did you find this helpful?
(1500 characters remaining)
Thank you for your feedback
Show:
© 2014 Microsoft. All rights reserved.