Map Control: Zoom Levels -> Resolution

A common question we get is, “What is the resolution or scale at a particular zoom level in Virtual Earth or Windows Live Local?”  Well, that is actually a little more complex of answer than you might expect.  We use Mercator projection across the maps in the Virtual Earth Map Control so they can be be cut up into image tiles for quick delivery to clients and reliable stitching back together of the images.  But, because it is a cylindrical projection, the map gets distorted as you approach the poles–just look at Antarctica to see the effect.  So, we measure the effective resolution at the equator where there is the least distortion.  

The resolution at the equator for each zoom level (see this earlier post to find your current zoom level in Windows Live Local):

Meters/Pixel Zoom Level
78271.52 1
39135.76 2
19567.88 3
9783.94 4
4891.97 5
2445.98 6
1222.99 7
611.50 8
305.75 9
152.87 10
76.44 11
38.22 12
19.11 13
9.55 14
4.78 15
2.39 16
1.19 17
0.60 18
0.30 19

How did we arrive at these seemingly convoluted numbers? Well, honestly, we picked a view that we thought looked good at one of the closest zoom levels bottom and then just did some math to generate the rest–hence the kind of wacky numbers at higher zoom levels.

But, remember I said it was complicated? Well to get the resolution where you are looking on the map, you actually have to do some quick math as it varies by latitude. To get the resolution where you are:

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

But, what about Scale–this is just resolution?

For you cartographers or junior explorers out there, who think not in resolution but in map scale ratios, you can do a little more math to convery it correctly.  To convert the map resolution into scale, you need to know (or assume) the screen resolution.  Then the formula becomes:

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

For example, assuming a ScreenRes of 100 pixels/inch, the map scale at level 10 and latitude 40 degrees is:

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

You can thank Joe, a Tech Lead on Virtual Earth, for the excellent explanation.

Join the conversation

11 comments
  1. Anonymous

    Comment:  I think the "* pi/180" is missing from the Map resolution calculation.

    Question:  Isn’t the map resolution above the east-west resolution?  Shouldn’t the north-south resolution be constant?  If so, do you just do away with the cos multiplication altogether?

  2. Anonymous

    On the comment, you’re right — it should be cos(latitude * pi/180).

    On the question, the map resolution applies to both the X and Y axes (east-west and north-south).  In the Mercator projection, the X and Y scale factors are always equal.

    It’s true that the number of meters per degree is constant in the north-south direction, but in the Mercator projection, the number of meters per pixel varies with the latitude, in both the north-south and east-west directions.  See http://www.3dsoftware.com/Cartography/USGS/MapProjections/Cylindrical/Mercator/ for an illustration with grid lines.

  3. Anonymous

    Hi,

    Are there any API calls on VE available or planned to provide zoom level -> meters/pixel@equator values?  I’m hardcoding(!) this table into a mapping app now – would much rather get this from you.

    Thanks!

    jmp

  4. Anonymous

    resolved my own question…

    this works great – many thanks for the post!

  5. Anonymous

    Where did you get the constant, 156543.04, in your calculation for the map resolution?  Using your equation versus that found in the sample code in the following link in the GroundResolution() method, I get two very different numbers.

    msdn.microsoft.com/…/bb259689.aspx

    For a latitude of 30 degrees and zoom level 3, we have:

    156543.04 * Math.Cos(30 * Math.PI / 180) / (2^3) = 135570.249425644

    The equation from the link above produces:

    Math.Cos(30 * Math.PI / 180) * 2 * Math.PI * EarthRadius / (256 << 3) = 16946.280520896595

    Where EarthRadius is 6378137 meters and the 256 comes from the fact that each map tile image is 256 pixels wide.

    I know your original article is dated Feb. 25, 2006 and it's now June 3, 2009, so there may be some differences, but the values you have for the Meters Per Pixel/Zoom Level are still the same, so I think the map resolution equation should be the same.

  6. Anonymous

    You've miscalculated the first line, by forgetting to divide by 2^3 (see highlighted section).

    To answer your question, the constant 156543.04 was calculated by collapsing several constants into a single constant:  6378137 * 2 * pi / 256.

    CP

  7. Anonymous

    Thanks, Chris.  My bad.

  8. Chris Pendleton

    Just to be clear…when I refered to 2^3 I meant 2 raised to the power of 3, not xor (sorry). So, for you coders it's 2**3. :) Hope that helps.

    CP

  9. wloescher

    We're having trouble getting the map to accurately reposition to a specified locationbounding box (min/max lat/lon). We're currently using the SetMapView method, passing those two points, and getting incorrect map position.

    Has anybody come up with a method to determine the correct zoom level from an array of two or more points? We could then call the SetMapView with the center point and zoom level.

    Thanks in advance…

  10. wloescher

    Answered my own question: create array with two bounding box points, SetMapView(points), and then call ZoomIn().

  11. hectormeneses333

    wloescher and myself work in the same team and we experienced erratic behavior in the solution that he provided. The map would intermittently zoom out/in or pan left/right out of the correct zoom/position.

    In the happy side, we found the following solution that works in all cases.

    Basically, first you need to get a best fit to the two points in your bounding box using a dummy VEShapeLayer. Then call SetMapView with a delay of 1 second using the best fit bounding box. No need for extra ZoomIn.

    function repositionMap( topLeft, bottomRight ) {

               var dummyLayer = new VEShapeLayer();

               dummyLayer.AddShape( new VEShape( VEShapeType.Pushpin, topLeft));

               dummyLayer.AddShape( new VEShape( VEShapeType.Pushpin, lowerRight));

               _bestFitBoundingBox = dummyLayer.GetBoundingRectangle();

               setTimeout("MapSetMapView();", 1000);

    }

    var _bestFitBoundingBox;

    function MapSetMapView() {

       _map.SetMapView(_bestFitBoundingBox);

    }

    In this way, repositioning works for all cases just fine.

    Now, why is this delay needed?

    This issue was haunting us for quite some time, so I hope this solution can help others to avoid the frustration we went through.

    Hector

Comments are closed.