d3.js - force - ¿Cómo dibujar una línea/enlace entre dos puntos en un mapa D3 en función de la latitud/longitud?
d3 tooltip onmouseover (1)
Estoy intentando crear un mapa de las 10 instalaciones principales de la NASA en D3. He generado con éxito el mapa base de los Estados Unidos y adjuntado los logotipos de la NASA en cada una de las ubicaciones del centro en función de un .csv con latitud y longitud. Sin embargo, no puedo encontrar una manera elegante de dibujar líneas / enlaces / arcos / conexiones entre los puntos en el mapa.
En el código a continuación, dibujé una línea entre GSFC y KSC (usando ''var = lugares'', ''var = ruta'' y ''svg.append ("ruta")'') pero está en una capa SVG, así que se encuentra en la parte superior de los logotipos (que se ve horrible) y no se puede escalar (o desaparecería también estaría bien) al hacer clic para ampliar un estado. Me gustaría poder dibujar enlaces entre los centros basados en los datos de latitud y longitud de .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>
El archivo .csv está en el siguiente formato:
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
Modifiqué tu ejemplo ligeramente según los problemas que describiste: http://bl.ocks.org/erikhazzard/6201948
Parece que hay tres problemas:
Trayectorias dibujan sobre el icono . Para solucionar este problema, puede cambiar el orden de cuando agrega elementos al grupo o agregar subgrupos a su grupo
g
principal, asegurándose de que el orden en que agregue los grupos coincida con el orden en que desea que aparezcan las cosas.Las rutas entre los puntos no hacen zoom cuando se acerca el mapa . Para solucionar este problema, asegúrese de agregar todo al grupo que está modificando la función de clic (). En este caso, su grupo
g
se está ampliando, así que si agrega las rutas al grupog
lugar de a lasvg
directamente, las rutas también se acercarán. En el ejemplo proporcionado, el texto tampoco se acerca, porque se agrega directamente al SVG y no al grupog
que se está transformando.Las rutas no se crean automáticamente a partir de los datos . Para solucionar esto, puede generar una matriz que contenga objetos LineString a partir de los datos. Por ejemplo,
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 ] ] }); }
Luego, haga el patrón de unión de datos estándar y pase la lista de
links
a los datos. Cuando pasa enpath
como el atributod
, generará un gran arco basado en las coordenadas para cada elemento:// 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'' })
En mi ejemplo ( http://bl.ocks.org/enoex/6201948 ) agregué una transición en las grandes rutas de arco para ilustrar cómo se dibuja la ruta según el orden de los pares de coordenadas pasados al objeto de enlaces.
¡Espero que ayude!