【问题标题】:Why doesn't my geo LineString follow latitude/graticule curves?为什么我的地理 LineString 不遵循纬度/刻度曲线?
【发布时间】:2023-03-22 15:50:02
【问题描述】:

我正在尝试绘制遵循各种纬度段的 LineStrings,但是内置的测地弧插值似乎并未绘制遵循纬度的弧。我的问题是:为什么不呢?我该如何实现呢?

这是我的结果:

还有我的代码:

const width = 500;
const height = 500;
const scale = 200;

const svg = d3.select('svg').attr("viewBox", [0, 0, width, height]);

const projection = d3.geoStereographic().rotate([0, -90]).precision(0.1).clipAngle(90.01).scale(scale).translate([width / 2, height / 2]);
const path = d3.geoPath(projection);

const graticule = d3.geoGraticule().stepMajor([15, 15]).stepMinor([0, 0])();

svg
  .append("path")
  .datum(graticule)
  .attr("d", path)
  .attr("fill", "none")
  .attr("stroke", '#000000')
  .attr("stroke-width", 0.3)
  .attr("stroke-opacity", 1);

let curve = {
  "type": "Feature",
  "geometry": {
    "type": "LineString",
    "coordinates": [
      [-180, 15],
      [-90, 15]
    ]
  }
}

svg
  .append("path")
  .datum(curve)
  .attr("d", path)
  .attr('fill-opacity', 0)
  .attr('stroke', 'red')
  .attr("stroke-width", 1)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

我的小提琴:https://jsfiddle.net/jovrtn/komfxycz/

【问题讨论】:

    标签: d3.js d3.geo


    【解决方案1】:

    D3 在地理数据方面相当独特:它使用球面数学(尽管有很多好处,但确实会带来一些挑战)。 d3.geoPath 对两点之间的线段进行采样,以便路径遵循一个大圆(地球上两点之间的最短路径)。平行线不遵循大圆距离,因此您的路径不遵循平行线。

    您正在寻找的行为要求我们在两个经纬度点之间画一条线,就好像它们不是笛卡尔一样,然后在应用立体投影时保留该线沿线的点。

    使用圆柱投影时,解决方案很简单,不要在直线上的点之间采样。这个answer 包含这样的解决方案。

    这对立体投影没有帮助 - 链接方法只会在第一点和终点之间产生一条直线,而不是沿着平行线的一条曲线。

    一种解决方案是在开始和结束之间手动采样点,就好像数据是笛卡尔数据一样,然后将它们视为 3D 以便使用立体投影进行投影。这会导致路径遵循平行线,其中起点和终点具有相同的北/南值。使用 d3.geoPath 时,采样频率可以减少/消除大圆距离的影响。

    在我的解决方案中,我将使用两个 d3 辅助函数:

    • d3.geoDistance 以弧度为单位测量两个经纬度对之间的距离。
    • d3.interpolate 创建两个值之间的插值函数。
        let sample = function(line) {
           let a = line.geometry.coordinates[0];  // first point
           let b = line.geometry.coordinates[1];  // end point
    
           let distance = d3.geoDistance(a, b);   // in radians
           let precision = 1*Math.PI/180;         // sample every degree.
           let n = Math.ceil(distance/precision); // number of sample points
           let interpolate = d3.interpolate(a,b)  // create an interpolator
           let points = [];                       // sampled points.
           for(var i = 0; i <= n; i++) {          // sample n+1 times
             points.push([...interpolate(i/n)]);  // interpolate a point
           }
           line.geometry.coordinates = points;    // replace the points in the feature
        }
    

    以上假设一条线有两个点/一个线段,如果您的线比这更复杂,您自然需要调整它。它只是作为一个起点。

    在行动中:

    const width = 500;
    const height = 500;
    const scale = 200;
    
    const svg = d3.select('svg').attr("viewBox", [0, 0, width, height]);
    
    const projection = d3.geoStereographic().rotate([0, -90]).precision(0.1).clipAngle(90.01).scale(scale).translate([width / 2, height / 2]);
    const path = d3.geoPath(projection);
    
    const graticule = d3.geoGraticule().stepMajor([15, 15]).stepMinor([0, 0])();
    
    svg
      .append("path")
      .datum(graticule)
      .attr("d", path)
      .attr("fill", "none")
      .attr("stroke", '#000000')
      .attr("stroke-width", 0.3)
      .attr("stroke-opacity", 1);
    
    let curve = {
      "type": "Feature",
      "geometry": {
        "type": "LineString",
        "coordinates": [
          [-180, 15],
          [-90, 15]
        ]
      }
    }
    
    svg
      .append("path")
      .datum(curve)
      .attr("d", path)
      .attr('fill-opacity', 0)
      .attr('stroke', 'red')
      .attr("stroke-width", 1)
    
    
    let sample = function(line) {
       let a = line.geometry.coordinates[0];
       let b = line.geometry.coordinates[1];
       
       let distance = d3.geoDistance(a, b); // in radians
       let precision = 5*Math.PI/180;
       let n = Math.ceil(distance/precision);
       let interpolate = d3.interpolate(a,b)
       let points = [];
       for(var i = 0; i <= n; i++) {
         points.push([...interpolate(i/n)]);
       }
       line.geometry.coordinates = points;
    }
    
    sample(curve);
    
    
    svg
      .append("path")
      .datum(curve)
      .attr("d", path)
      .attr('fill-opacity', 0)
      .attr('stroke', 'blue')
      .attr("stroke-width", 1)
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>

    【讨论】:

      猜你喜欢
      • 2018-05-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-23
      • 2019-04-27
      • 1970-01-01
      • 2023-04-07
      • 1970-01-01
      相关资源
      最近更新 更多