Yur*_*rik 3 map-projections mercator d3.js leaflet
给定 Leaflet 缩放级别 (0..22),我将如何计算 geoMercator 投影的 D3 缩放值?当 zoom=0 时,整个世界都适合在单个图块 (256x256) 内。在瓦片中,世界大小是 2^zoom x 2^zoom 瓦片。
为了与墨卡托地图的 d3 比例进行比较:
墨卡托地图使用以下公式:
var point = [x, Math.log(Math.tan(Math.PI / 4 + y / 2))];
Run Code Online (Sandbox Code Playgroud)
d3 地图中的比例因子基本上应用以下变换:
point[0] = point[0] * k;
point[1] = -point[1] * k;
Run Code Online (Sandbox Code Playgroud)
d3 墨卡托投影的默认地图比例为 961/2?,或 961 像素乘 360 度。
墨卡托公式的一个怪癖是,“整个”世界的方形视图实际上将在北/南 ±85.05113 度处结束。Web Mercators 可以推动这一点,因为它们不是等角投影。Leaflet 将“整个”世界的范围推到 ±89.15 度左右。
因此,使用适当的墨卡托的 d3 和使用 Web 墨卡托的 Leaflet 意味着比例值可能无法很好地啮合。GIS.stackexchange上的这个答案将提供有关两者之间差异的更多信息。
但是,您仍然可以将两者对齐(大多数情况下)。
Yurik 在下面的评论中指出,一种方法是使用缩放级别和图块大小来获得比例因子:
Web 地图服务使用平铺网格,其中增加缩放级别会使显示的平铺数量增加四倍。在缩放级别为 1 时,世界适合一个图块,在缩放级别为 2 时,世界适合四个正方形图块。瓷砖数量的一般公式是:
瓷砖数量 = 4^zoomLevel
最重要的是,每次我们放大时,穿过地图(一行)所需的图块数量都会增加一倍。
以此为起点,我们可以弄清楚如何匹配两者。
d3 geoMercator961/(2*Math.PI)
用作默认比例 - 这将赤道的 360 度(或 2 弧度)扩展到 961 像素。要将其设置为适用于基于图块的图层,我们只需要知道图块大小和缩放级别。
我们需要知道赤道分布了多少像素,以便我们使用:
tileSize * Math.pow(2,zoomLevel)
这为我们提供了环绕赤道的所有瓷砖的宽度。然后我们可以将其除以 2Pi 并得到我们的 d3 比例:
projection.scale(tileSize * Math.pow(2,zoomLevel) / (2 * Math.PI))
由于 d3 墨卡托和 web 墨卡托之间的差异,可能会出现失真问题,具体取决于您所在的位置和缩放的距离,但这在大多数情况下应该提供良好的对齐。
或者,我们可以使用传单地图的实际角来确定适当的比例:
D3 提供了一种方法,该方法允许投影将地理范围拟合到 svg 范围:projection.fitExtent()
. 此方法采用 geojson 或几何图形。在这个答案中,我使用的是 geojson。
.getBounds()
将返回传单地图范围,因此您可以相当轻松地创建一个 geojson 边界框:
var bbox = {
"type": "Polygon",
"coordinates": [
[
[mymap.getBounds()._northEast.lng, mymap.getBounds()._northEast.lat],
[mymap.getBounds()._northEast.lng, mymap.getBounds()._southWest.lat],
[mymap.getBounds()._southWest.lng, mymap.getBounds()._southWest.lat],
[mymap.getBounds()._southWest.lng, mymap.getBounds()._northEast.lat],
[mymap.getBounds()._northEast.lng, mymap.getBounds()._northEast.lat]
]
]
}
Run Code Online (Sandbox Code Playgroud)
请注意,绕线顺序在 d3 中实际上很重要 - 外圈是逆时针
然后你所要做的就是设置你的投影以适应这个范围:
var projection = d3.geoMercator()
.fitSize([600, 400], bbox);
Run Code Online (Sandbox Code Playgroud)
使用稍微修改的传单示例(更改中心点和缩放),整个过程如下所示:
var point = [x, Math.log(Math.tan(Math.PI / 4 + y / 2))];
Run Code Online (Sandbox Code Playgroud)
point[0] = point[0] * k;
point[1] = -point[1] * k;
Run Code Online (Sandbox Code Playgroud)
var bbox = {
"type": "Polygon",
"coordinates": [
[
[mymap.getBounds()._northEast.lng, mymap.getBounds()._northEast.lat],
[mymap.getBounds()._northEast.lng, mymap.getBounds()._southWest.lat],
[mymap.getBounds()._southWest.lng, mymap.getBounds()._southWest.lat],
[mymap.getBounds()._southWest.lng, mymap.getBounds()._northEast.lat],
[mymap.getBounds()._northEast.lng, mymap.getBounds()._northEast.lat]
]
]
}
Run Code Online (Sandbox Code Playgroud)
我没有添加在地图更改时更新投影的代码,但这只是重新计算边界框并重新应用 fitSize 方法。
归档时间: |
|
查看次数: |
996 次 |
最近记录: |