d3 GeoJSON эквивалент эллипса geoCircle - PullRequest
0 голосов
/ 30 мая 2018

Название в значительной степени говорит само за себя.Я ищу удобный способ создания многоугольника geoJSON, определяющего эллипс, подобный d3-geo d3.geoCircle()();. Я хочу использовать этот эллипс GeoJSON с d3-geo.Для пояснения и примера, у Цезия есть эта возможность с простой функцией, позволяющей создать эллипс, подобный следующему:

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)
});

Если бы эта функция вернула GeoJSON, я бы установил.Каков наилучший способ создания полигона GeoJSON, определяющего эллипс?

1 Ответ

0 голосов
/ 30 мая 2018

D3 не предлагает ничего, что может действительно помочь здесь.Vanilla Javascript может достичь этого довольно легко.Сначала давайте создадим эллипс геойсона в декартовом координатном пространстве.После этого мы можем использовать формулу haversine для рисования эллипса.

  1. Создать эллипс геоджона в декартовом координатном пространстве.

Это довольно простой метод, который я использую.используется для расчета радиуса эллипса под заданным углом.Используя эти полярные координаты, мы можем соединить эллипс.Формула для радиуса эллипса в данной точке может быть найдена довольно легко, я использовал этот источник , который дает нам:

enter image description here

Итак, мы можем легко перебрать серию углов, вычислить радиус под этим углом, а затем преобразовать эту полярную координату в декартову.Возможно что-то вроде:

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] };

}

Примечание: a / b: оси (в пикселях), x / y: центр (в пикселях), вращение: вращение в градусах

Вот что в быстром фрагменте:

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] };
	
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
Применение формулы haversine.

Один из лучших ресурсов по haversine и связанным с ним функциям, о которых я знаю, это Скрипты подвижного типа .Формула, которую я получил оттуда несколько лет назад, имела несколько косметических модификаций.Я не собираюсь разбивать формулу здесь, так как связанная ссылка должна быть полезной.

Таким образом, вместо вычисления декартовых координат, мы можем взять полярную координату и использовать угол в качестве ориентира и радиускак расстояние в формуле haversine, который должен быть относительно тривиальным.

Это может выглядеть так:

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");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

Примечание: оси a / b в метрах, x, y, поворот в градусах

Это довольно скучнодемонстрация, возможно эта простая демонстрация лучше:

enter image description here

Формула, которую я использую, предполагает, что землясфера, а не эллипсоид, это может привести к ошибкам на расстоянии до 0,3%.Однако, в зависимости от масштаба карты, это часто будет меньше ширины штриха.

Возможно, мне придется попытаться сделать особенно визуально сложную версию индикатрисы салфетки с помощью этой

Фрагменты используют значения параметров по умолчанию, которые не совместимы с IE, пример блока предлагает поддержку IE

...