zoukankan      html  css  js  c++  java
  • 真正的墨卡托投影转换详解

    所谓天下文章一大抄,看你会抄不会抄。网上对于墨卡托投影的解释比较多,但是我发现很多都是是漏洞百出,无脑抄。例如:

    X轴:由于赤道半径为6378137米,则赤道周长为2*PI*r = 20037508.3427892,因此X轴的取值范围:[-20037508.3427892,20037508.3427892]。

    2 * Math.PI * 6378137 = 40075016.0019724
    
    

    X轴取值范围确实是[-20037508.3427892,20037508.3427892],但是请不要闭着眼睛说这是周长(实际是半周长)

    地理经度的取值范围是[-180,180],纬度不可能到达90°,通过纬度取值范围为[20037508.3427892,20037508.3427892] ,反计算可得到纬度值为85.05112877980659。因此纬度取值范围是[-85.05112877980659,85.05112877980659]

    这跳的也跳快了,为啥纬度取值范围也要半周长,反计算过程又在哪?


    墨卡托投影的模型:地球近似为正球体,球体中心有个发亮的光点,光点将球面上的每个点都投影到正切赤道的圆柱内表面上,将圆柱体内表面展开就是一张世界地图。

    形象的理解:有个特殊的乒乓球(地球),这个乒乓球球体内中心点会发光,乒乓球卡在塑料水管内部,把塑料水管水管按有乒乓球阴影部分锯开碾平后水管内表面就是世界地图。

    ![](data:image/svg+xml;utf8,)

    在WebGIS中,经常有已知WGS84(EPSG:4326)经纬度[lng, lat],在Canvas2D上要显示该点就要换算成墨卡托投影(EPSG:3857),其实只要知道地球变成平面地图时的转换比率,那就能通过经纬度求出墨卡托的值。Openlayers以及Mapbox/sphericalmercator都有转换函数,但是这个计算公式怎么来的呢?

    假设有个Q点离P点无限近,地球半径为 R ,如下图

    ![](data:image/svg+xml;utf8,)

    P的经纬度为 [lambda, varphi]lambdavarphi 都为弧度),相邻点Q的经纬度为 [lambda + deltalambda, varphi + deltavarphi] ,那么:

    平行赤道的 varphi 纬线所在圆的半径: r = R 	imes cosvarphi ,弧度PM = r 	imes  deltalambda = R   cosvarphi deltalambda ,如下示意图

    ![](data:image/svg+xml;utf8,)

    tananglealpha approx frac{PM}{QM} = frac{Rcosvarphideltalambda}{Rdeltavarphi}
    tanangleeta = frac{P'M' }{Q'M' } = frac{delta x}{delta y}

    与纬线平行转换比率: frac{P'M' }{PM} = frac{delta x}{R cosvarphi deltalambda}
    与经线平行转换比率: frac{P'K'}{PK} = frac{delta y}{Rdeltavarphi}

    由于经线转化后未发生形变,那么根据globe和projection对比图( frac{delta x}{2pi R} = frac{delta lambda}{2pi}

    得出  delta x = R 	imes delta lambda

    再利用求导概念, y'(varphi) = frac{delta y}{deltavarphi} 那么:

    与纬线平行转换比率: frac{P'M' }{PM} = frac{delta x}{R cosvarphi deltalambda} = frac{R deltalambda}{R cosvarphi deltalambda} = frac{1}{cosvarphi } = secvarphi
    与经线平行转换比率:frac{P'K'}{PK} = frac{delta y}{Rdeltavarphi} = frac{frac{y'(varphi)}{deltavarphi}}{Rdeltavarphi} = frac{y'(varphi)}{R}

     tananglealpha =frac{ Rcosvarphi deltalambda}{Rdeltavarphi} = frac{ delta x cosvarphi}{Rdeltavarphi}=frac{tanangleeta	imesdelta y	imes cosvarphi}{Rdeltavarphi}=tanangleeta	imes y'(varphi)	imes frac{cosvarphi}{R}

    由于墨卡托投影为了保证投影后达到等角航线(航向角和经度所成的夹角保持不变),那么可以认定 anglealpha=angleeta

    等等,各位看到这里肯定会问,墨卡托设计的为什么需要等角航线?为什么是要和经度夹角而不是维度夹角?其实我写的时候也想了下,我们不妨大胆猜测下:

    墨卡托设计出来的投影是为了航海用的,当时航海导航全靠指南针,指南针指向南北两极,与经线平行,那么要根据地图找到航行路线上的陆地的话,一定要根据地图上的所指的角度进行航线行驶(想象下航海电影中主角拿出地图,地图上压着个指南针,然后用尺子画出两点的直线,再量下这条直线和经线的夹角,最后指挥舵手往哪里哪里打几度方向。电影果然没欺骗我们)。

    回归正题,由于 anglealpha = angleeta ,那么 tan∠alpha =  tan∠eta 	imes y'(varphi) 	imes frac{cosφ}{R} 可得出  y'(varphi) = frac{R}{cosvarphi}= R 	imes secφ

    那么根据

    y'(varphi) 的原函数为 y(varphi) = R 	imes ln |tan(frac{varphi}{2} + frac{pi}{4})|y 的反函数 y^{-1} 称为高德曼函数 gd(x) , y(x) = gd^{-1}(x) (这里由于为了和截图对应需要把 varphi 变为了 x

    在Web中,需要对地图进行缩放,为了更好的计算缩放比例,设定横宽比1。之前由于地图的x的取值范围为 [-pi R, pi R] , 那么y的取值范围也为 [-pi R, pi R] ,于是可以求得Web下的墨卡托投影的最大纬度值为:

    而EPSG:4326转EPSG:3857就很好计算了

    // 弧度版
    const x = R * lng 
    const y = R * Math.log(Math.tan((Math.PI*0.25) + (0.5 * lat)))
    
    // 角度版
    const radians = Math.PI / 180
    const x = R * lng * radians
    const y = R * Math.log(Math.tan((Math.PI*0.25) + (0.5 * lat * radians)))
    
    

    参考资料:

    本文转自 https://zhuanlan.zhihu.com/p/326955505,如有侵权,请联系删除。

  • 相关阅读:
    进程池,线程池,协程,gevent模块,协程实现单线程服务端与多线程客户端通信,IO模型
    线程相关 GIL queue event 死锁与递归锁 信号量l
    生产者消费者模型 线程相关
    进程的开启方式 进程的join方法 进程间的内存隔离 其他相关方法 守护进程 互斥锁
    udp协议 及相关 利用tcp上传文件 socketserver服务
    socket套接字 tcp协议下的粘包处理
    常用模块的完善 random shutil shevle 三流 logging
    day 29 元类
    Django入门
    MySQL多表查询
  • 原文地址:https://www.cnblogs.com/hustshu/p/15353562.html
Copyright © 2011-2022 走看看