# How to "calculate" distance-to-ground of all 18 OSM zoom levels?

Assuming I'm building a 3D solution with the "world" being mapped to global geo-data supplied by OSM / Cloudmade... think http://www.webglearth.com but with completely different goals and use-cases. In fact we won't have a round sphere but only work off a "flat" (+ elevation later) surface/ground plane at all times.

Now assume further that we want to texture the "visible" fragments of the "ground" plane with OSM and/or Bing/MapQuest/Google aerial map tiles, the level of detail / zoom level to be chosen based on the "camera's" / viewer's "altitude" / distance-to-ground -- say in meters.

Knowing that 1. we only use OSM-equivalent tiles using exactly the same spherical mercator projection (which, from what I gather, is used by all the major map players ie. OSM, Google, Bing) and hence 2. always equivalent scale-distortions depending on latitude: how can I decide which zoom level to switch to when viewer altitude changes?

Of course the boundary cases are easy: zoom level 18 is to be used on-or-close-to-the-ground and zoom level 0 from "outer space". But the intermediate cases are a mystery to me when it comes to figuring out "from which altitude they had been taken if they were simple photos", so to speak.

I saw this page http://wiki.openstreetmap.org/wiki/Zoom_levels and some related pages but nowhere did I find an approximation on how to "map" a fictive altitude to a zoom level.

## 2 Answers

Some ideas that can help you:

1. Web tile has a certain resolution (PPI, pixels per inch). You probably want to avoid displaying the tile as a texture that looses a lot of PPI. So a tile that is 256x256 probably wouldn't look too good shown as a 1024x1024 texture.
2. Each OSM zoom level has its own (approximate) map scale.
3. Since you say you'll show a flat Earth, you can still calculate the current map scale based on the distance of the observer from the Earth, the latitude the observer is on and the size of the screen.
4. Once you have the current map scale, you can calculate the zoom level (code taken from my project Maperitive):

``````public static float MapScaleToOsmZoomLevel(float mapScale, float latitude, float ppi)
{
const float MetersPerInch = 2.54f / 100;

const double EarthCircumference = EarthRadius * Math.PI * 2;
double realLengthInMeters = EarthCircumference * Math.Cos (Deg2Rad (latitude));

double zoomLevelExp = (realLengthInMeters*ppi) / (256*MetersPerInch*mapScale);

return (float) Math.Log(zoomLevelExp, 2);
}
``````

Now you have a zoom level as a real value and you can calculate the actual zoom level based on a "quality" setting - for example if you want a higher quality, you can use something like `Math.Ceiling(zoom)`.

• Nice ideas. Where I'm stuck is the apparent inconsistency in conceptual approaches: In a "view from space" projection of the earth, the distances vary across the map and so does the scale. So we're mixing up pieces of a Web Mercator projection with pieces of an orthographic projection. It's not at all clear they are even comparable. Aug 3, 2011 at 14:26
• The calculation is for the nearest point of the surface. And in the end, each tile gets reprojected somehow and you still end up with a (rough) PPI for each tile. 20 pixels per inch are ugly in any kind of projection. Aug 3, 2011 at 15:29

I know this is an old question but I was stuck too (I have to model a 3d globe that uses a TMS) and found the "altitude" of the camera partly thanks to Igor Brejc's reply and mostly thanks to this article:

there is a mistake in their formula btw ; the +8 in S=C*cos(y)/2^(z+8) should not be there.

in my case the following (JavaScript) function worked:

``````function altitude( latitude, zoomlevel )
{
/*
The distance represented by one pixel (S) is given by

S = C * cos( latitude )/ 2 ^ zoomlevel

where
latitude: is the latitude of where you're interested in the scale ( in radians ).
zoomlevel: is the zoom level ( typically a value between 0 & 21 )
and
C is the (equatorial) circumference of the Earth
EARTH_RADIUS = 6378137 ( earth radius in meters )
//*/

var C = Math.PI * 2 * EARTH_RADIUS;
return EARTH_RADIUS + ( C * Math.cos( latitude ) / Math.pow( 2, zoomlevel ) );

}
``````

my 3D scene's unit is the meter (I use EARTH_RADIUS as a reference) and the above method return the camera's position from the center of the Earth (hence the EARTH_RADIUS + C * cos( y )/ 2 ^ zoomlevel in the return value).

there is also a relation between the field of view, the visible area and the altitude of the camera which I haven't found yet. the smaller the FOV, the better though (I use a constant 36° which works).

note: the maximum visible degrees' span is related to the zoom level (0-360°, 1-180°, 2-90° a.s.o.) this can helpe determine the visible area. see the linked article for the complete list.