d3.js - 如何在世界地图上准确地绘制半径以公里为单位的圆

标签 d3.js maps geo

我在大约以太平洋为中心的世界地图上使用 d3.geo.conicEquidistant() 投影,我的目标是从一点画圈,说明从 map 上的这一点到特定距离,比如 1000 公里。

给定特定的公里数,我如何计算投影的正确半径?

这是我的代码示例(是的,它是关于来自朝鲜的导弹。使用人为的平壤作为发射点:),基于这个问题:Scale a circle's radius (given in meters) to D3.js d3.geo.mercator map

<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
  stroke: white;
  stroke-width: 0.25px;
  fill: grey;
}
circle {
  stroke: red;
  stroke-width: 1px;
  stroke-dasharray: 5;
  fill: transparent;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
    height = 500;

var projection = d3.geo.conicEquidistant()
    .center([0, 5 ])
    .scale((width + 1) / 2 / Math.PI)//.scale(150)
    .rotate([-160,0]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var path = d3.geo.path()
    .projection(projection);

var g = svg.append("g");

var missiles = [
  {
    name: "missile1",
    location: { // appx lat&long of pyongyang
      latitude: 125.6720717, 
      longitude: 39.0292506
    },
    reach: 1000 //radius of circle in kilometers on map
  },
    {
    name: "missile2",
    location: { // appx lat&long of pyongyang
      latitude: 125.6720717, 
      longitude: 39.0292506
    },
    reach: 3500 //radius of circle in kilometers on map
  },
];


// load and display the World
d3.json("https://s3-us-west-2.amazonaws.com/vida-public/geo/world-topo-min.json", function(error, topology) {
    g.selectAll("path")
      .data(topojson.object(topology, topology.objects.countries)
          .geometries)
    .enter()
      .append("path")
      .attr("d", path)
});


svg.selectAll(".pin")
    .data(missiles)
  .enter().append("circle", ".pin")
    .attr("r", scaledRadius)
      /*function(d) {
      return d.reach
      })*/


    .attr("transform", function(d) {
      return "translate(" + projection([
        d.location.latitude,
        d.location.longitude
      ]) + ")"
    })

</script>
</body>
</html>

我查看了这个答案,在上面进行了尝试,但不太能够将其应用到我的代码中(老实说,我对 d3 很陌生,可能有一些非常明显的错误):
https://stackoverflow.com/a/31616927/4623519

进一步的问题:这个解决方案是否特定于墨卡托投影?

(让我们忽略我们确实需要在这个投影中隐藏南极洲。)

最佳答案

有两种方法可以实现这一点。

一个(更简单的选项)使用 d3 的 geoCircle 功能来创建地理循环特征:

var circle = d3.geoCircle().center([x,y]).radius(r);

为此,x 和 y 是以度为单位的中心点,而 r 是以度为单位的圆的半径。为了找到以米为单位的圆的半径,我们需要将米转换为度 - 如果我们假设地球是圆形的,这是最简单的(地球只是略呈椭圆形,因此这是一个高达 0.3% 的诱导误差,但即使使用椭球体,地球实际上更像马铃薯形状,因此也会产生误差)。使用 6,371 公里的平均半径,我们可以得到一个粗略的公式,例如:
var circumference = 6371000 * Math.PI * 2;

var angle = distance in meters / circumference * 360;

var circle = d3.geoCircle().center([x,y]).radius(angle);

这给了我们类似的东西:

var width = 500;
var height = 300;

var svg = d3.select("body")
  .append("svg")
  .attr("width",width)
  .attr("height",height);
  
var projection = d3.geoAlbers()
  .scale(200)
  .translate([width/2,height/2]);
  
var path = d3.geoPath().projection(projection);

var usa = {"type":"FeatureCollection", "features": [
{"type":"Feature","geometry":{"type":"MultiPolygon","coordinates":[[[[-94.81758,49.38905],[-88.378114,48.302918],[-82.550925,45.347517],[-82.439278,41.675105],[-71.50506,45.0082],[-69.237216,47.447781],[-66.96466,44.8097],[-70.11617,43.68405],[-70.64,41.475],[-73.982,40.628],[-75.72205,37.93705],[-75.72749,35.55074],[-81.49042,30.72999],[-80.056539,26.88],[-81.17213,25.20126],[-83.70959,29.93656],[-89.18049,30.31598],[-94.69,29.48],[-99.02,26.37],[-100.9576,29.38071],[-104.45697,29.57196],[-106.50759,31.75452],[-111.02361,31.33472],[-117.12776,32.53534],[-120.36778,34.44711],[-123.7272,38.95166],[-124.53284,42.76599],[-124.68721,48.184433],[-122.84,49],[-116.04818,49],[-107.05,49],[-100.65,49],[-94.81758,49.38905]]],[[[-155.06779,71.147776],[-140.985988,69.711998],[-140.99777,60.306397],[-148.018066,59.978329],[-157.72277,57.570001],[-166.121379,61.500019],[-164.562508,63.146378],[-168.11056,65.669997],[-161.908897,70.33333],[-155.06779,71.147776]]]]},"properties":{"name":"United States of America"},"id":"USA"}
]};

var circumference = 6371000 * Math.PI * 2;
var angle = 1000000 / circumference * 360;

var circle = d3.geoCircle().center([-100,40]).radius(angle);

svg.append("path")
      .attr("d",path(usa));

svg.append("path")
  .attr("d", path(circle()))
  .attr("fill","steelblue");
  
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>


另一种选择是从自定义函数而不是通过 d3 动态创建 geojson 功能。最简单的方法可能是取一个点,并计算相距 10 度的方位距 x 米的点(对于圆的 36 个点)。这需要使用起点、方位和距离来计算一个点,公式可以在这里找到。不久前,我使用这种方法构建了一个示例 tissot 的指标:Tissot's Indicatrix Bl.ock

关于d3.js - 如何在世界地图上准确地绘制半径以公里为单位的圆,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45421774/

相关文章:

javascript - d3 在附加的 div 之后附加空格

javascript - 我应该如何在 d3js 中使用嵌套数据

javascript - 增加 d3 SVG 容器大小

api - Last.fm geo.getEvents 返回无效方法 - 此包中没有具有该名称的方法

ios - iOS 中的城市搜索

javascript - 如何在 D3 Js 中将 y 轴 (yDomain) 值设置为最大值

reactjs - map.getCenter 和 map.getBounds 不是 map.target.on ('click' ) 函数内的函数

c++ - map 不能用作 C++ 中的数组样式打印吗?

java - 比较两个excel文件中的数据并在第三个文件中写入相应的映射

database - 有免费/付费的地理数据服务吗?