在Leaflet.js中跨越antimeridian包装线条/多边形

时间:2016-11-10 16:28:11

标签: javascript leaflet

有没有办法让一个形状从一条边穿过日线边经线(180°经度)到Leaflet.js的地图的另一边?

我看过:

但我不确定我能做些什么才能让它可靠地划过日期线......

提前谢谢!

2 个答案:

答案 0 :(得分:8)

哦,你正在点击antimeridian artifacts。你不是第一个,也不会是最后一个。

在Leaflet中,基本上有两种解决此问题的方法:

1a:事先剪切多边形

如果您了解GIS工具,请预处理多边形,这样您最终会得到两个(或可能更多)多边形。请参阅«How can I make a polyline wrap around the world?»

一旦你有一个文件中有几个没有穿过反子弹的多边形,它们就会渲染得很好。如果对多边形应用边框,则会出现伪影(即,在反射影像处的垂直多边形边界,跨越多边形内部),因此您可能希望使用多边形边缘剪切多边形和折线。你想要很好地渲染。

1b:在浏览器上剪切多边形

如果您不想事先剪切多边形,可以让网络浏览器动态执行。

有一些实用程序可以在这里提供帮助,但我特别指出Leaflet.VectorGrid。通过利用geojson-vt,它可以将多边形及其边缘切割为平铺大小的多边形和多边形边。它可以handle geometries crossing the antimeridian quite well

您可能希望直接查看geojson-vt,或者turf.js进行一些动态地理处理。

2:在[-180..180]范围

之外思考

Leaflet可以处理[-180..180]范围之外的经度。在Leaflet中,经度仅包含 TileLayer的图块,而不包括标记或折线。

换句话说:[0, -179]处的标记显示在与[0, 181]不同的位置。请参阅this answer for an example

换句话说:从[0, 179][0, -179]的一条线长358度,但从[0, 179][0, 181]的线条长度为2度。

换句话说:您可以使用坐标经过[-180..180]范围以外的经度的线串或多边形,这对于Leaflet来说很好。对于很多GIS软件来说并不好(事实上,我认为新的GeoJSON规范禁止它)。但它会使Leaflet感到高兴。

答案 1 :(得分:1)

当您使用圆柱投影时,就像 Leaflet 所做的那样,它可以通过三角学相对容易地解决。我的解决方案是基于上面Ivan's answer的第一种方法,即在第180个子午线处将线切割成两部分。我的解决方案并不完美,我将在下面展示,但这是一个好的开始。

代码如下:

function addLineToMap(start, end) {
    if (Math.abs(start[1] - end[1]) > 180.0) {
        const start_dist_to_antimeridian = start[1] > 0 ? 180 - start[1] : 180 + start[1];
        const end_dist_to_antimeridian = end[1] > 0 ? 180 - end[1] : 180 + end[1];
        const lat_difference = Math.abs(start[0] - end[0]);
        const alpha_angle = Math.atan(lat_difference / (start_dist_to_antimeridian + end_dist_to_antimeridian)) * (180 / Math.PI) * (start[1] > 0 ? 1 : -1);
        const lat_diff_at_antimeridian = Math.tan(alpha_angle * Math.PI / 180) * start_dist_to_antimeridian;
        const intersection_lat = start[0] + lat_diff_at_antimeridian;
        const first_line_end = [intersection_lat, start[1] > 0 ? 180 : -180];
        const second_line_start = [intersection_lat, end[1] > 0 ? 180 : -180];

        L.polyline([start, first_line_end]).addTo(map);
        L.polyline([second_line_start, end]).addTo(map);
    } else {
        L.polyline([start, end]).addTo(map);
    }
}

这将计算线穿过第 180 条子午线的纬度,并在第 180 条子午线上从起点到该纬度绘制第一条线,然后从该点到终点绘制第二条线。

下图显示了一个结果示例。

Example

尽管我相当确定我的计算经过数学验证,但在两条线分开的地方还是有一个小问题。我不确定这是由于 Leaflet 地图的渲染,还是我的计算中的实际错误。

起点是[35.552299, 139.779999],终点是[64.81510162, -147.8560028]

点之间的总纵向差为72.364,纬度差为29.263。使用下面的代码或 online calculator,角度 α22.018。仅取起点到第 180 条子午线的距离和角度 α,起点与交点之间的 latitudinal difference16.264。加上起点的纬度和这个值,我们得到第 180 个子午线的纬度 51.8166。在地图上画一条直线告诉我这个值应该稍微高一点,但我不知道为什么或如何计算。

如果您想要一条能够准确显示地球曲线的曲线,我强烈建议您使用 Leaflet.Geodisic。它易于使用,并且内置了反子午线问题的解决方案,因此您不必担心。