D3 не предлагает ничего, что может действительно помочь здесь.Vanilla Javascript может достичь этого довольно легко.Сначала давайте создадим эллипс геойсона в декартовом координатном пространстве.После этого мы можем использовать формулу haversine для рисования эллипса.
- Создать эллипс геоджона в декартовом координатном пространстве.
Это довольно простой метод, который я использую.используется для расчета радиуса эллипса под заданным углом.Используя эти полярные координаты, мы можем соединить эллипс.Формула для радиуса эллипса в данной точке может быть найдена довольно легко, я использовал этот источник , который дает нам:
Итак, мы можем легко перебрать серию углов, вычислить радиус под этим углом, а затем преобразовать эту полярную координату в декартову.Возможно что-то вроде:
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, поворот в градусах
Это довольно скучнодемонстрация, возможно эта простая демонстрация лучше:
Формула, которую я использую, предполагает, что землясфера, а не эллипсоид, это может привести к ошибкам на расстоянии до 0,3%.Однако, в зависимости от масштаба карты, это часто будет меньше ширины штриха.
Возможно, мне придется попытаться сделать особенно визуально сложную версию индикатрисы салфетки с помощью этой
Фрагменты используют значения параметров по умолчанию, которые не совместимы с IE, пример блока предлагает поддержку IE