如何绘制在给定的公里上精确的世界地图半径的圆

问题描述:

我使用的是世界地图大致位于太平洋上的d3.geo.conicEquidistant()的投影,我的目标是从点画圈圈,说明从地图上的这个点开始,一个特定的距离,例如1000km。如何绘制在给定的公里上精确的世界地图半径的圆

我如何计算上的投影正确的半径,给出具体的公里?

这里是我的代码示例(是的,它是关于导弹从朝鲜到达使用人工平壤为出发点:),建立在这个问题: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

进一步的问题:是该解决方案专门针对墨卡托投影?

(让我们忽略了,我们真的需要隐藏在南极洲这一预测。)

+0

你能链接我们的工作代码,或jsfiddle这个莫名其妙? – barrycarter

+0

这是一个jfiddle,我用Andrew的答案更改我的代码后:https://jsfiddle.net/y8qn8tr1/ - 出于某种原因,世界地图路径不显示在小提琴中... – Julius

+0

尝试https://而不是http://访问geojson时:https://jsfiddle.net/38he3mt9/ –

有两种方法来实现这一目标。

之一(更容易的选择)使用D3的geoCircle功能来创建一个地理上圆形特征:

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

对于这一点,x和y是在度的中心点,r是圆的半径度。要查找米的圆的半径,我们需要米转换为度 - 如果我们假设一个圆形地球(地球上只有轻微椭圆形这是最简单的,所以这是高达0.3%的感应误差,但即使使用椭球体,地球真的更加马铃薯形,所以也会导致错误)。使用6371公里平均半径,我们可以得到一个粗略的公式,如:

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>

另一种选择是创建一个GeoJSON的功能从自定义函数而不是通过d3。最简单的方法可以是把一个点,并计算为x米远在间隔开的10度轴承点(36点到圆)。这需要使用起点,方位和距离来计算一个点,公式可以在这里找到。 Tissot's Indicatrix Bl.ock:前段时间我用这个方法建立了一个例子天梭的指示线。

+0

伟大的安德鲁,谢谢!这非常有帮助。我将代码更改为d3 v4,并且能够像我希望使用第一个更简单的方法一样绘制圆。这似乎工作,无论地图投影? 6371公里从哪里来?我将在这里将我的编辑代码作为anser。 – Julius

+0

我使用了维基百科提供的平均地球半径,尽管您可以对地球的主轴和次轴(或任何其他基准)使用WGS84规范,并将两者的平均值作为相当接近的地球半径。 –

建立在安德鲁的答案,这是我如何解决我的问题:

新增的造型,改变了我的代码,以D3 V4:

</!DOCTYPE html> 
<html> 
<head> 
    <title></title> 
    <style> 
     path { 
     stroke: white; 
     stroke-width: 0.25px; 
     fill: grey; 
    } 
    .circle { 
     stroke: red; 
     stroke-width: 1px; 
     stroke-dasharray: 5; 
     fill: transparent; 
    } 
    </style> 
</head> 
<body> 
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script> 

D3的脚本:

var width = 1200; 
var height = 400; 

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

使用圆锥等距投影:

var projection = d3.geoConicEquidistant() 
    .center([0, 20]) 
    .scale((width + 1)/2/Math.PI) 
    .rotate([-140,0]); 

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

从安德鲁的回答中创建圈子,其中1000公里半径为3500公里。这些可以更好地排列成我猜想的列表/对象。

var circumference = 6371000 * Math.PI * 2; 

var angle1 = 1000000/circumference * 360; 
var missile1 = d3.geoCircle().center([125.6720717,39.0292506]).radius(angle1); 

var angle2 = 3500000/circumference * 360; 
var missile2 = d3.geoCircle().center([125.6720717,39.0292506]).radius(angle2); 

加载和显示世界:

var url = "http://enjalot.github.io/wwsd/data/world/world-110m.geojson"; 
//display the world 
    d3.json(url, function(err, geojson) { 
     svg.append("path") 
     .attr("d", path(geojson)); 
//append the circles 
     svg.append("path") 
     .attr("d", path(missile1())) 
     .attr("class","circle"); 
     svg.append("path") 
     .attr("d", path(missile2())) 
     .attr("class","circle"); 
    }); 

编辑:添加HTTPS的JSON文件,并修改代码以使这个圆的世界地图上面绘制。我有一个工作jfiddle在这里:https://jsfiddle.net/y8qn8tr1/2/

+0

答案中的问题通常得不到答复 - 仅限于评论,并且受众有限。在小提琴中尝试'https://'geojson,并且在绘制其他所有内容之后会绘制世界 - d3.json是异步的,可以在回调中附加圆或者使用以指定顺序附加的g元素(在d3.json回调之外)并向这些g元素添加特征(从而对它们进行排序)。 –

+0

再次感谢!我用https更新了jfiddle,现在它可以工作。这是我的第一个问题,仍然试图弄清后续问题的做法是什么,我有点不确定我是否应该发布一个新答案或编辑原始答案,并将新问题作为评论。也许编辑原创更好?但是这也许与我原来的问题无关。 – Julius

+2

如果您有新的/不同的问题,请单独提问。如果你认为它会添加上下文,你可以随时链接到这个问题。 –