地图瓦片编号与经纬度的换算关系及不同源坐标之间的相互转换


1、根据输入等级计算全部瓦片行列号及总数量

void GetTilesByLevel(int nLevel)
{
	if (nLevel == 0)
	{
		cout << 0 << "," << 0 << endl;
		cout << "The sumTile = " << 1 << endl;
		return;
	}
	
	int nSumTile = 1;
	for (int m = 1; m <= nLevel; m++)
	{
		int nLevelTileCount = 0;
		for (int i = 0; i < pow(2, m); i++)
		{
			for (int j = 0; j < pow(2, m); j++)
			{
				cout << i << "," << j << endl;
				nLevelTileCount++;
			}
		}

		cout << "The level = " << m << "; TileCount = " << nLevelTileCount << endl;

		nSumTile += nLevelTileCount;
	}
	
	cout << "The sumTile = " << nSumTile << endl;
}

int main(int argc, char** argv)
{
	cout << "please input level: " << endl;
	int nLevel = 0;
	cin >> nLevel;

	GetTilesByLevel(nLevel);
}

前言

地图瓦片编号与与经纬度坐标之间的转换与简单理解。相关资料看了好多次,每次看完就忘,这里做一个简单的学习笔记。

Web墨卡托投影

通常提到Web墨卡托投影,我最先想到的关键词是: “3857”、“谷歌地图”。再往深了想就是“正轴等角圆柱投影”、“越靠近两极变形越大”等特性。以前对“越靠近两极变形越大”的理解是:越靠近两极,地图横向拉伸越严重。今天查资料时突然意识到一点:越靠近两级纵向拉伸同样越严重。也就是说纬度分布是不均匀的

墨卡托投影示意图

地图瓦片分割

目前接触的绝大多数地图瓦片是以左上角为原点开始编号的(TMS 瓦片除外)。从左至右为 x 轴, 从上到下为 y轴。


对于整个地球而言,基于 Web 墨卡托投影的地图左上角经纬度坐标为(180°,85.0511 °),右下角经纬度为(-180°,-85.0511°)。纬度范围是[-85.0511, 85.0511 ] 的原因是:保证整个地图为正方形。85.0511 这个数字是由如下公式得到:

arctan(sinh(π))

公式的具体推导过程见:https://en.wikipedia.org/wiki/Mercator_projection

ArcGIS JS API 加载 TMS 地图瓦片 这篇笔记中提到过缩放等级 z 和每行(或每列)瓦片数量 n 的关系如下:

n = 2^{z}

由上述可知投影后地图经度范围是[-180, 180],在第 z 级别每行的瓦片数为 n。那么等级 z 下某一经度对应的 x 轴编号为:

tileX = \frac{lon + 180}{360}\cdot n
其中 lon 的单位为度。

而等级 z 下某一纬度对应的 y 轴编号则比较复杂(因为纬度分布不均匀):

tileY = n \cdot \frac{(1 - \frac{(log(tan(lat) + sec(lat)) }{π}))}{2}

其中 lat 的单位为弧度制。

已知瓦片编号反算该瓦片左上角经纬度坐标公式如下:

lon = \frac{tileX}{n} \cdot 360.0 - 180.0
lat = arctan(sinh(π \cdot (1 - \frac{2 * tileY} {n})))

相应转换代码如下:
JS:

// 经纬度转瓦片编号
function lon2tile(lon,zoom) { 
  return (Math.floor((lon+180)/360*Math.pow(2,zoom))); 
}
 function lat2tile(lat,zoom)  { 
   return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,zoom))); 
}

// 瓦片编号转经纬度
 function tile2long(x,z) {
  return (x/Math.pow(2,z)*360-180);
 }
 function tile2lat(y,z) {
  var n=Math.PI-2*Math.PI*y/Math.pow(2,z);
  return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
 }

JAVA:

public class slippytest {
 public static void main(String[] args) {
   int zoom = 10;
   double lat = 47.968056d;
   double lon = 7.909167d;
   System.out.println("https://tile.openstreetmap.org/" + getTileNumber(lat, lon, zoom) + ".png");
 }
 public static String getTileNumber(final double lat, final double lon, final int zoom) {
   int xtile = (int)Math.floor( (lon + 180) / 360 * (1<<zoom) ) ;
   int ytile = (int)Math.floor( (1 - Math.log(Math.tan(Math.toRadians(lat)) + 1 / Math.cos(Math.toRadians(lat))) / Math.PI) / 2 * (1<<zoom) ) ;
    if (xtile < 0)
     xtile=0;
    if (xtile >= (1<<zoom))
     xtile=((1<<zoom)-1);
    if (ytile < 0)
     ytile=0;
    if (ytile >= (1<<zoom))
     ytile=((1<<zoom)-1);
    return("" + zoom + "/" + xtile + "/" + ytile);
   }
 }

class BoundingBox {
    double north;
    double south;
    double east;
    double west;   
  }
  BoundingBox tile2boundingBox(final int x, final int y, final int zoom) {
    BoundingBox bb = new BoundingBox();
    bb.north = tile2lat(y, zoom);
    bb.south = tile2lat(y + 1, zoom);
    bb.west = tile2lon(x, zoom);
    bb.east = tile2lon(x + 1, zoom);
    return bb;
  }

  static double tile2lon(int x, int z) {
     return x / Math.pow(2.0, z) * 360.0 - 180;
  }

  static double tile2lat(int y, int z) {
    double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
    return Math.toDegrees(Math.atan(Math.sinh(n)));
  }

不通源坐标之间的相互转换——百度坐标与其他坐标系转换:

void fun() throws IOException
 {
   	
   	   String url = "http://api.map.baidu.com/geoconv/v1/?coords=113.538248,23.132953&from=1&to=5&ak=edGc5mIugVxx7lwUx9YpraKeWmExG64o";
   	
       HttpClient client = HttpClients.createDefault();// 创建默认http连接
       HttpPost post = new HttpPost(url);// 创建一个post请求
      
       HttpResponse response = client.execute(post);// 用http连接去执行get请求并且获得http响应
       HttpEntity entity = response.getEntity();// 从response中取到响实体
       String html = EntityUtils.toString(entity);// 把响应实体转成文本
       System.out.println("返回信息"+html);
  }

from参数说明
源坐标类型:
1:GPS设备获取的角度坐标,WGS84坐标;
2:GPS获取的米制坐标、sogou地图所用坐标;
3:google地图、soso地图、aliyun地图、mapabc地图和amap地图所用坐标,国测局(GCJ02)坐标;
4:3中列表地图坐标对应的米制坐标;
5:百度地图采用的经纬度坐标;
6:百度地图采用的米制坐标;
7:mapbar地图坐标;
8:51地图坐标

返回信息

{“status”:0,“result”:[{“x”:113.54988989895114,“y”:23.13628309504525}]}

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

欧特克_Glodon

很高兴能帮助到您!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值