地图投影上的弯曲geojson多边形边缘

时间:2019-06-01 04:17:56

标签: d3.js geojson

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

Example: Aitoff Projection Example: Mercator 这是我正在使用的代码:

<!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>

1 个答案:

答案 0 :(得分:0)

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

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

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

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

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 行有效,但对某些行则无效,因为诸如经络切割:

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),则线条将不会与刻度线对齐。

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

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