您的位置:首页 > 其它

GIS理论(墨卡托投影、地理坐标系、地面分辨率、地图比例尺、Bing Maps Tile System)

2013-07-24 22:16 483 查看
墨卡托投影(MercatorProjection),又名“等角正轴圆柱投影”,荷兰地图学家墨卡托(Mercator)在1569年拟定,假设地球被围在一个中空的圆柱里,其赤道与圆柱相接触,然后再假想地球中心有一盏灯,把球面上的图形投影到圆柱体上,再把圆柱体展开,这就是一幅标准纬线为零度(即赤道)的“墨卡托投影”绘制出的世界地图。

        


一、墨卡托投影坐标系(MercatorProjection)

  墨卡托投影以整个世界范围,赤道作为标准纬线,本初子午线作为中央经线,两者交点为坐标原点,向东向北为正,向西向南为负。南北极在地图的正下、上方,而东西方向处于地图的正右、左。

  由于MercatorProjection在两极附近是趋于无限值得,因此它并没完整展现了整个世界,地图上最高纬度是85.05度。为了简化计算,我们采用球形映射,而不是椭球体形状。虽然采用MercatorProjection只是为了方便展示地图,需要知道的是,这种映射会给Y轴方向带来0.33%的误差。

        


  由于赤道半径为6378137米,则赤道周长为2*PI*r=20037508.3427892,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]。当纬度φ接近两极,即90°时,Y值趋向于无穷。因此通常把Y轴的取值范围也限定在[-20037508.3427892,20037508.3427892]之间。因此在墨卡托投影坐标系(米)下的坐标范围是:最小为(-20037508.3427892,-20037508.3427892)到最大坐标为(20037508.3427892,20037508.3427892)。

二、地理坐标系(Geographicalcoordinates)

  地理经度的取值范围是[-180,180],纬度不可能到达90°,通过纬度取值范围为[20037508.3427892,20037508.3427892]反计算可得到纬度值为85.05112877980659。因此纬度取值范围是[-85.05112877980659,85.05112877980659]。因此,地理坐标系(经纬度)对应的范围是:最小地理坐标(-180,-85.05112877980659),最大地理坐标(180,85.05112877980659)。

三、地面分辨率(GroundResolution)
  地面分辨率是以一个像素(pixel)代表的地面尺寸(米)。以微软BingMaps为例,当Level为1时,图片大小为512*512(4个Tile),那么赤道空间分辨率为:赤道周长/512。其他纬度的空间分辨率则为纬度圈长度/512,极端的北极则为0。Level为2时,赤道的空间分辨率为赤道周长/1024,其他纬度为纬度圈长度1024。很明显,GroundResolution取决于两个参数,缩放级别Level和纬度latitude,Level决定像素的多少,latitude决定地面距离的长短。

  地面分辨率的公式为,单位:米/像素:

  groundresolution=(cos(latitude*pi/180)*2*pi*6378137meters)/(256*2levelpixels)  

  最低地图放大级别(1级),地图是512x512像素。每下一个放大级别,地图的高度和宽度分别乘于2:2级是1024x1024像素,3级是2048x2048像素,4级是4096x4096像素,等等。通常而言,地图的宽度和高度可以由以下式子计算得到:mapwidth=mapheight=256*2^level
pixels




四、地图比例尺(MapScale)

  地图比例尺是指测量相同目标时,地图上距离与实际距离的比例。通过地图分辨率在计算可知由Level可得到图片的像素大小,那么需要把其转换为以米为单位的距离,涉及到DPI(dotperinch),暂时可理解为类似的PPI(pixelperinch),即每英寸代表多少个像素。256*2level/DPI即得到相应的英寸inch,再把英寸inch除以0.0254转换为米。实地距离仍旧是:cos(latitude*pi/180)*2*pi*6378137meters;因此比例尺的公式为:

  mapscale=256*2level/screendpi/0.0254/(cos(latitude*pi/180)*2*pi*6378137)

  比例尺=1:(cos(latitude*pi/180)*2*pi*6378137*screendpi)/(256*2level*0.0254)

  地面分辨率和地图比例尺之间的关系:

  mapscale=1:groundresolution*screendpi/0.0254meters/inch

缩放级别

地图宽度、高度(像素)

地面分辨率(米/像素)

地图比例尺(以96dpi为例)

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

五、BingMaps像素坐标系和地图图片编码

  为了优化地图系统性能,提高地图下载和显示速度,所有地图都被分割成256x256像素大小的正方形小块。由于在每个放大级别下的像素数量都不一样,因此地图图片(Tile)的数量也不一样。每个tile都有一个XY坐标值,从左上角的(0,0)至右下角的(2^level–1,2^level–1)。例如在3级放大级别下,所有tile的坐标值范围为(0,0)至(7,7),如下图:

        


  已知一个像素的XY坐标值时,我们很容易得到这个像素所在的Tile的XY坐标值:

    tileX=floor(pixelX/256)  
tileY=floor(pixelY/256)




  为了简化索引和存储地图图片,每个tile的二维XY值被转换成一维字串,即四叉树键值(quardtreekey,简称quadkey)。每个quadkey独立对应某个放大级别下的一个tile,并且它可以被用作数据库中B-tree索引值。为了将坐标值转换成quadkey,需要将Y和X坐标二进制值交错组合,并转换成4进制值及对应的字符串。例如,假设在放大级别为3时,tile的XY坐标值为(3,5),quadkey计算如下:


  tileX=3=011(二进制)


  tileY=5=101(二进制)


  quadkey=100111(二进制)
=213(四进制)
=“213”


  Quadkey还有其他一些有意思的特性。第一,quadkey的长度等于该tile所对应的放大级别;第二,每个tile的quadkey的前几位和其父tile(上一放大级别所对应的tile)的quadkey相同,下图中,tile2是tile20至23的父tile,tile13是tile130至133的父级:

    

  

  最后,quadkey提供的一维索引值通常显示了两个tile在XY坐标系中的相似性。换句话说,两个相邻的tile对应的quadkey非常接近。这对于优化数据库的性能非常重要,因为相邻的tile通常被同时请求显示,因此可以将这些tile存放在相同的磁盘区域中,以减少磁盘的读取次数。

  下面是微软BingMaps的TileSystem相关算法:

usingSystem;
usingSystem.Text;

namespaceMicrosoft.MapPoint
{
staticclassTileSystem
{
privateconstdoubleEarthRadius=6378137;
privateconstdoubleMinLatitude=-85.05112878;
privateconstdoubleMaxLatitude=85.05112878;
privateconstdoubleMinLongitude=-180;
privateconstdoubleMaxLongitude=180;

///<summary>
///Clipsanumbertothespecifiedminimumandmaximumvalues.
///</summary>
///<paramname="n">Thenumbertoclip.</param>
///<paramname="minValue">Minimumallowablevalue.</param>
///<paramname="maxValue">Maximumallowablevalue.</param>
///<returns>Theclippedvalue.</returns>
privatestaticdoubleClip(doublen,doubleminValue,doublemaxValue)
{
returnMath.Min(Math.Max(n,minValue),maxValue);
}

///<summary>
///Determinesthemapwidthandheight(inpixels)ataspecifiedlevel
///ofdetail.
///</summary>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<returns>Themapwidthandheightinpixels.</returns>
publicstaticuintMapSize(intlevelOfDetail)
{
return(uint)256<<levelOfDetail;
}

///<summary>
///Determinesthegroundresolution(inmetersperpixel)ataspecified
///latitudeandlevelofdetail.
///</summary>
///<paramname="latitude">Latitude(indegrees)atwhichtomeasurethe
///groundresolution.</param>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<returns>Thegroundresolution,inmetersperpixel.</returns>
publicstaticdoubleGroundResolution(doublelatitude,intlevelOfDetail)
{
latitude=Clip(latitude,MinLatitude,MaxLatitude);
returnMath.Cos(latitude*Math.PI/180)*2*Math.PI*EarthRadius/MapSize(levelOfDetail);
}

///<summary>
///Determinesthemapscaleataspecifiedlatitude,levelofdetail,
///andscreenresolution.
///</summary>
///<paramname="latitude">Latitude(indegrees)atwhichtomeasurethe
///mapscale.</param>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<paramname="screenDpi">Resolutionofthescreen,indotsperinch.</param>
///<returns>Themapscale,expressedasthedenominatorNoftheratio1:N.</returns>
publicstaticdoubleMapScale(doublelatitude,intlevelOfDetail,intscreenDpi)
{
returnGroundResolution(latitude,levelOfDetail)*screenDpi/0.0254;
}

///<summary>
///Convertsapointfromlatitude/longitudeWGS-84coordinates(indegrees)
///intopixelXYcoordinatesataspecifiedlevelofdetail.
///</summary>
///<paramname="latitude">Latitudeofthepoint,indegrees.</param>
///<paramname="longitude">Longitudeofthepoint,indegrees.</param>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<paramname="pixelX">OutputparameterreceivingtheXcoordinateinpixels.</param>
///<paramname="pixelY">OutputparameterreceivingtheYcoordinateinpixels.</param>
publicstaticvoidLatLongToPixelXY(doublelatitude,doublelongitude,intlevelOfDetail,outintpixelX,outintpixelY)
{
latitude=Clip(latitude,MinLatitude,MaxLatitude);
longitude=Clip(longitude,MinLongitude,MaxLongitude);

doublex=(longitude+180)/360;
doublesinLatitude=Math.Sin(latitude*Math.PI/180);
doubley=0.5-Math.Log((1+sinLatitude)/(1-sinLatitude))/(4*Math.PI);

uintmapSize=MapSize(levelOfDetail);
pixelX=(int)Clip(x*mapSize+0.5,0,mapSize-1);
pixelY=(int)Clip(y*mapSize+0.5,0,mapSize-1);
}

///<summary>
///ConvertsapixelfrompixelXYcoordinatesataspecifiedlevelofdetail
///intolatitude/longitudeWGS-84coordinates(indegrees).
///</summary>
///<paramname="pixelX">Xcoordinateofthepoint,inpixels.</param>
///<paramname="pixelY">Ycoordinatesofthepoint,inpixels.</param>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<paramname="latitude">Outputparameterreceivingthelatitudeindegrees.</param>
///<paramname="longitude">Outputparameterreceivingthelongitudeindegrees.</param>
publicstaticvoidPixelXYToLatLong(intpixelX,intpixelY,intlevelOfDetail,outdoublelatitude,outdoublelongitude)
{
doublemapSize=MapSize(levelOfDetail);
doublex=(Clip(pixelX,0,mapSize-1)/mapSize)-0.5;
doubley=0.5-(Clip(pixelY,0,mapSize-1)/mapSize);

latitude=90-360*Math.Atan(Math.Exp(-y*2*Math.PI))/Math.PI;
longitude=360*x;
}

///<summary>
///ConvertspixelXYcoordinatesintotileXYcoordinatesofthetilecontaining
///thespecifiedpixel.
///</summary>
///<paramname="pixelX">PixelXcoordinate.</param>
///<paramname="pixelY">PixelYcoordinate.</param>
///<paramname="tileX">OutputparameterreceivingthetileXcoordinate.</param>
///<paramname="tileY">OutputparameterreceivingthetileYcoordinate.</param>
publicstaticvoidPixelXYToTileXY(intpixelX,intpixelY,outinttileX,outinttileY)
{
tileX=pixelX/256;
tileY=pixelY/256;
}

///<summary>
///ConvertstileXYcoordinatesintopixelXYcoordinatesoftheupper-leftpixel
///ofthespecifiedtile.
///</summary>
///<paramname="tileX">TileXcoordinate.</param>
///<paramname="tileY">TileYcoordinate.</param>
///<paramname="pixelX">OutputparameterreceivingthepixelXcoordinate.</param>
///<paramname="pixelY">OutputparameterreceivingthepixelYcoordinate.</param>
publicstaticvoidTileXYToPixelXY(inttileX,inttileY,outintpixelX,outintpixelY)
{
pixelX=tileX*256;
pixelY=tileY*256;
}

///<summary>
///ConvertstileXYcoordinatesintoaQuadKeyataspecifiedlevelofdetail.
///</summary>
///<paramname="tileX">TileXcoordinate.</param>
///<paramname="tileY">TileYcoordinate.</param>
///<paramname="levelOfDetail">Levelofdetail,from1(lowestdetail)
///to23(highestdetail).</param>
///<returns>AstringcontainingtheQuadKey.</returns>
publicstaticstringTileXYToQuadKey(inttileX,inttileY,intlevelOfDetail)
{
StringBuilderquadKey=newStringBuilder();
for(inti=levelOfDetail;i>0;i--)
{
chardigit='0';
intmask=1<<(i-1);
if((tileX&mask)!=0)
{
digit++;
}
if((tileY&mask)!=0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
returnquadKey.ToString();
}

///<summary>
///ConvertsaQuadKeyintotileXYcoordinates.
///</summary>
///<paramname="quadKey">QuadKeyofthetile.</param>
///<paramname="tileX">OutputparameterreceivingthetileXcoordinate.</param>
///<paramname="tileY">OutputparameterreceivingthetileYcoordinate.</param>
///<paramname="levelOfDetail">Outputparameterreceivingthelevelofdetail.</param>
publicstaticvoidQuadKeyToTileXY(stringquadKey,outinttileX,outinttileY,outintlevelOfDetail)
{
tileX=tileY=0;
levelOfDetail=quadKey.Length;
for(inti=levelOfDetail;i>0;i--)
{
intmask=1<<(i-1);
switch(quadKey[levelOfDetail-i])
{
case'0':
break;

case'1':
tileX|=mask;
break;

case'2':
tileY|=mask;
break;

case'3':
tileX|=mask;
tileY|=mask;
break;

default:
thrownewArgumentException("InvalidQuadKeydigitsequence.");
}
}
}
}
}

  注:本文中内容来源于互联网整理而成,如涉及到任何侵权等行为请联系本人。

  推荐资源:

  《BingMapsTileSystem》

转载自/article/4658542.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐