#javascript #html #jquery #d3.js
#javascript #HTML #jquery #d3.js
Вопрос:
Я изменил этот пример d3, чтобы динамически добавлять места / точки и красную линию ПОСЛЕ рисования глобуса. (По сути, я хочу добавлять данные динамически, а не начинать с фиксированного набора точек, как в примере). Красная пунктирная линия рисуется, но, когда глобус вращается, линии остаются в том же положении. Как мне заставить мои строки вращаться вместе с глобусом, как в примере? Моя скрипка: https://jsfiddle.net/6qhvt8aL/2 /
var width = 960,
height = 500;
var proj = d3.geoOrthographic().scale(230).translate([width / 2, height / 2]).clipAngle(90);
var path = d3.geoPath().projection(proj).pointRadius(1.5);
var graticule = d3.geoGraticule();
var london = [-0.118667702475932, 51.5019405883275];
var time = Date.now();
var rotate = [39.666666666666664, -30];
var velocity = [.015, -0];
var lineToLondon = function(d) {
return path({
"type": "LineString",
"coordinates": [london, d.geometry.coordinates]
});
}
function stripWhitespace(str) {
if (!str) {
return "";
}
return str.replace(" ", "");
}
var svg = d3.select("body").append("svg").attr("width", width).attr("height", height)
svg.call(d3.drag().on("start", dragstarted).on("drag", dragged));
queue().defer(d3.json, "https://openlayers.org/en/latest/examples/data/topojson/world-110m.json").defer(d3.json, "/static/destinations.json").await(ready);
var places = {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"properties": {
"name": "San Francisco"
},
"geometry": {
"type": "Point",
"coordinates": [-122.417168773552248, 37.769195629687431]
}
}, {
"type": "Feature",
"properties": {
"name": "Chicago"
},
"geometry": {
"type": "Point",
"coordinates": [-87.752000832709314, 41.831936519278429]
}
}, {
"type": "Feature",
"properties": {
"name": "Los Angeles"
},
"geometry": {
"type": "Point",
"coordinates": [-118.243683, 34.052235]
}
}, {
"type": "Feature",
"properties": {
"name": "Vancouver"
},
"geometry": {
"type": "Point",
"coordinates": [-123.116226, 49.246292]
}
}, {
"type": "Feature",
"properties": {
"name": "Calgary"
},
"geometry": {
"type": "Point",
"coordinates": [-114.062019, 51.044270]
}
}, {
"type": "Feature",
"properties": {
"name": "Barcelona"
},
"geometry": {
"type": "Point",
"coordinates": [2.154007, 41.390205]
}
}, {
"type": "Feature",
"properties": {
"name": "Berlin"
},
"geometry": {
"type": "Point",
"coordinates": [13.404954, 52.520008]
}
}, {
"type": "Feature",
"properties": {
"name": "Paris"
},
"geometry": {
"type": "Point",
"coordinates": [2.349014, 48.864716]
}
}, {
"type": "Feature",
"properties": {
"name": "Cologne"
},
"geometry": {
"type": "Point",
"coordinates": [6.953101, 50.935173]
}
}, {
"type": "Feature",
"properties": {
"name": "Geneva"
},
"geometry": {
"type": "Point",
"coordinates": [6.143158, 46.204391]
}
}, {
"type": "Feature",
"properties": {
"name": "Fethiye"
},
"geometry": {
"type": "Point",
"coordinates": [29.1263, 36.6592]
}
}, {
"type": "Feature",
"properties": {
"name": "Edinburgh"
},
"geometry": {
"type": "Point",
"coordinates": [-3.188267, 55.953251]
}
}]
};
addLocations(places);
function addLocations(places) {
svg.append("g").attr("class", "points")
.selectAll("text").data(places.features)
.enter().append("path")
.attr("class", "point")
.attr("d", path);
svg.append("g").attr("class", "lines")
.selectAll(".lines").data(places.features)
.enter().append("path")
.attr("class", "lines")
.attr("id", d => stripWhitespace(d.properties.name))
.attr("d", d => lineToLondon(d));
refresh();
svg.selectAll(".lines").attr("d", (d) => {
if (d) {
return lineToLondon(d);
}
});
}
function ready(error, world, places) {
$.getJSON("https://openlayers.org/en/latest/examples/data/topojson/world-110m.json", function(world) {
svg.append("circle").attr("cx", width / 2).attr("cy", height / 2).attr("r", proj.scale()).attr("class", "noclicks").attr("fill", "none");
svg.append("path").datum(topojson.object(world, world.objects.land)).attr("class", "land").attr("d", path);
svg.append("path").datum(graticule).attr("class", "graticule noclicks").attr("d", path);
svg.append("g").attr("class", "countries").selectAll("path").data(topojson.object(world, world.objects.countries).geometries).enter().append("path").attr("d", path);
refresh();
spin();
});
}
function refresh() {
svg.selectAll(".land").attr("d", path);
svg.selectAll(".countries path").attr("d", path);
svg.selectAll(".graticule").attr("d", path);
svg.selectAll(".point").attr("d", path);
}
var timer;
function spin() {
timer = d3.timer(function() {
var dt = Date.now() - time;
proj.rotate([rotate[0] velocity[0] * dt, rotate[1] velocity[1] * dt]);
refresh();
});
}
function dragstarted() {
timer.stop();
v0 = versor.cartesian(proj.invert(d3.mouse(this)));
r0 = proj.rotate();
q0 = versor(r0);
}
function dragged() {
var v1 = versor.cartesian(proj.rotate(r0).invert(d3.mouse(this))),
q1 = versor.multiply(q0, versor.delta(v0, v1)),
r1 = versor.rotation(q1);
proj.rotate(r1);
refresh();
}
Комментарии:
1. Почему бы не обновлять путь к линиям каждый раз, когда вы обновляете все другие функции в функции обновления:
svg.selectAll("path.lines").attr("d",lineToLondon);
?
Ответ №1:
Вы обновляете все в функции обновления, кроме строк, вам просто нужно их обновить.
Вы не можете использовать svg.selectAll(".lines").attr("d", lineToLondon)
, потому что у вас есть g
класс with lines
, содержащий все пути с этим классом. Вместо этого вы можете использовать:
svg.selectAll("path.lines").attr("d", lineToLondon);
Следует отметить, что вам не нужны эти строки в вашем коде:
svg.selectAll(".lines").attr("d", (d) => {
if (d) {
return lineToLondon(d);
}
});
Вот обновленная скрипка с этими изменениями.
Комментарии:
1. О, круто! Последний вопрос: как мне сделать так
lineToLondon
, чтобы он принимал 2 аргумента: источник и назначение. Прямо сейчас он принимает один аргументd
, но источник не всегда будет London…it это может быть Париж, Техас или что-то еще. Еще раз спасибо!2. Вы бы структурировали свои данные по-другому, например, две точки вместо одной? Как бы вы определили другую точку? Однако это другой вопрос — в зависимости от вашей структуры данных ваш подход будет отличаться, если вы опубликуете новый вопрос по этой теме, я добавлю ответ (если кто-нибудь не опередит меня с хорошим ответом).