Предполагая, что вы использовали силу, чтобы вы могли добавлять точки и ссылки, давайте немного отступим назад, давайте отбросим все, что связано с силой, без узлов и без ссылок.Расположение сил не является необходимым ни в этой ситуации.Давайте начнем с вашего земного шара с анимации и перетаскивания (и перейдем к d3v5, пока мы на нем):
var width = 500,
height = 500,
t0 = Date.now(),
velocity = [0.01, 0],
origin = [0, -45];
var projection = d3.geoOrthographic()
.scale(height/2.1)
.translate([width/2, height/2])
.clipAngle(90)
var path = d3.geoPath()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then(function(topo) {
var land = topojson.feature(topo, topo.objects.land);
svg.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path);
d3.timer(function() {
var dt = Date.now() - t0;
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]);
svg.selectAll("path")
.attr("d", path);
});
});
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
Кроме перехода на v5, я сделал несколько небольших изменений, чтобы оптимизировать просмотр фрагмента (например, размер) или краткость (например, жесткое кодирование клипа).угол), но код по сути тот же минус сила / узлы / ссылки
Я думаю, что проверяет половину вашего первого требования «удалить макет силы, но сохранить узлы / ссылки».Это также дает нам более простой код, с которым можно работать для удовлетворения остальных требований.
Хорошо, теперь, когда у нас есть базовая карта, мы можем добавлять точки, а затем мы можем добавлять линии.Но давайте разберемся с этим, сначала мы добавим точки, затем добавим ссылки.
Добавление точек с географической привязкой
Давайте возьмем формат данных, я пойдус помощью словаря точек / узлов, которые мы хотим показать:
var points = {
"Vancouver":[-123,49.25],
"Tokyo":[139.73,35.68],
"Honolulu":[-157.86,21.3],
"London":[0,50.5],
"Kampala":[32.58,0.3]
}
Поскольку мы имеем дело с ортогональной проекцией, целесообразно использовать точки геоджона с d3.geoPath, так как это автоматически обрежет те точки, которыенаходятся на противоположной стороне земного шара.Точка геойсона выглядит следующим образом (как вы создали в своей скрипке):
{ type: "Point", geometry: [long,lat] }
Итак, мы можем получить массив точек геойсона с:
var geojsonPoints = d3.entries(points).map(function(d) {
return {type: "Point", coordinates: d.value}
})
d3.entries возвращает массив при кормлении объекта.Каждый элемент в массиве представляет пару значений ключа исходного объекта {key: key, value: value}
, для получения дополнительной информации см. документы
Теперь мы можем добавить наши точки геоджона в svg:
svg.selectAll()
.data(geojsonPoints)
.enter()
.append("path")
.attr("d",path)
.attr("fill","white")
.attr("stroke-width",2)
.attr("stroke","steelblue");
И так как это точки, нам нужно установить радиус точки пути:
var path = d3.geoPath()
.projection(projection)
.pointRadius(5);
Наконец, так как я удалил фильтр, который вы применили в функции таймера, все путибудет обновляться вместе при каждом повороте, что немного упрощает код.
Хорошо, в целом, это дает нам:
var width = 500,
height = 500,
t0 = Date.now(),
velocity = [0.01, 0],
origin = [0, -45];
var points = {
"Vancouver":[-123,49.25],
"Tokyo":[139.73,35.68],
"Honolulu":[-157.86,21.3],
"London":[0,50.5],
"Kampala":[32.58,0.3]
}
var projection = d3.geoOrthographic()
.scale(height/2.1)
.translate([width/2, height/2])
.clipAngle(90)
var path = d3.geoPath()
.projection(projection)
.pointRadius(5);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then(function(topo) {
var land = topojson.feature(topo, topo.objects.land);
svg.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path);
var geojsonPoints = d3.entries(points).map(function(d) {
return {type: "Point", coordinates: d.value}
});
svg.selectAll(null)
.data(geojsonPoints)
.enter()
.append("path")
.attr("d",path)
.attr("fill","white")
.attr("stroke-width",2)
.attr("stroke","steelblue");
d3.timer(function() {
var dt = Date.now() - t0;
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]);
svg.selectAll("path")
.attr("d", path);
});
});
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
Мы могли бы добавлять круги, но это создает новую проблему: нам нужно проверить, должен ли каждый круг быть видимым при каждом движенииГлобус, увидев, если угол между текущим центром вращения и точкой больше, чем 90 градусов.Поэтому для простоты я использовал геоджон и использовал проекцию и путь, чтобы скрыть эти точки на противоположной стороне земного шара.
Пути
Причина, по которой я предпочитаю приведенный выше формат для точек, заключается в том, что он дает нам удобочитаемый список ссылок:
var links = [
{ source: "Vancouver", target: "Tokyo" },
{ source: "Tokyo", target: "Honolulu" },
{ source: "Honolulu", target: "Vancouver" },
{ source: "Tokyo", target: "London" },
{ source: "London", target: "Kampala" }
]
Теперь, как и выше, нам нужно преобразовать его в геойсон.Линия геойсона выглядит так (как вы создали в скрипте):
{type:"LineString", coordinates: [[long,lat],[long,lat], ... ]
Итак, мы можем создать массив линий геоджона с:
var geojsonLinks = links.map(function(d) {
return {type: "LineString", coordinates: [points[d.source],points[d.target]] }
})
Это использует преимуществасловарная структура данных для точек.
Теперь вы можете добавлять их следующим образом:
svg.selectAll(null)
.data(geojsonLinks)
.enter()
.append("path")
.attr("d", path)
.attr("stroke-width", 2)
.attr("stroke", "steelblue")
.attr("fill","none")
Как и в случае точек, они обновляются каждый тик таймера:
var width = 500,
height = 500,
t0 = Date.now(),
velocity = [0.01, 0],
origin = [0, -45];
var points = {
"Vancouver":[-123,49.25],
"Tokyo":[139.73,35.68],
"Honolulu":[-157.86,21.3],
"London":[0,50.5],
"Kampala":[32.58,0.3]
}
var links = [
{ source: "Vancouver",target: "Tokyo" },
{ source: "Tokyo", target: "Honolulu" },
{ source: "Honolulu", target: "Vancouver" },
{ source: "Tokyo", target: "London" },
{ source: "London", target: "Kampala" }
]
var projection = d3.geoOrthographic()
.scale(height/2.1)
.translate([width/2, height/2])
.clipAngle(90)
var path = d3.geoPath()
.projection(projection)
.pointRadius(5);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.call(d3.drag()
.subject(function() { var r = projection.rotate(); return {x: 2 * r[0], y: -2 * r[1]}; })
.on("drag", function() { var r = [d3.event.x / 2, -d3.event.y / 2, projection.rotate()[2]]; t0 = Date.now(); origin = r; projection.rotate(r); }))
d3.json("https://unpkg.com/world-atlas@1/world/110m.json").then(function(topo) {
var land = topojson.feature(topo, topo.objects.land);
svg.append("path")
.datum(land)
.attr("class", "land")
.attr("d", path);
var geojsonPoints = d3.entries(points).map(function(d) {
return {type: "Point", coordinates: d.value}
});
var geojsonLinks = links.map(function(d) {
return {type: "LineString", coordinates: [points[d.source],points[d.target]] }
})
svg.selectAll(null)
.data(geojsonLinks)
.enter()
.append("path")
.attr("d",path)
.attr("fill","none")
.attr("stroke-width",2)
.attr("stroke","steelblue");
svg.selectAll(null)
.data(geojsonPoints)
.enter()
.append("path")
.attr("d",path)
.attr("fill","white")
.attr("stroke-width",2)
.attr("stroke","steelblue");
d3.timer(function() {
var dt = Date.now() - t0;
projection.rotate([velocity[0] * dt + origin[0], velocity[1] * dt + origin[1]]);
svg.selectAll("path")
.attr("d", path);
});
});
<script type="text/javascript" src="https://d3js.org/d3.v5.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
Имейте в виду, что разбиение путей SVG осуществляется в порядке их добавления - первый добавленный будет позади второго добавленного.Поэтому, если вы хотите, чтобы ссылки были поверх точек, просто поменяйте местами порядок их добавления.Вы также можете использовать группы g
для управления заказами - g
тоже многоуровневые.