Как нарисовать линию/ссылку между двумя точками на карте D3 на основе широты/долготы?
Я пытаюсь создать карту 10 основных объектов НАСА в D3. Я успешно создал базовую карту Соединенных Штатов и добавленные логотипы NASA в каждом из центральных мест на основе CSV с широтой и долготой. Тем не менее, я не могу найти элегантный способ рисовать линии/ссылки/дуги/соединения между точками на карте.
В приведенном ниже коде я нарисовал линию между GSFC и KSC (используя "var = places", "var = route" и "svg.append(" путь ")), но он находится на SVG слой, поэтому он находится поверх логотипов (что выглядит ужасно) и не масштабируется (или уйти тоже будет хорошо) при нажатии для увеличения масштаба. Я хотел бы иметь возможность рисовать связи между центрами на основе данных широты и долготы из .csv.
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.background {
fill: none;
pointer-events: all;
}
#states {
fill: #aaaaaa;
}
#states .active {
fill: #ff0000;
fill-opacity: .5;
}
#state-borders {
fill: none;
stroke: #ffffff;
stroke-width: 1.5px;
stroke-linejoin: round;
stroke-linecap: round;
pointer-events: none;
}
path.link {
fill: none;
stroke: #666666;
stroke-width: 1.5px;
}
.stroke {
fill: none;
stroke: #000;
stroke-width: 3px;
}
.fill {
fill: #fff;
}
.graticule {
fill: none;
stroke: #777;
stroke-width: .5px;
stroke-opacity: .5;
}
.route {
fill: none;
stroke: blue;
stroke-width: 3px;
}
</style>
<body>
<h2>
<span>NASA Centers</span>
</h2>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/d3.geo.projection.v0.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 1000,
height = 600,
centered;
var projection = d3.geo.albersUsa()
.scale(1070)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var graticule = d3.geo.graticule();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
var places = {
GSFC: [-76.852587, 38.991621],
KSC: [-80.650813, 28.524963]
};
var route = {
type: "LineString",
coordinates: [
places.GSFC,
places.KSC
]
};
var point = svg.append("g")
.attr("class", "points")
.selectAll("g")
.data(d3.entries(places))
.enter().append("g")
.attr("transform", function(d) { return "translate(" + projection(d.value) + ")"; });
point.append("text")
.attr("y", 5)
.attr("dx", "1em")
.text(function(d) { return d.key; });
d3.json("us.json", function(error, us) {
g.append("g")
.attr("id", "states")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.on("click", clicked);
g.append("path")
.datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
.attr("id", "state-borders")
.attr("d", path);
d3.csv("nasacenters.csv", function(error, data) {
g.selectAll("image").data([0])
.data(data)
.enter()
.append("image")
.attr("xlink:href", "nasalogo.png")
.attr("width", "30")
.attr("height", "30")
.attr("x", function(d) {
return projection([d.lon, d.lat])[0]-15;
})
.attr("y", function(d) {
return projection([d.lon, d.lat])[1]-15;
})
svg.append("path")
.datum(route)
.attr("class", "route")
.attr("d", path)
.style("opacity", 0.5);
});
});
function clicked(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
k = 4;
centered = d;
} else {
x = width / 2;
y = height / 2;
k = 1;
centered = null;
}
g.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
g.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
</script>
</body>
</html>
Файл .csv находится в следующем формате:
code,center,lat,lon
GSFC,Goddard Space Flight Center,38.991621,-76.852587
KSC,Kennedy Space Center,28.524963,-80.650813
JPL,Jet Propulsion Laboratory,34.200463,-118.176008
DFRC,Dryden Flight Research Center,34.613714,-118.076790
GRC,Glenn Research Center,41.415891,-81.861774
MSFC,Marshall Space Flight Center,34.646554,-86.674368
ARC,Ames Research Center,37.409574,-122.064292
LaRC,Langley Research Center,37.092123,-76.376230
JSC,Johnson Space Center,29.551508,-95.092256
SSC,Stennis Space Center,30.363692,-89.600036
Ответы
Ответ 1
Я немного изменил ваш пример в зависимости от описанных проблем: http://bl.ocks.org/erikhazzard/6201948
Похоже, есть три проблемы:
-
Контуры рисуются поверх значка. Чтобы исправить это, вы можете изменить порядок добавления элементов в группу или добавить подгруппы в свою основную группу g
, гарантируя, что порядок добавления групп будет соответствовать порядку, в котором вы хотите, чтобы все отображалось.
-
Пути между точками не масштабируются при масштабировании карты. Чтобы исправить это, обязательно добавьте все в группу, в которую вы изменяете функцию clicked(). В этом случае ваша группа g
будет увеличена, поэтому, если вы добавите пути к группе g
, а не к svg
, то также будут увеличиваться и пути. В приведенном примере текст также не увеличивается - это потому, что он добавляется непосредственно в SVG, а не в группу g
, которая преобразуется.
-
Контуры не создаются автоматически из данных. Чтобы исправить это, вы можете сгенерировать массив, содержащий объекты LineString, из данных. Например,
for(var i=0, len=data.length-1; i<len; i++){
// (note: loop until length - 1 since we're getting the next
// item with i+1)
links.push({
type: "LineString",
coordinates: [
[ data[i].lon, data[i].lat ],
[ data[i+1].lon, data[i+1].lat ]
]
});
}
Затем выполните стандартный шаблон соединения данных и перейдите в список links
к данным. Когда вы передаете path
в качестве атрибута d
, он будет генерировать большую дугу, основанную на координатах для каждого элемента:
// Standard enter / update
var pathArcs = arcGroup.selectAll(".arc")
.data(links);
//enter
pathArcs.enter()
.append("path").attr({
'class': 'arc'
}).style({
fill: 'none',
});
//update
pathArcs.attr({
//d is the points attribute for this path, we'll draw
// an arc between the points using the arc function
d: path
})
.style({
stroke: '#0000ff',
'stroke-width': '2px'
})
В моем примере (http://bl.ocks.org/enoex/6201948) Я добавил переход на большие дуговые пути, чтобы проиллюстрировать, как путь рисуется на основе порядка пар координат, переданных в объект связей.
Надеюсь, что это поможет!