Я изо всех сил пытаюсь найти какой-либо пример глобуса D3 (или Globe-gl и т. д.), в котором в качестве слоя меток есть названия стран. Я вообще ищу эту демоверсию
https://observablehq.com/@michael-keith/draggable-globe-in-d3
но с названиями стран (в идеале в географическом центре страны). Конечно, они должны масштабироваться вместе с глобусом. Ни один из 37 форков демо-версии мира, похоже, не добавил этого. Я был бы признателен за любые указания.
Я добавил ручку на случай, если так будет проще:
<https://codepen.io/alhuber1502/pen/YzbGPMB>
Большое спасибо!
По сути, решение по-прежнему svg.selectAll(null).data(features).enter().append("text").attr("d", path.centroid)...
Затем вам нужно применить преобразование масштабирования/поворот проекции так же, как вы делаете для остальной части земного шара.
Спасибо, Андрей! Я добавил ручку на случай, если вам будет проще указать: codepen.io/alhuber1502/pen/YzbGPMB Я просмотрел как связанный вопрос, на который вы ссылались, так и предложенное вами решение, но у меня возникли проблемы с получением меток. показывать. Я задавался вопросом, можно ли использовать тот же механизм, что и для меток добавляются мировые границы, будет ли это работать? Спасибо!
@AndrewReid У меня есть метки для показа codepen.io/alhuber1502/pen/YzbGPMB, к сожалению, они не вращаются, не масштабируются и не проецируются должным образом. Я на правильном пути? Любая помощь принимается с благодарностью!
В завершение вы обновляете контуры страны с помощью svg.selectAll("path").attr("d", path);
даже при перетаскивании, но не текст, вам нужно применить ту же логику, которую вы используете для первоначального размещения меток при перетаскивании. Я мог бы взглянуть повнимательнее сегодня вечером
Чтобы разместить текст на глобусе, вы используете метод пути path.centroid. Это возвращает центр пути (как нарисовано, а не географически). Это можно использовать для размещения текста следующим образом:
let text = svg.selectAll(null)
.data(data)
.enter()
.append("text")
.attr("transform", function(d) { return "translate("+path.centroid(d)+")"});
Однако это не работает для объектов, у которых нет видимого пути (т. е. объектов на дальней стороне Земли, которые не визуализируются). Для этого нам нужно сделать проверку, иначе текст появится по адресу [0,0]:
.style("opacity", function(d) {
if (path(d)) return 1; else return 0;
})
Это проверяет, созданы ли какие-либо данные пути генератором пути, если нет, мы скрываем текст. Если данные пути не созданы, объект не виден и не должен быть помечен.
Мы обновляем их при каждом событии ротации:
text.attr("transform", function(d) { return "translate("+path.centroid(d)+")"})
.style("opacity", function(d) {
if (path(d)) return 1; else return 0;
})
Нам не нужно обновлять стиль при каждом масштабировании, поскольку мы не вращаем Землю (и, следовательно, не скрываем/открываем новые пути).
Я взял ваш пример (обновил его до версии 7 для d3.json) и применил вышеуказанные изменения.
d3.json("https://static.observableusercontent.com/files/cbb0b433d100be8f4c48e19de6f0702d83c76df3def6897d7a4ccdb48d2f5f039bc3ae1141dd1005c253ca13c506f5824ae294f4549e5af914d0e3cb467bd8b0?response-content-disposition=attachment%3Bfilename*%3DUTF-8%27%27world.json").then(function(data) {
let width = 500;
let height = 500;
const sensitivity = 75;
let projection = d3
.geoOrthographic()
.scale(250)
.center([0, 0])
.rotate([0, -30])
.translate([width / 2, height / 2]);
const initialScale = projection.scale();
let path = d3.geoPath().projection(projection);
let svg = d3
.select("svg")
.attr("width", width)
.attr("height", height);
let globe = svg
.append("circle")
.attr("fill", "#EEE")
.attr("stroke", "#000")
.attr("stroke-width", "0.2")
.attr("cx", width / 2)
.attr("cy", height / 2)
.attr("r", initialScale);
let map = svg.append("g");
map
.append("g")
.attr("class", "countries")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("class", (d) => "country_" + d.properties.name.replace(" ", "_"))
.attr("d", path)
.attr("fill", "white")
.style("stroke", "black")
.style("stroke-width", 0.3)
.style("opacity", 0.8);
let text = map
.append("g")
.selectAll("text")
.data(data.features)
.enter()
.append("text")
.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.text(function(d) { return d.properties.name; })
.style("opacity", function(d) {
if (path(d)) return 1; else return 0;
})
svg
.call(
d3.drag().on("drag", () => {
const rotate = projection.rotate();
const k = sensitivity / projection.scale();
projection.rotate([
rotate[0] + d3.event.dx * k,
rotate[1] - d3.event.dy * k
]);
path = d3.geoPath().projection(projection);
svg.selectAll("path").attr("d", path);
text.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
.style("opacity", function(d) {
if (path(d)) return 1; else return 0;
})
})
)
.call(
d3.zoom().on("zoom", () => {
if (d3.event.transform.k > 0.3) {
projection.scale(initialScale * d3.event.transform.k);
path = d3.geoPath().projection(projection);
svg.selectAll("path").attr("d", path);
globe.attr("r", projection.scale());
text.attr("transform", function(d) { return "translate(" + path.centroid(d) + ")"; })
} else {
d3.event.transform.k = 0.3;
}
})
);
function placeText(d, projection, text) {
d3.select(this)
.attr("opacity", path(d) == NULL ? 1 : 0)
.attr("transform", "translate(" + path.centroid(d) + ")");
}
})
<script src = "https://cdnjs.cloudflare.com/ajax/libs/d3/5.9.0/d3.min.js"></script>
<svg></svg>