翻譯:Bing地圖瓦片體系

Bing Maps Tile System Bing地圖瓦片體系git

原文連接:http://msdn.microsoft.com/en-us/library/bb259689.aspx數據庫

Bing Maps provides a world map that users can directly manipulate to pan and zoom. To make this interaction as fast and responsive as possible, we chose to pre-render the map at many different levels of detail, and to cut each map into tiles for quick retrieval and display. This document describes the projection, coordinate systems, and addressing scheme of the map tiles, which collectively are called the Bing Maps Tile System.express

Bing地圖提供了用戶能夠直接交互的世界地圖。爲了使地圖的交互響應速度進可能的快,app

咱們選擇預渲染不一樣等級的地圖(Lod),而且爲了快速檢索和顯示將地圖切割到不一樣的瓦片less

這篇文檔描述了投影,座標系統和地圖瓦片的定位方法,這個地圖瓦片被稱做BMTS,Bing地圖瓦片體系。ide

Map Projection地圖投影性能

To make the map seamless, and to ensure that aerial images from different sources line up properly, we have to use a single projection for the entire world. We chose to use the Mercator projection, which looks like this:優化

爲了實現地圖的無縫,保證不一樣數據源的一系列影像正常的上線,咱們不得不給整個世界選擇一個統一的投影,這裏選擇墨卡託投影,形式以下:ui

 IC130641.jpg

Although the Mercator projection significantly distorts scale and area (particularly near the poles), it has two important properties that outweigh the scale distortion:this

儘管墨卡託有顯著的尺度形變和麪積形變(尤爲是近極地地區),它有兩個比長度形變動重要的屬性

  1. It’s a conformal projection, which means that it preserves the shape of relatively small objects. This is especially important when showing aerial imagery, because we want to avoid distorting the shape of buildings. Square buildings should appear square, not rectangular.

它是一個正形投影(等角投影),具備保持形狀。這在顯示航空影像時是尤爲重要的。由於咱們想避免建築物形狀的形變。方形的建築物應該看着是方形的,不是長方形的。

  1. It’s a cylindrical projection, which means that north and south are always straight up and down, and west and east are always straight left and right.

它是圓柱投影,北極和南極老是指向上下。東西老是指向左右。

Since the Mercator projection goes to infinity at the poles, it doesn’t actually show the entire world. Using a square aspect ratio for the map, the maximum latitude shown is approximately 85.05 degrees.

To simplify the calculations, we use the spherical form of this projection, not the ellipsoidal form. Since the projection is used only for map display, and not for displaying numeric coordinates, we don’t need the extra precision of an ellipsoidal projection. The spherical projection causes approximately 0.33% scale distortion in the Y direction, which is not visually noticeable.

由於墨卡託投影在兩極地區趨於無限,並不顯示整個世界。爲地圖設定一個square比例,最大的顯示緯度接近85.05度。爲了簡化計算,咱們採用球進行投影,不是橢球。因爲投影是爲了地圖的顯示,不是爲了展現座標數值,咱們沒有必要採用高精度的橢球投影。球形投影產生大約0.33%的y方向的長度變形,這不會明顯的能夠察覺。

Ground Resolution and Map Scale地面分辨率和地圖比例尺

In addition to the projection, the ground resolution or map scale must be specified in order to render a map. At the lowest level of detail (Level 1), the map is 512 x 512 pixels. At each successive level of detail, the map width and height grow by a factor of 2: Level 2 is 1024 x 1024 pixels, Level 3 is 2048 x 2048 pixels, Level 4 is 4096 x 4096 pixels, and so on. In general, the width and height of the map (in pixels) can be calculated as:

map width = map height = 256 * 2 level pixels

除了投影以外,爲了展現地圖,地面分辨率和地圖比例尺必須被定義。在最低一級Level1,地圖是512x512像素的。在每一個Lod系列,地圖的寬度和高度按照2倍比例增加:Level2是1024x1024像素,Level3是2048x2048像素,level4是4096x4096像素,依次…一般,寬度和高度的計算公式以下:

 

The ground resolution indicates the distance on the ground that’s represented by a single pixel in the map. For example, at a ground resolution of 10 meters/pixel, each pixel represents a ground distance of 10 meters. The ground resolution varies depending on the level of detail and the latitude at which it’s measured. Using an earth radius of 6378137 meters, the ground resolution (in meters per pixel) can be calculated as:

ground resolution = cos(latitude * pi/180) * earth circumference / map width

                         = (cos(latitude * pi/180) * 2 * pi * 6378137 meters) / (256 * 2 level pixels)

地面分辨率指示地圖上一個象素表明地面距離。例如,地面分辨率10m/pixel,每一個像素表明地面距離10m。地面分辨率變量依賴被量測的Lod和緯度。採用地球半徑6378137米,地面分辨率(m/pixel)按以下公式計算: 

The map scale indicates the ratio between map distance and ground distance, when measured in the same units. For instance, at a map scale of 1 : 100,000, each inch on the map represents a ground distance of 100,000 inches. Like the ground resolution, the map scale varies with the level of detail and the latitude of measurement. It can be calculated from the ground resolution as follows, given the screen resolution in dots per inch, typically 96 dpi:

map scale = 1 : ground resolution * screen dpi / 0.0254 meters/inch

              = 1 : (cos(latitude * pi/180) * 2 * pi * 6378137 * screen dpi) / (256 * 2 level * 0.0254)

地圖比例尺是指圖上距離和實際距離的比值,以一樣的測量單位。

例如,地圖比例尺1 : 100,000,每英寸的地圖距離表明地面100,000英寸的距離。和地面分辨率同樣,地圖比例尺隨Lod和緯度變化。能夠根據地面分辨率計算,方法以下:已知屏幕分辨率每英寸像素點數,通常爲96dpi:

 This table shows each of these values at each level of detail, as measured at the Equator. (Note that the ground resolution and map scale also vary with the latitude, as shown in the equations above, but not shown in the table below.)

下表顯示了不一樣Lod在地球赤道測量的地面分辨率和比例尺的值。(提醒:地面分辨率和地圖比例尺還隨緯度變化,如上面的方程那樣,但沒有在下面的表格中顯示。)

Level of Detail

Map Width and Height (pixels)

Ground Resolution (meters / pixel)

Map Scale
(at 96 dpi)

1

512

78,271.5170

1 : 295,829,355.45

2

1,024

39,135.7585

1 : 147,914,677.73

3

2,048

19,567.8792

1 : 73,957,338.86

4

4,096

9,783.9396

1 : 36,978,669.43

5

8,192

4,891.9698

1 : 18,489,334.72

6

16,384

2,445.9849

1 : 9,244,667.36

7

32,768

1,222.9925

1 : 4,622,333.68

8

65,536

611.4962

1 : 2,311,166.84

9

131,072

305.7481

1 : 1,155,583.42

10

262,144

152.8741

1 : 577,791.71

11

524,288

76.4370

1 : 288,895.85

12

1,048,576

38.2185

1 : 144,447.93

13

2,097,152

19.1093

1 : 72,223.96

14

4,194,304

9.5546

1 : 36,111.98

15

8,388,608

4.7773

1 : 18,055.99

16

16,777,216

2.3887

1 : 9,028.00

17

33,554,432

1.1943

1 : 4,514.00

18

67,108,864

0.5972

1 : 2,257.00

19

134,217,728

0.2986

1 : 1,128.50

20

268,435,456

0.1493

1 : 564.25

21

536,870,912

0.0746

1 : 282.12

22

1,073,741,824

0.0373

1 : 141.06

23

2,147,483,648

0.0187

1 : 70.53

Pixel Coordinates像素座標

Having chosen the projection and scale to use at each level of detail, we can convert geographic coordinates into pixel coordinates. Since the map width and height is different at each level, so are the pixel coordinates. The pixel at the upper-left corner of the map always has pixel coordinates (0, 0). The pixel at the lower-right corner of the map has pixel coordinates (width-1, height-1), or referring to the equations in the previous section, (256 * 2level–1, 256 * 2level–1). For example, at level 3, the pixel coordinates range from (0, 0) to (2047, 2047), like this:

選擇了投影和不一樣Lod的比例尺,咱們能夠將地理座標轉換到像素座標。因爲每級地圖的寬度和高度不一樣,一樣的像素座標也不相同。左上角的像素座標老是(0,0)。右下角的地圖象素座標爲(width-1,heght-1),或者參照上一節的方程, 。例如,在Level3,象素座標的範圍是(0.0)到(2047,2047),以下:

 IC19035.jpg

Given latitude and longitude in degrees, and the level of detail, the pixel XY coordinates can be calculated as follows:

sinLatitude = sin(latitude * pi/180)

pixelX = ((longitude + 180) / 360) * 256 * 2 level

pixelY = (0.5 – log((1 + sinLatitude) / (1 – sinLatitude)) / (4 * pi)) * 256 * 2 level

給定經緯度和Lod等級,像素座標能夠按以下方式計算:

The latitude and longitude are assumed to be on the WGS 84 datum. Even though Bing Maps uses a spherical projection, it’s important to convert all geographic coordinates into a common datum, and WGS 84 was chosen to be that datum. The longitude is assumed to range from -180 to +180 degrees, and the latitude must be clipped to range from -85.05112878 to 85.05112878. This avoids a singularity at the poles, and it causes the projected map to be square.

經緯度假定在WGS84基準面。儘管Bing地圖採用墨卡託投影,將地理座標轉換到一般的基準面是十分重要的,WGS84被做爲基準面。經度範圍-180到180,緯度範圍必須被裁減在範圍(-85.05112878 , 85.05112878)。這樣能夠避免極地的異常,它使投影的地圖爲方形的。

Tile Coordinates and Quadkeys瓦片座標和四叉樹鍵

To optimize the performance of map retrieval and display, the rendered map is cut into tiles of 256 x 256 pixels each. As the number of pixels differs at each level of detail, so does the number of tiles:

map width = map height = 2 level tiles

爲了優化地圖的檢索和顯示,渲染地圖被切割成256x256像素。不一樣Lod等級的象素數目不一樣,一樣瓦片數目也不相同:

Each tile is given XY coordinates ranging from (0, 0) in the upper left to (2level–1, 2level–1) in the lower right. For example, at level 3 the tile coordinates range from (0, 0) to (7, 7) as follows:

每一個瓦片給定xy座標範圍左上角(0, 0),右下角(2level–1, 2level–1)。例如Level3的瓦片座標範圍(0, 0)到(7, 7)

 http://i.msdn.microsoft.com/dynimg/IC5902.jpg

Given a pair of pixel XY coordinates, you can easily determine the tile XY coordinates of the tile containing that pixel:

tileX = floor(pixelX / 256)

tileY = floor(pixelY / 256)

給定一組像素座標XY,能夠容易的計算包含該像素的瓦片的xy座標。

To optimize the indexing and storage of tiles, the two-dimensional tile XY coordinates are combined into one-dimensional strings called quadtree keys, or 「quadkeys」 for short. Each quadkey uniquely identifies a single tile at a particular level of detail, and it can be used as an key in common database B-tree indexes. To convert tile coordinates into a quadkey, the bits of the Y and X coordinates are interleaved, and the result is interpreted as a base-4 number (with leading zeros maintained) and converted into a string. For instance, given tile XY coordinates of (3, 5) at level 3, the quadkey is determined as follows:

tileX = 3 = 011 2

tileY = 5 = 101 2

quadkey = 100111 2 = 213 4 = 「213」

爲了優化瓦片存儲的索引,二維的瓦片座標被包含在一維的字符串中,稱爲「四叉樹鍵」,或者簡稱「四叉鍵」。每一個四叉鍵惟一的定義了在特定Lod級別的一個瓦片,能夠被用來做爲通用數據庫的b-tree索引。爲了將瓦片座標轉換到四叉鍵,y和x座標的位是隔行讀取的,結果造成了4進制數,轉換成字符串。例如,給定xy座標(3, 5) 等級level 3,四叉鍵爲下值:

Quadkeys have several interesting properties. First, the length of a quadkey (the number of digits) equals the level of detail of the corresponding tile. Second, the quadkey of any tile starts with the quadkey of its parent tile (the containing tile at the previous level). As shown in the example below, tile 2 is the parent of tiles 20 through 23, and tile 13 is the parent of tiles 130 through 133:

四叉鍵有一些有趣的屬性。

首先,四叉鍵的長度等於Lod對應的瓦片等級。

第二,任何瓦片的四叉鍵開始於其父親瓦片的四叉鍵。以下所示,瓦片2是瓦片20到23的父節點,瓦片13是瓦片130到133的父節點。

 http://i.msdn.microsoft.com/dynimg/IC96238.jpg

Finally, quadkeys provide a one-dimensional index key that usually preserves the proximity of tiles in XY space. In other words, two tiles that have nearby XY coordinates usually have quadkeys that are relatively close together. This is important for optimizing database performance, because neighboring tiles are usually requested in groups, and it’s desirable to keep those tiles on the same disk blocks, in order to minimize the number of disk reads.

最後,四叉鍵提供了一維的索引鍵,一般保存了鄰近瓦片的XY空間。換言之,兩個瓦片具備相鄰的xy座標一般具備相鄰的四叉鍵。這對於優化數據庫性能十分重要,由於相鄰的瓦片一般會做爲組進行請求,這樣的描述能夠保證這些瓦片在相同的硬盤塊,減小磁盤的讀取次數。

Sample Code

The following sample C# code illustrates how to implement the functions described in this document. These functions can be easily translated into other programming languages as needed.

以下的C# 代碼闡述瞭如何實現本文的方法,這些方法能夠很容易的轉換到其餘語言。

 1 //------------------------------------------------------------------------------  2 
 3 // <copyright company="Microsoft">  4 
 5 // Copyright (c) 2006-2009 Microsoft Corporation. All rights reserved.  6 
 7 // </copyright>  8 
 9 //------------------------------------------------------------------------------
 10 
 11  
 12 
 13 using System;  14 
 15 using System.Text;  16 
 17  
 18 
 19 namespace Microsoft.MapPoint  20 
 21 {  22 
 23     static class TileSystem  24 
 25  {  26 
 27         private const double EarthRadius = 6378137;  28 
 29         private const double MinLatitude = -85.05112878;  30 
 31         private const double MaxLatitude = 85.05112878;  32 
 33         private const double MinLongitude = -180;  34 
 35         private const double MaxLongitude = 180;  36 
 37  
 38 
 39  
 40 
 41         /// <summary>
 42 
 43         /// Clips a number to the specified minimum and maximum values.  44 
 45         /// </summary>
 46 
 47         /// <param name="n">The number to clip.</param>
 48 
 49         /// <param name="minValue">Minimum allowable value.</param>
 50 
 51         /// <param name="maxValue">Maximum allowable value.</param>
 52 
 53         /// <returns>The clipped value.</returns>
 54 
 55         private static double Clip(double n, double minValue, double maxValue)  56 
 57  {  58 
 59             return Math.Min(Math.Max(n, minValue), maxValue);  60 
 61  }  62 
 63         
 64 
 65         
 66 
 67  
 68 
 69         /// <summary>
 70 
 71         /// Determines the map width and height (in pixels) at a specified level  72 
 73         /// of detail.  74 
 75         /// </summary>
 76 
 77         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail)  78 
 79         /// to 23 (highest detail).</param>
 80 
 81         /// <returns>The map width and height in pixels.</returns>
 82 
 83         public static uint MapSize(int levelOfDetail)  84 
 85  {  86 
 87             return (uint) 256 << levelOfDetail;  88 
 89  }  90 
 91  
 92 
 93  
 94 
 95  
 96 
 97         /// <summary>
 98 
 99         /// Determines the ground resolution (in meters per pixel) at a specified 100 
101         /// latitude and level of detail. 102 
103         /// </summary>
104 
105         /// <param name="latitude">Latitude (in degrees) at which to measure the 106 
107         /// ground resolution.</param>
108 
109         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) 110 
111         /// to 23 (highest detail).</param>
112 
113         /// <returns>The ground resolution, in meters per pixel.</returns>
114 
115         public static double GroundResolution(double latitude, int levelOfDetail) 116 
117  { 118 
119             latitude = Clip(latitude, MinLatitude, MaxLatitude); 120 
121             return Math.Cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapSize(levelOfDetail); 122 
123  } 124 
125  
126 
127  
128 
129  
130 
131         /// <summary>
132 
133         /// Determines the map scale at a specified latitude, level of detail, 134 
135         /// and screen resolution. 136 
137         /// </summary>
138 
139         /// <param name="latitude">Latitude (in degrees) at which to measure the 140 
141         /// map scale.</param>
142 
143         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) 144 
145         /// to 23 (highest detail).</param>
146 
147         /// <param name="screenDpi">Resolution of the screen, in dots per inch.</param>
148 
149         /// <returns>The map scale, expressed as the denominator N of the ratio 1 : N.</returns>
150 
151         public static double MapScale(double latitude, int levelOfDetail, int screenDpi) 152 
153  { 154 
155             return GroundResolution(latitude, levelOfDetail) * screenDpi / 0.0254; 156 
157  } 158 
159  
160 
161  
162 
163  
164 
165         /// <summary>
166 
167         /// Converts a point from latitude/longitude WGS-84 coordinates (in degrees) 168 
169         /// into pixel XY coordinates at a specified level of detail. 170 
171         /// </summary>
172 
173         /// <param name="latitude">Latitude of the point, in degrees.</param>
174 
175         /// <param name="longitude">Longitude of the point, in degrees.</param>
176 
177         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) 178 
179         /// to 23 (highest detail).</param>
180 
181         /// <param name="pixelX">Output parameter receiving the X coordinate in pixels.</param>
182 
183         /// <param name="pixelY">Output parameter receiving the Y coordinate in pixels.</param>
184 
185         public static void LatLongToPixelXY(double latitude, double longitude, int levelOfDetail, out int pixelX, out int pixelY) 186 
187  { 188 
189             latitude = Clip(latitude, MinLatitude, MaxLatitude); 190 
191             longitude = Clip(longitude, MinLongitude, MaxLongitude); 192 
193  
194 
195             double x = (longitude + 180) / 360; 196 
197             double sinLatitude = Math.Sin(latitude * Math.PI / 180); 198 
199             double y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI); 200 
201  
202 
203             uint mapSize = MapSize(levelOfDetail); 204 
205             pixelX = (int) Clip(x * mapSize + 0.5, 0, mapSize - 1); 206 
207             pixelY = (int) Clip(y * mapSize + 0.5, 0, mapSize - 1); 208 
209  } 210 
211  
212 
213  
214 
215  
216 
217         /// <summary>
218 
219         /// Converts a pixel from pixel XY coordinates at a specified level of detail 220 
221         /// into latitude/longitude WGS-84 coordinates (in degrees). 222 
223         /// </summary>
224 
225         /// <param name="pixelX">X coordinate of the point, in pixels.</param>
226 
227         /// <param name="pixelY">Y coordinates of the point, in pixels.</param>
228 
229         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) 230 
231         /// to 23 (highest detail).</param>
232 
233         /// <param name="latitude">Output parameter receiving the latitude in degrees.</param>
234 
235         /// <param name="longitude">Output parameter receiving the longitude in degrees.</param>
236 
237         public static void PixelXYToLatLong(int pixelX, int pixelY, int levelOfDetail, out double latitude, out double longitude) 238 
239  { 240 
241             double mapSize = MapSize(levelOfDetail); 242 
243             double x = (Clip(pixelX, 0, mapSize - 1) / mapSize) - 0.5; 244 
245             double y = 0.5 - (Clip(pixelY, 0, mapSize - 1) / mapSize); 246 
247  
248 
249             latitude = 90 - 360 * Math.Atan(Math.Exp(-y * 2 * Math.PI)) / Math.PI; 250 
251             longitude = 360 * x; 252 
253  } 254 
255  
256 
257  
258 
259  
260 
261         /// <summary>
262 
263         /// Converts pixel XY coordinates into tile XY coordinates of the tile containing 264 
265         /// the specified pixel. 266 
267         /// </summary>
268 
269         /// <param name="pixelX">Pixel X coordinate.</param>
270 
271         /// <param name="pixelY">Pixel Y coordinate.</param>
272 
273         /// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
274 
275         /// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
276 
277         public static void PixelXYToTileXY(int pixelX, int pixelY, out int tileX, out int tileY) 278 
279  { 280 
281             tileX = pixelX / 256; 282 
283             tileY = pixelY / 256; 284 
285  } 286 
287  
288 
289  
290 
291  
292 
293         /// <summary>
294 
295         /// Converts tile XY coordinates into pixel XY coordinates of the upper-left pixel 296 
297         /// of the specified tile. 298 
299         /// </summary>
300 
301         /// <param name="tileX">Tile X coordinate.</param>
302 
303         /// <param name="tileY">Tile Y coordinate.</param>
304 
305         /// <param name="pixelX">Output parameter receiving the pixel X coordinate.</param>
306 
307         /// <param name="pixelY">Output parameter receiving the pixel Y coordinate.</param>
308 
309         public static void TileXYToPixelXY(int tileX, int tileY, out int pixelX, out int pixelY) 310 
311  { 312 
313             pixelX = tileX * 256; 314 
315             pixelY = tileY * 256; 316 
317  } 318 
319  
320 
321  
322 
323  
324 
325         /// <summary>
326 
327         /// Converts tile XY coordinates into a QuadKey at a specified level of detail. 328 
329         /// </summary>
330 
331         /// <param name="tileX">Tile X coordinate.</param>
332 
333         /// <param name="tileY">Tile Y coordinate.</param>
334 
335         /// <param name="levelOfDetail">Level of detail, from 1 (lowest detail) 336 
337         /// to 23 (highest detail).</param>
338 
339         /// <returns>A string containing the QuadKey.</returns>
340 
341         public static string TileXYToQuadKey(int tileX, int tileY, int levelOfDetail) 342 
343  { 344 
345             StringBuilder quadKey = new StringBuilder(); 346 
347             for (int i = levelOfDetail; i > 0; i--) 348 
349  { 350 
351                 char digit = '0'; 352 
353                 int mask = 1 << (i - 1); 354 
355                 if ((tileX & mask) != 0) 356 
357  { 358 
359                     digit++; 360 
361  } 362 
363                 if ((tileY & mask) != 0) 364 
365  { 366 
367                     digit++; 368 
369                     digit++; 370 
371  } 372 
373  quadKey.Append(digit); 374 
375  } 376 
377             return quadKey.ToString(); 378 
379  } 380 
381  
382 
383  
384 
385  
386 
387         /// <summary>
388 
389         /// Converts a QuadKey into tile XY coordinates. 390 
391         /// </summary>
392 
393         /// <param name="quadKey">QuadKey of the tile.</param>
394 
395         /// <param name="tileX">Output parameter receiving the tile X coordinate.</param>
396 
397         /// <param name="tileY">Output parameter receiving the tile Y coordinate.</param>
398 
399         /// <param name="levelOfDetail">Output parameter receiving the level of detail.</param>
400 
401         public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out int levelOfDetail) 402 
403  { 404 
405             tileX = tileY = 0; 406 
407             levelOfDetail = quadKey.Length; 408 
409             for (int i = levelOfDetail; i > 0; i--) 410 
411  { 412 
413                 int mask = 1 << (i - 1); 414 
415                 switch (quadKey[levelOfDetail - i]) 416 
417  { 418 
419                     case '0': 420 
421                         break; 422 
423  
424 
425                     case '1': 426 
427                         tileX |= mask; 428 
429                         break; 430 
431  
432 
433                     case '2': 434 
435                         tileY |= mask; 436 
437                         break; 438 
439  
440 
441                     case '3': 442 
443                         tileX |= mask; 444 
445                         tileY |= mask; 446 
447                         break; 448 
449  
450 
451                     default: 452 
453                         throw new ArgumentException("Invalid QuadKey digit sequence."); 454 
455  } 456 
457  } 458 
459  } 460 
461  } 462 
463 }
View Code
相關文章
相關標籤/搜索