一般在创建地图时会设置缩放级别,也就是zoom level,比如从1至23个级别,但这些级别是地图库(比如openlayers.js或mapbox.js或leaflet.js)内置的缩放比例,如果没有特殊要求,那么就使用默认就好,而如果需要自己指定每一级的缩放比例,那么常规的zoom level就不够用了。
这里就需要用到resolution,它是scale比例尺的变体。比如地图上1cm代表实际的10000cm,那么scale就是1:10000,简化为resolution就是10000,也就是地图上的一米代表实际的10km。
道理很简单,但实际使用时却有很多坑。比如使用openlayers.js时,由于地图是在电脑上显示的,宽高都是像素为单位,并不清楚像素与物理宽度的关系,openlayers.js定义的缩放比其实是像素值与现实距离米之间的比例。举例,如果是110像素宽代表了50海里,那么比值就是1852 * 50 / 110 = 842,写成代码就是this.view.setResolution(842)。
但有些软件就比较较真,比如mapserver,它的SCALEDENOM就是物理宽度的比值,首先它有个DPI值默认为72,表示一英寸多少像素点,然后英寸又是0.0254米,于是一像素的物理宽度就是1 / 72 * 0.0254 米,以上面的例子来看,比例就是 1852 * 50 / (110 * 1 / 72 * 0.0254) = 2386256,所以对应到mapserver里的scale就是2386256了。
有些软件里,没有scale或者resolution,但是zoom并不是离散整数,而可以是连续的浮点数。此时就需要弄清楚zoom是怎么计算而来的。
zoom或者叫zoom level,是为了瓦片(tile)式加载地图设计的缩放等级。在EPSG:3857投影下,我们先定义zoom=0,即将地图展开为一个256x256的方形,即pixels bound为(0, 0, 256, 256),考虑到赤道周长为40075017米,那么这个展开图在横轴上的单像素代表距离就是40075017/256 = 156543 m/px。
每增大一个zoom,展开图的宽高都乘以2,瓦片数量变为原来的4倍,所以zoom=1,就是256^2x256^2的方形,自然横轴单像素距离就是40075017/(256*2) = 78272 m/px。
完整缩放等级表可查:https://wiki.openstreetmap.org/wiki/Zoom_levels
以上是离散zoom的解释,推演到连续,那么计算公式就是:
zoom = log2(156543 / 赤道单像素距离)
举例:当你希望一个赤道处图片上的100像素代表100km时,那么单像素距离就是1km,zoom值就是log2(156543 / 1000) = 7.29。
但有时候,我们并不是知道赤道上的单像素距离呢,比如我们希望在北维40度的位置,100像素代表100km,那么计算公式就是:
zoom = log2(156543 * cos(latitude) / 单像素距离)
按上面的例子,zoom值就是log(156543 * cos(40 * pi / 180) / 1000) = 6.9。理解起来就是,在不同的维度,zoom=0时,该维度上的周长都会展开为256像素宽。
额外提一点:当单像素代表的距离过大,比如1px代表5km时,误差就会明显起来,原因在于,同一个展开图上,不同维度上的像素点其代表的物理距离不一样,跨越的维度越大,差异越大,此时用中心点的单像素距离去代替整体的,已经不准确了。
在不同的软件里,比例尺的的定义不一样,计算方式就略有一不同。如果有resolution支持,即可以直接设置比例值,那就优先设置比例值,否则就通过计算zoom来实现想要的缩放比。