d3 GeoJSON geoCircle椭圆等效

LBa*_*ish 3 javascript ellipse geojson d3.js cesium

标题基本概括了所有内容。我正在寻找一种生成geoJSON多边形的便捷方法,该多边形定义类似于d3-geo的d3.geoCircle()();椭圆。我想将此GeoJSON椭圆与d3-geo一起使用。为了阐明示例,Cesium具有此功能和简单的功能,可让您创建如下所示的椭圆:

var ellipse = new Cesium.EllipseGeometry({
  center : Cesium.Cartesian3.fromDegrees(-75.59777, 40.03883),
  semiMajorAxis : 500000.0,
  semiMinorAxis : 300000.0,
  rotation : Cesium.Math.toRadians(60.0)
});
Run Code Online (Sandbox Code Playgroud)

如果该函数返回了GeoJSON,则会被设置。生成定义椭圆的GeoJSON多边形的最佳方法是什么?

And*_*eid 5

D3没有提供任何可以真正帮助您的内容。Vanilla javascript可以很容易地实现这一目标。首先,让我们在笛卡尔坐标空间中创建geojson椭圆。之后,我们可以使用Haversine公式绘制椭圆。

  1. 在笛卡尔坐标空间中创建geojson椭圆。

这非常简单,我使用的方法是计算给定角度下的椭圆半径。使用这些极坐标,我们可以将椭圆缝合在一起。可以很容易地找到给定点处的椭圆半径的公式,我使用了这个,它为我们提供了:

在此处输入图片说明

因此,我们可以轻松地迭代一系列角度,计算该角度的半径,然后将该极坐标转换为笛卡尔坐标。也许像这样:

function createEllipse(a,b,x=0,y=0,rotation=0) {

  rotation = rotation / 180 * Math.PI;
  var n = n = Math.ceil(36 * (Math.max(a/b,b/a))); // n sampling angles, more for more elongated ellipses
  var coords = [];

  for (var i = 0; i <= n; i++) {
    // get the current angle
    var ? = Math.PI*2/n*i + rotation;

    // get the radius at that angle
    var r = a * b / Math.sqrt(a*a*Math.sin(?)*Math.sin(?) + b*b*Math.cos(?)*Math.cos(?));

    // get the x,y coordinate that marks the ellipse at this angle
    x1 = x + Math.cos(?-rotation) * r;
    y1 = y + Math.sin(?-rotation) * r;

    coords.push([x1,y1]);
  }

  // return a geojson object:
  return { "type":"Polygon", "coordinates":[coords] };

}
Run Code Online (Sandbox Code Playgroud)

注意:a / b:轴(以像素为单位),x / y:中心(以像素为单位),旋转:以度为单位的旋转

这是一个简短的片段:

function createEllipse(a,b,x=0,y=0,rotation=0) {

  rotation = rotation / 180 * Math.PI;
  var n = n = Math.ceil(36 * (Math.max(a/b,b/a))); // n sampling angles, more for more elongated ellipses
  var coords = [];

  for (var i = 0; i <= n; i++) {
    // get the current angle
    var ? = Math.PI*2/n*i + rotation;

    // get the radius at that angle
    var r = a * b / Math.sqrt(a*a*Math.sin(?)*Math.sin(?) + b*b*Math.cos(?)*Math.cos(?));

    // get the x,y coordinate that marks the ellipse at this angle
    x1 = x + Math.cos(?-rotation) * r;
    y1 = y + Math.sin(?-rotation) * r;

    coords.push([x1,y1]);
  }

  // return a geojson object:
  return { "type":"Polygon", "coordinates":[coords] };

}
Run Code Online (Sandbox Code Playgroud)
var geojson = createEllipse(250,50,200,200,45);

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

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


function createEllipse(a,b,x=0,y=0,rotation=0) {

	rotation = rotation / 180 * Math.PI;
	var n = n = Math.ceil(36 * (Math.max(a/b,b/a))); // n sample angles
	var coords = [];
	
	for (var i = 0; i <= n; i++) {
	    // get the current angle
		var ? = Math.PI*2/n*i + rotation;
		
		// get the radius at that angle
		var r = a * b / Math.sqrt(a*a*Math.sin(?)*Math.sin(?) + b*b*Math.cos(?)*Math.cos(?));
		
		// get the x,y coordinate that marks the ellipse at this angle
		x1 = x + Math.cos(?-rotation) * r;
		y1 = y + Math.sin(?-rotation) * r;

		coords.push([x1,y1]);
	}
	
	// return a geojson object:
	return { "type":"Polygon", "coordinates":[coords] };
	
}
Run Code Online (Sandbox Code Playgroud)

  1. 应用haversine公式。

我所知道的有关hasversine和相关功能的最佳资源之一是Moveable Type Scripts。我的配方几年前就来自那里,并且进行了一些化妆品修饰。我不会在这里分解公式,因为链接的引用应该很有用。

因此,我们可以采用极坐标,而不必计算笛卡尔坐标,而将它们的角作为方位角,将半径作为距离,在hasrsine公式中,这应该是相对微不足道的。

可能看起来像:

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Run Code Online (Sandbox Code Playgroud)
function createEllipse(a,b,x=0,y=0,rotation=0) {
	
	var k = Math.ceil(36 * (Math.max(a/b,b/a))); // sample angles
	var coords = [];
	
	for (var i = 0; i <= k; i++) {
	
		// get the current angle
		var angle = Math.PI*2 / k * i + rotation
		
		// get the radius at that angle
		var r = a * b / Math.sqrt(a*a*Math.sin(angle)*Math.sin(angle) + b*b*Math.cos(angle)*Math.cos(angle));

		coords.push(getLatLong([x,y],angle,r));
	}
	return { "type":"Polygon", "coordinates":[coords] };
}
 
function getLatLong(center,angle,radius) {
	
	var rEarth = 6371000; // meters
	
	x0 = center[0] * Math.PI / 180; // convert to radians.
	y0 = center[1] * Math.PI / 180;
	
	var y1 = Math.asin( Math.sin(y0)*Math.cos(radius/rEarth) + Math.cos(y0)*Math.sin(radius/rEarth)*Math.cos(angle) );
	var x1 = x0 + Math.atan2(Math.sin(angle)*Math.sin(radius/rEarth)*Math.cos(y0), Math.cos(radius/rEarth)-Math.sin(y0)*Math.sin(y1));
	
	y1 = y1 * 180 / Math.PI;
	x1 = x1	* 180 / Math.PI;
			
	return [x1,y1];
} 

// Create & Render the geojson:
var geojson = createEllipse(500000,1000000,50,70); // a,b in meters, x,y, rotation in degrees.
var geojson2 = createEllipse(500000,1000000)

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

var projection = d3.geoMercator().translate([300,200]).scale(600/Math.PI/2);

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

g.selectAll("path")
 .data([geojson,geojson2])
 .enter().append("path")
 .attr("d", path);
 
g.selectAll("circle")
  .data([[50,70],[0,0]])
  .enter().append("circle")
  .attr("cx", function(d) { return projection(d)[0] })
  .attr("cy", function(d) { return projection(d)[1] })
  .attr("r", 4)
  .attr("fill","orange");
Run Code Online (Sandbox Code Playgroud)

注意:a / b轴以米为单位,x,y,旋转以度为单位

那是一个非常无聊的演示,也许这个简单的演示会更好:

在此处输入图片说明

我使用的公式假定地球是一个球体,而不是一个椭球体,这可能导致距离误差高达0.3%。但是,取决于地图比例,该比例通常会小于笔划宽度。

我可能要尝试用这种方式制作一个特别具有视觉冲击力的天梭的版本

片段使用与IE不兼容的默认参数值,示例块提供了IE支持

  • 这只是使代码更短/更少麻烦的提示。默认参数在除Internet Explorer之外的所有浏览器上都可以使用...让我改一下:它在所有浏览器上都可以使用。 (2认同)