Winse Blog

走走停停都是风景, 熙熙攘攘都向最好, 忙忙碌碌都为明朝, 何畏之.

Map入门指南

最近了解了一些Map地图相关的知识点,把学习的资料罗列一下:

坐标体系

说明:

  • WGS84:为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。标准的Web墨卡托投影坐标系。
  • GCJ02:又称火星坐标系,是由中国国家测绘局制定的地理坐标系统,是由WGS84加密后得到的坐标系。指中国国家测绘局制订的加偏Web墨卡托投影,正式名称为GCJ-02,国内可用的地图多数属于这种坐标系。
  • BD09:为百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。

地图API

百度

腾讯

高德

国内其他

Google

NOTE: Google的在Java里面用需要指定证书和代理:

网页访问一次,把geo的CA证书保存到本地,然后导入到本地的证书库,加入到应用得启动参数里面:

1
2
3
keytool -import -rfc -v -alias geo_ca -keystore truststore -file geo.cer

java -Djava.net.useSystemProxies=true -Djavax.net.ssl.trustStore=.\security\truststore 

各坐标系间的转换

Example:

坐标转换代码:

比例尺

百度

1
2
3
map.addControl(new BMap.ScaleControl());
map.enableScrollWheelZoom();   //启用滚轮放大缩小,默认禁用
map.enableContinuousZoom();    //启用地图惯性拖拽,默认禁用

左下角标注的尺寸包括一个数字加一条线段,就是地图上与那条线等长的距离的实际距离为数字表示的长度。假设长度为一厘米,那就是说那一厘米在地图上同等长度实际是20m的距离,比例为1:2000。

在百度地图API中,平面坐标是以最大级别18级为基准的。就是说在18级平面坐标的一个单位就代表了屏幕上的1个像素 (详细的内容后面讲,可以参考百度地图API详解之地图坐标系统)。

Android里面计算百度比例尺的方式:取两个点获取它们的经纬度,然后算两个点之间的距离。

NOTE: 百度地图SDK还提供了标注工具(PushpinTool),测距工具(DistanceTool)。

Google

Bing

深入了解地图 - 瓦片

各种tile的地址路径

腾讯”矢量”地图 - 通过JSON传数据画Canvas

国内百度腾讯网页端的实现

现在的地图基本都是使用瓦片技术,计算步骤如下:

  • 首先,根据投影(墨卡托投影)把 经纬度(度) 转成 平面坐标(m);
  • 然后,更具比例尺把 平面坐标 转成 像素坐标;
  • 最后,根据坐标的平移把窗口内的瓦片从服务端下载并进行展示。

通过JS代码了解地图的实现:

百度

打开一个百度的应用 http://api.map.baidu.com/lbsapi/getpoint/index.html 然后在调试窗口运行转换经纬度的代码,然后进到对应的代码,打断点,然后艰辛进行与混淆的代码死磕!

1
2
3
4
5
6
7
8
9
var projection =new BMap.MercatorProjection();
var point = projection.lngLatToPoint(new BMap.Point(113.338191,23.138992));
point
Q {x: 12616886.99, y: 2631894}

var projection =new BMap.MercatorProjection();
var point = projection.pointToLngLat(new BMap.Pixel(12616886.99,2631894));
point
H {lng: 113.338191, lat: 23.138992}

从代码上看还是不难的,但是里面有一堆魔法数字完全不懂。

如果仅仅获取瓦片 https://github.com/CntChen/tile-lnglat-transform/ 推荐使用这个项目。

这里仅仅是经纬度转换为平面坐标(m)的过程。我们在源码中查找 getTilesUrl 在5901行打个断点,然后在回到网页,移动一下地图。接下来,就可以调试整个过程了。

注意标识的两处,是进行层级缩放、计算出瓦片编号的代码。

腾讯

看过了百度的,再看腾讯的。然鹅并没有觉得轻松啊,两种不同的坐标系,做法差别还是挺大的。不过从命名上看腾讯算学术派的了。

打开 http://lbs.qq.com/javascript_v2/case-run.html#sample-geocoding-reverse ,在 map.qq.com/js/v2.js 的 apiLoad 处打断点进行到真正的map的js文件。

然后查找 fromLatLngToPoint ,再在界面动一下,就可以调试整个过程:

  • fromLatLngToPoint
  • fromPointToLatLng

调式的时刻可以顺便看看整个调用链,会发现:

  • fromDivPixelToLatLng
  • fromLatLngToDivPixel

fromDivPixelToLatLng的条用链,以及数据的传递如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
fromDivPixelToLatLng 
=>

->Rh 转成相对位置转成绝对坐标后,传入到Mc
[
a
P {x: 930, y: -471}
c
ia {width: 1725872.4160540444, height: 794188.9248479041}
b
true
]

->Mc(g, a, h, f)  <->  Gf 缩放后,调用fromPointToLatLng
[
g
Sh {a: P, b: 0.7111111111111111, c: 40.74366543152521, d: true}
a
P {x: 1726802, y: 793718}
h
13
f
true
]

->fromPointToLatLng(Ad, e)
[
Ad
P {x: 210.791259765625, y: 96.889404296875}
e
true
]

-> Return value : u
    lat:40.02892889530204
    lng:116.42520904541016

从 坐标计算 经纬度 反过来了:

腾讯的计算过程直接把 转平面坐标和转像素坐标 两个过程合并了。通过 fromLatLngToPoint 得到就是一个 像素坐标 的值,然后通过缩放就可以得到当前层级级别像素坐标

查找瓦片地址的代码,直接在代码里面查找 x= 在37823处代码都打断点,刷新重新加载瓦片就会进到断点。

然后查看调用链,

详细跟踪的话,会发现,每次加载都会计算左上角和右下角两个点的像素坐标 (窗口的bounds)。计算要加载的瓦片时,直接用最大减最小除以256(每个瓦片的像素),就得到要加载瓦片的编号了。

用了几天比较肤浅的跟了下QQ地图的功能,如果没有混淆应该看起来会爽很多。。。没有很深层次的东西,仅仅是一个源码调试过程的记载,一些理论原理的知识请查完文章中的链接。

其他

–END

Comments