1. 问题背景
问题的起因是在业务开发中,需要编写一个
MapPicker组件,组件可拆分为Preview和Picker两个子组件。在Preview组件中,可接受一组geoJson数据,Preview组件需要在地图上绘制出区域,并且根据区域的尺寸自动调整到合适的缩放比例,为了让区域信息更好的展示。 具体效果如下图所示:
2. 实现思路
2.1 地图尺寸与缩放级别
首先需要了解全画幅地图尺寸和缩放级别的关系, 通过下图可以得到如下关系:
zoom: 为地图缩放级别全画幅地图宽度 = 256 * 2^zoom全画幅地图高度 = 256 * 2^zoom
2.2 像素与实际距离的关系
[注] 一像素(px)代表的实际距离(m) 用pxm指代。pxm(18)指代18级缩放下的pxm
通过上面的公式可以得到
- 在
18级的缩放等级下, 全画幅地图的宽度为256 * 2^18(一般18级为最大缩放等级) - 因为
全画幅地图的宽度 = 赤道长度(40075016.68557849m), 所以在18级缩放等级下,pxm(18) = 40075016.68557849 / 256 * 2^18, - 整理下为:
pxm(zoom) = 40075016.68557849 / 256 * 2^zoom, - 这里需要铺垫的是,
pxm(18-n) / pxm(18) = 2^n
2.3 最后的验算
- 容器尺寸指: 地图在
DOM中的尺寸, 这里以200px * 200px为例- 讲解一般以
18级别缩放为例, 是为了通过18级倒推结果, 当然你也可以从0级开始
- 计算
18级别缩放下容器宽度(200px)能够展现的实际距离vm(zoom):vm(18) = 200 * pxm(18) - 假设现在需要展示的区域为矩形, 尺寸为
1000m * 1000m, 中线点为[26,120](具体解析方法见代码详情) - 根据
2.2的公式pxm(18-n) / pxm(18) = 2^n, 可知:vm(18) - pxm(18) = 2^n, 那么:n = lg(vm(18)/pxm(18))/lg(2)
3.具体代码
代码以
javaScript语言为例
import * as turf from '@turf/turf';
// coordinates: Array<[number, number, numer]>
function fullViewWindow (coordinates) {
const polygon = turf.polygon([[...coordinates]]);
const center = turf.center(polygon).geometry.coordinates; //区域的几何中心
//以矩形为例, 如果非矩形需要计算`bbox`转为矩形
//1.通过三个相邻点位, 计算出矩形的最长边
const pointA = turf.point(coordinates[0])
const pointB = turf.point(coordinates[1])
const pointC = turf.point(coordinates[2])
const d1 = turf.distance(pointA, pointB, { units: 'meters' });
const d2 = turf.distance(pointB, pointC, { units: 'meters' });
const d = Math.max(d1, d2);
//2.计算18级别缩放下的pxm(赤道周长/18级缩放全球地图总像素点)
const pxm18 = 40075016.68557849 / (256 * Math.pow(2, 18));
//3.计算视图范围宽度呈现的实际距离(单位米)
//假设容器尺寸为(200px * 200px)
const clientWidth = 200
const clientHeight = 200
const vm18 = Math.min(clientWidth, clientHeight) * pxm18;
let zoom = 18 - Math.log2(d / viewMeter);
zoom = zoom > 18 ? 18 : zoom;
return { zoom, center }
}