墨卡托投影、地理坐标系、地面分辨率、地图比例尺

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

墨卡托投影、地理坐标系、地⾯分辨率、地图⽐例尺
GIS理论(墨卡托投影、地理坐标系、地⾯分辨率、地图⽐例尺、Bing Maps Tile System)
墨卡托投影(Mercator Projection),⼜名“等⾓正轴圆柱投影”,荷兰地图学家墨卡托(Mercator)在1569年拟定,假设地球被围在⼀个中空的圆柱⾥,其⾚道与圆柱相接触,然后再假想地
球中⼼有⼀盏灯,把球⾯上的图形投影到圆柱体上,再把圆柱体展开,这就是⼀幅标准纬线为零度(即⾚道)的“墨卡托投影”绘制出的世界地图。

⼀、墨卡托投影坐标系(Mercator Projection)
墨卡托投影以整个世界范围,⾚道作为标准纬线,本初⼦午线作为中央经线,两者交点为坐标原点,向东向北为正,向西向南为负。

南北极在地图的正下、上⽅,⽽东西⽅向处于地图的正右、左。

由于Mercator Projection在两极附近是趋于⽆限值得,因此它并没完整展现了整个世界,地图上最⾼纬度是85.05度。

为了简化计算,我们采⽤球形映射,⽽不是椭球体形状。

虽然采⽤Mercator Projection只是为了⽅便展⽰地图,需要知道的是,这种映射会给Y轴⽅向带来0.33%的误差。

------------------------------------------------------------------------------------------------------------------------------------------
earthRadius =6378137
20037508.3427892 = earthRadius * (math.pi - 0)
85.05112877980659 = (math.atan(math.exp(aa / earthRadius))-math.pi/4)*2 * 180 / math.pi
image = 512 * 512
groundResolution(1 level) = (20037508.3427892 * 2) / 512 = 78271.516964
screendpi = 96
mapScale = groundResolution * 96 / 0.0254 = 295829355.455
---------------------------------------------------------------------------------------------------------------------------------------
由于⾚道半径为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)。

⼆、地理坐标系(Geographical coordinates)
地理经度的取值范围是[-180,180],纬度不可能到达90°,通过纬度取值范围为[20037508.3427892,20037508.3427892]反计算可得到纬度值为85.05112877980659。

因此纬度取值范围是[-85.05112877980659,85.05112877980659]。

因此,地理坐标系(经纬度)对应的范围是:最⼩地理坐标(-180,-85.05112877980659),最⼤地理坐标(180, 85.05112877980659)。

三、地⾯分辨率(Ground Resolution)
地⾯分辨率是以⼀个像素(pixel)代表的地⾯尺⼨(⽶)。

以微软Bing Maps为例,当Level为1时,图⽚⼤⼩为512*512(4个Tile),那么⾚道空间分辨率为:⾚道周长/512。

其他纬度的空间分辨率则为纬度圈长度/512,极端的北极则为0。

Level为2时,⾚道的空间分辨率为⾚道周长/1024,其他纬度为纬度圈长度1024。

很明显,Ground Resolution取决于两个参数,缩放级别Level和纬度latitude ,Level决定像素的多少,latitude决定地⾯距离的长短。

地⾯分辨率的公式为,单位:⽶/像素:
ground resolution = (cos(latitude * pi/180) * 2 * pi * 6378137 meters) / (256 * 2level pixels)
最低地图放⼤级别(1级),地图是512 x 512像素。

每下⼀个放⼤级别,地图的⾼度和宽度分别乘于2:2级是1024 x 1024像素,3级是2048 x 2048像素,4级是4096 x 4096像素,等等。

通常⽽⾔,地图的宽度和⾼度可以由以下式⼦计算得到:map width = map height = 256 * 2^level pixels
四、地图⽐例尺(Map Scale)
地图⽐例尺是指测量相同⽬标时,地图上距离与实际距离的⽐例。

通过地图分辨率在计算可知由Level可得到图⽚的像素⼤⼩,那么需要把其转换为以⽶为单位的距离,涉及到DPI(dot per inch),暂时可理解为类似的PPI(pixel per inch),即每英⼨代表多少个像素。

256 * 2level / DPI 即得到相应的英⼨inch,再把英⼨inch除以0.0254转换为⽶。

实地距离仍旧是:cos(latitude * pi/180) * 2 * pi * 6378137 meters; 因此⽐例尺的公式为:
map scale = 256 * 2level / screen dpi / 0.0254 / (cos(latitude * pi/180) * 2 * pi * 6378137)
⽐例尺= 1 : (cos(latitude * pi/180) * 2 * pi * 6378137 * screen dpi) / (256 * 2level * 0.0254)
地⾯分辨率和地图⽐例尺之间的关系:
map scale = 1 : ground resolution * screen dpi / 0.0254 meters/inch
缩放级别地图宽度、⾼度(像素)地⾯分辨率(⽶/像素)地图⽐例尺(以96dpi为例)
151278,271.5170 1 : 295,829,355.45
21,02439,135.7585 1 : 147,914,677.73
32,04819,567.8792 1 : 73,957,338.86
44,0969,783.9396 1 : 36,978,669.43
58,1924,891.9698 1 : 18,489,334.72
616,3842,445.9849 1 : 9,244,667.36
732,7681,222.9925 1 : 4,622,333.68
865,536611.4962 1 : 2,311,166.84
9131,072305.7481 1 : 1,155,583.42
10262,144152.8741 1 : 577,791.71
11524,28876.4370 1 : 288,895.85
121,048,57638.2185 1 : 144,447.93
132,097,15219.1093 1 : 72,223.96
144,194,3049.5546 1 : 36,111.98
158,388,608 4.7773 1 : 18,055.99
1616,777,216 2.3887 1 : 9,028.00
1733,554,432 1.1943 1 : 4,514.00
1867,108,8640.5972 1 : 2,257.00
19134,217,7280.2986 1 : 1,128.50
20268,435,4560.1493 1 : 564.25
21536,870,9120.0746 1 : 282.12
221,073,741,8240.0373 1 : 141.06
232,147,483,6480.0187 1 : 70.53
五、Bing Maps像素坐标系和地图图⽚编码
为了优化地图系统性能,提⾼地图下载和显⽰速度,所有地图都被分割成256 x 256像素⼤⼩的正⽅形⼩块。

由于在每个放⼤级别下的像素数量都不⼀样,因此地图图⽚(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值被转换成⼀维字串,即四叉树键值(quardtree key,简称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相同,下图中,tile 2是tile 20⾄23的⽗tile,tile 13是tile 130⾄133的⽗级:
最后,quadkey提供的⼀维索引值通常显⽰了两个tile在XY坐标系中的相似性。

换句话说,两个相邻的tile对应的quadkey⾮常接近。

这对于优化数据库的性能⾮常重要,因为相邻的tile通常被同时请求显⽰,因此可以将这些tile存放在相同的磁盘区域中,以减少磁盘的读取次数。

下⾯是微软Bing Maps的TileSystem相关算法:
using System;
using System.Text;
namespace Microsoft.MapPoint
{
static class TileSystem
{
private const double EarthRadius = 6378137;
private const double MinLatitude = -85.05112878;
private const double MaxLatitude = 85.05112878;
private const double MinLongitude = -180;
private const double MaxLongitude = 180;
///<summary>
/// Clips a number to the specified minimum and maximum values.
///</summary>
///<param name="n">The number to clip.</param>
///<param name="minValue">Minimum allowable value.</param>
///<param name="maxValue">Maximum allowable value.</param>
///<returns>The clipped value.</returns>
private static double Clip(double n, double minValue, double maxValue)
{
return Math.Min(Math.Max(n, minValue), maxValue);
}
///<summary>
///Determines the map width and height (in pixels) at a specified level
/// of detail.
///</summary>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<returns>The map width and height in pixels.</returns>
public static uint MapSize(intlevelOfDetail)
{
return (uint) 256 << levelOfDetail;
}
///<summary>
///Determines the ground resolution (in meters per pixel) at a specified
/// latitude and level of detail.
///</summary>
///<param name="latitude">Latitude (in degrees) at which to measure the
/// ground resolution.</param>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<returns>The ground resolution, in meters per pixel.</returns>
public static double GroundResolution(double latitude, int levelOfDetail)
{
latitude = Clip(latitude, MinLatitude, MaxLatitude);
return Math.Cos(latitude * Math.PI / 180) * 2 * Math.PI * EarthRadius / MapSize(levelOfDetail);
}
///<summary>
///Determines the map scale at a specified latitude, level of detail,
/// and screen resolution.
///</summary>
///<param name="latitude">Latitude (in degrees) at which to measure the
/// map scale.</param>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<param name="screenDpi">Resolution of the screen, in dots per inch.</param>
///<returns>The map scale, expressed as the denominator N of the ratio 1 : N.</returns>
public static double MapScale(double latitude, int levelOfDetail, intscreenDpi)
{
return GroundResolution(latitude, levelOfDetail) * screenDpi / 0.0254;
}
///<summary>
/// Converts a point from latitude/longitude WGS-84 coordinates (in degrees)
/// into pixel XY coordinates at a specified level of detail.
///</summary>
///<param name="latitude">Latitude of the point, in degrees.</param>
///<param name="longitude">Longitude of the point, in degrees.</param>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<param name="pixelX">Output parameter receiving the X coordinate in pixels.</param>
///<param name="pixelY">Output parameter receiving the Y coordinate in pixels.</param>
public static void LatLongToPixelXY(double latitude, double longitude, intlevelOfDetail, out int pixelX, out int pixelY) {
latitude = Clip(latitude, MinLatitude, MaxLatitude);
longitude = Clip(longitude, MinLongitude, MaxLongitude);
double x = (longitude + 180) / 360;
double sinLatitude = Math.Sin(latitude * Math.PI / 180);
double y = 0.5 - Math.Log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);
uint mapSize = MapSize(levelOfDetail);
pixelX = (int) Clip(x * mapSize + 0.5, 0, mapSize - 1);
pixelY = (int) Clip(y * mapSize + 0.5, 0, mapSize - 1);
}
///<summary>
/// Converts a pixel from pixel XY coordinates at a specified level of detail
/// into latitude/longitude WGS-84 coordinates (in degrees).
///</summary>
///<param name="pixelX">X coordinate of the point, in pixels.</param>
///<param name="pixelY">Y coordinates of the point, in pixels.</param>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<param name="latitude">Output parameter receiving the latitude in degrees.</param>
///<param name="longitude">Output parameter receiving the longitude in degrees.</param>
public static void PixelXYToLatLong(int pixelX, int pixelY, intlevelOfDetail, out double latitude, out double longitude) {
double mapSize = MapSize(levelOfDetail);
double x = (Clip(pixelX, 0, mapSize - 1) / mapSize) - 0.5;
double y = 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>
/// Converts pixel XY coordinates into tile XY coordinates of the tile containing
/// the specified pixel.
///</summary>
///<param name="pixelX">Pixel X coordinate.</param>
///<param name="pixelY">Pixel Y coordinate.</param>
///<param name="tileX">Output parameter receiving the tile X coordinate.</param>
///<param name="tileY">Output parameter receiving the tile Y coordinate.</param>
public static void PixelXYToTileXY(int pixelX, int pixelY, out int tileX, out int tileY)
{
tileX = pixelX / 256;
tileY = pixelY / 256;
}
///<summary>
/// Converts tile XY coordinates into pixel XY coordinates of the upper-left pixel
/// of the specified tile.
///</summary>
///<param name="tileX">Tile X coordinate.</param>
///<param name="tileY">Tile Y coordinate.</param>
///<param name="pixelX">Output parameter receiving the pixel X coordinate.</param>
///<param name="pixelY">Output parameter receiving the pixel Y coordinate.</param>
public static void TileXYToPixelXY(int tileX, int tileY, out int pixelX, out int pixelY)
{
pixelX = tileX * 256;
pixelY = tileY * 256;
}
///<summary>
/// Converts tile XY coordinates into a QuadKey at a specified level of detail.
///</summary>
///<param name="tileX">Tile X coordinate.</param>
///<param name="tileY">Tile Y coordinate.</param>
///<param name="levelOfDetail">Level of detail, from 1 (lowest detail)
/// to 23 (highest detail).</param>
///<returns>A string containing the QuadKey.</returns>
public static string TileXYToQuadKey(int tileX, int tileY, intlevelOfDetail)
{
StringBuilder quadKey = newStringBuilder();
for (int i = levelOfDetail; i > 0; i--)
{
char digit = '0';
int mask = 1 << (i - 1);
if ((tileX & mask) != 0)
{
digit++;
}
if ((tileY & mask) != 0)
{
digit++;
digit++;
}
quadKey.Append(digit);
}
return quadKey.ToString();
}
///<summary>
/// Converts a QuadKey into tile XY coordinates.
///</summary>
///<param name="quadKey">QuadKey of the tile.</param>
///<param name="tileX">Output parameter receiving the tile X coordinate.</param>
///<param name="tileY">Output parameter receiving the tile Y coordinate.</param>
///<param name="levelOfDetail">Output parameter receiving the level of detail.</param>
public static void QuadKeyToTileXY(string quadKey, out int tileX, out int tileY, out intlevelOfDetail) {
tileX = tileY = 0;
levelOfDetail = quadKey.Length;
for (int i = levelOfDetail; i > 0; i--)
{
int mask = 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:
throw new ArgumentException("Invalid QuadKey digit sequence.");
}
}
}
}
}
--------------------------------------------------------------------------------------------------------------------------------------------------------
当我们在⽤arcgis server 构建切⽚时,我们会发现在缓存⽣成的conf.xml中有这样的⽚段:
在上述⽚段中<LODInfo>代表了每⼀级切⽚的信息,<LevelID>代表切⽚的级数。

在这⾥,<Scale>代表⽐例尺。

⽐例尺是表⽰图上距离⽐实地距离缩⼩的程度,也叫缩尺。

公式为:⽐例尺=图上距离/实地距离。

⽤数字的⽐例式或分数式表⽰⽐例尺的⼤⼩。

例如地图上1厘⽶代表实地距离500千⽶,可写成:1∶50,000,000或写成:1/50,000,000。

<Resolution>,代表分辨率。

Resolution 的实际含义代表当前地图范围内,1像素代表多少地图单位(X地图单位/像素),地图单位取决于数据本⾝的空间参考。

当我们在进⾏Web API的开发时,经常会碰到根据Resolution来缩放地图的情况。

但是实际需求中我们更需要根据Scale来缩放,因此就涉及到Scale和Resolution的转换。

Resolution和Scale的转换算法:
Resolution跟dpi有关,跟地图的单位有关。

(dpi代表每英⼨的像素数)
Resolution和Scale的转换算法
举例:
案例⼀:如果地图的坐标单位是⽶, dpi为96
1英⼨= 2.54厘⽶;
1英⼨=96像素;
最终换算的单位是⽶;
如果当前地图⽐例尺为1: 125000000,则代表图上1⽶实地125000000⽶;
⽶和像素间的换算公式:
1英⼨=0.0254⽶=96像素
1像素=0.0254/96 ⽶
则根据1:125000000⽐例尺,图上1像素代表实地距离是125000000*0.0254/96 = 33072.9166666667⽶。

我们这个换算结果和切⽚的结果略微有0.07⽶的误差。

这个误差产⽣的原因是英⼨换算厘⽶的参数决定的,server使⽤的换算参数1英⼨约等于0.025*******⽶。

案例⼆:如果地理坐标系是wgs84,地图的单位是度,dpi为96
Server中度和⽶之间的换算参数:
1度约等于 111194.872221777⽶
接下来就需要进⾏度和像素间的换算:
当⽐例尺为1:64000000⽶时,相当于1像素 = 64000000*0.025*******/96 = 16933.3672⽶
再将⽶转换为度 16933.3672/111194.872221777 = 0.1522855043731385度
因此当地图单位为度时,近似计算在1:64000000 对应的Resolution为0.1522855043731385度验证结果:
-----------------------------------------------------------------------------------------------------------------------double resolution = scale * 0.025*******/96/111194.872221777;。

相关文档
最新文档