地图投影上的弯曲geojson多边形边缘 [英] Curved geojson polygon edges on map projections

查看:57
本文介绍了地图投影上的弯曲geojson多边形边缘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用d3.js和geojson在不同的地图投影上绘制矩形.映射的坐标看似正确,但是边缘似乎以奇怪的方式弯曲.我知道这可能与真实地球上的最短路径有关,但是我想让边缘沿着投影的平行线/子午线刻度线.有没有办法做到这一点?有人可以帮忙吗?

I am trying to draw rectangles on different map projections using d3.js and geojson. The mapped coordinates seem right, however the edges appear curved in a strange way. I understand that this may have to do with the shortest path on the real Earth, but what I would like is that the edges follow the parallels/meridians graticule of the projection. Is there a way to do that? Can anyone help?

示例:Aitoff投影 示例:墨卡托 这是我正在使用的代码:

Example: Aitoff Projection Example: Mercator Here is the code I am using:

<!DOCTYPE html>
<html>

<head>
  <title>World Map</title>
  <meta charset="utf-8">

  <script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/topojson.v2.min.js"></script>
  <style>
    path {
      fill: red;
      stroke: #000;
      stroke-width: .1px;
    }
    .graticule {
      fill: none;
      stroke: #000;
      stroke-width: .2px;
    }
    .foreground {
      fill: none;
      stroke: #333;
      stroke-width: 1.2px;
    }

  </style>
</head>

<body>
  <svg width="960" height="600"></svg>
  <script>
    const svg = d3.select("svg")
    const myProjection = d3.geoMercator()
    const path = d3.geoPath().projection(myProjection)
    const graticule = d3.geoGraticule()

    const geojson = {

"type": "FeatureCollection",                                                      
"features": [
{ "type": "Feature", "properties": {
    "color": "blue"
},
"geometry": { 
    "type": "Polygon", 
    "coordinates": [[[-80.0, 50.0], [-20.0, 50.0], [-20.0, -10.0], [-80.0, -10.0], [-80.0, 50.0]]]
} }
]
}
    function drawMap(err, world) {
      if (err) throw err

      svg.append("g")
        .selectAll("path")
        .data(topojson.feature(world, world.objects.land).features)
        .enter().append("path")
        .attr("d", path);

      svg.append("path")
        .datum(graticule)
        .attr("class", "graticule")
        .attr("d", path);

      svg.append("path")
        .datum(graticule.outline)
        .attr("class", "foreground")
        .attr("d", path);

      svg.append("g")
        .selectAll("path")
        .data(geojson.features)
        .enter().append("path")
        .attr("d", path)

    }
    d3.json("https://unpkg.com/world-atlas@1.1.4/world/50m.json", drawMap)


  </script>
</body>

</html>

推荐答案

您的假设是正确的:d3使用大的圆距画线:这意味着使用d3 geoProjection和geoPath的两点之间的任何路径都遵循最短的真实世界两点之间的路径.这意味着:

Your assumption is right: d3 uses great circle distances to draw lines: this means that any path between two points using a d3 geoProjection and geoPath follows the shortest real world path between those two points. This means:

  • 无论投影如何,两个点之间的相同路径都将与其他地理点对齐并具有特征
  • 可以证明反子午线
  • 并且生成的地图更准确地描绘了线条.

要绘制直线和/或平行线(子午线是落在其上的两个点之间的最短路径-因此,假设未旋转的方格,路径已经遵循此直线),则有几种可能性.

To draw straight lines and/or lines that follow parallels (meridians are the shortest path between two points that fall on them - so paths follow this already, assuming an unrotated graticule) there are a few possibilities.

最简单的解决方案是使用像墨卡托(Mercator)这样的圆柱投影来创建自定义的geoTransform.与d3.geoProjections不同,d3.geoTransforms不使用球形几何,而是使用笛卡尔数据.因此,它们不会沿线采样以创建曲线:在使用笛卡尔数据时,这是不必要的.这使我们可以对geoTransform中的geojson顶点使用球形几何体,同时仍在地图上保持直线:

The easiest solution is to use a cylindrical projection like a Mercator to create a custom geoTransform. d3.geoTransforms do not use spherical geometry, unlike d3.geoProjections, instead they use Cartesian data. Consequently they don't sample along lines to create curved lines: this is unecessary when working with Cartesian data. This allows us to use spherical geometry for the geojson vertices within the geoTransform while still keeping straight lines on the map:

var transform = d3.geoTransform({
    point: function(x, y) {
      var projection = d3.geoMercator();
      this.stream.point(...projection([x,y]));
    }
});

如下所示:

var projection = d3.geoMercator();

var transform = d3.geoTransform({
    point: function(x, y) {
      var projection = d3.geoMercator();
      this.stream.point(...projection([x,y]));
    }
});

var color = ["steelblue","orange"]


var geojson = {type:"LineString",coordinates:[[-160,60],[30,45]]};
var geojson2 = {type:"Polygon",coordinates:[[[-160,60,],[-80,60],[-100,30],[-160,60]]]}

var svg = d3.select("body")
  .append("svg")
  .attr("width",960)
  .attr("height",500);
  
svg.selectAll(null)
  .data([projection,transform])
  .enter()
  .append("path")
  .attr("d", function(d) {
    return d3.geoPath().projection(d)(geojson)
  })
  .attr("fill","none")
  .attr("stroke",function(d,i) { return color[i]; } )
  .attr("stroke-width",1);

svg.selectAll(null)
  .data([projection,transform])
  .enter()
  .append("path")
  .attr("d", function(d) {
    return d3.geoPath().projection(d)(geojson2)
  })
  .attr("fill","none")
  .attr("stroke",function(d,i) { return color[i]; } )
  .attr("stroke-width",2);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

橙色线使用变换,蓝色线使用普通墨卡托

在某些情况下,您可以将投影的精度(调节自适应采样)设置为一些高得离谱的数字,这将对 some 行有效,而对其他行则无效,因为诸如经络切割:

In some cases you could set the precision of the projection (which regulates the adaptive sampling) to some absurdly high number, this will work for some lines, but not others due to things like anti-meridian cutting:

var projection = d3.geoMercator().precision(1000000);

var transform = d3.geoTransform({
    point: function(x, y) {
      var projection = d3.geoMercator();
      this.stream.point(...projection([x,y]));
    }
});

var color = ["steelblue","orange"]


var geojson = {type:"LineString",coordinates:[[-160,60],[30,45]]};
var geojson2 = {type:"Polygon",coordinates:[[[-160,60,],[-80,60],[-100,30],[-160,60]]]}

var svg = d3.select("body")
  .append("svg")
  .attr("width",960)
  .attr("height",500);
  
svg.selectAll(null)
  .data([projection,transform])
  .enter()
  .append("path")
  .attr("d", function(d) {
    return d3.geoPath().projection(d)(geojson)
  })
  .attr("fill","none")
  .attr("stroke",function(d,i) { return color[i]; } )
  .attr("stroke-width",1);

svg.selectAll(null)
  .data([projection,transform])
  .enter()
  .append("path")
  .attr("d", function(d) {
    return d3.geoPath().projection(d)(geojson2)
  })
  .attr("fill","none")
  .attr("stroke",function(d,i) { return color[i]; } )
  .attr("stroke-width",2);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

如果要在非圆柱投影上绘制与平行线对齐的线,则这两种方法均无效.对于圆柱投影,平行线是笔直的.上述方法只会产生直线.如果平行线没有笔直投影(例如Aitoff),则线条将不会与刻度线对齐.

Neither approach works if you want to draw lines that are aligned with parallels on a non-cylindrical projection. For a cylindrical projection parallels are straight. The above approaches will only create straight lines. If the parallels aren't projected straight, such as the Aitoff, the lines will not align with the graticule.

要使一条直线平行于一条平行线,您将需要沿路径采样点,因为投影的平行线并非全部都是直线,并且平行线的圆距也不大.因此,默认投影或以上方法均不适用于这些情况.

To have a line follow a parallel you will need to sample points along your paths because the projected parallels will not all be straight and parallels don't follow great circle distance. Therefore neither the default projection nor the method above will work in these instances.

采样时,您将需要将数据视为笛卡尔坐标-实质上是使用圆柱投影(平板Carree)使线与平行线平行.

When sampling you will need to treat the data as Cartesian - essentially using a cylindrical projection (Plate Carree) to have lines follow parallels.

这篇关于地图投影上的弯曲geojson多边形边缘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆