2013-08-10 21 views
17

Sto tentando di creare una mappa delle 10 principali strutture NASA in D3. Ho generato con successo la mappa degli Stati Uniti di base e ho aggiunto i loghi NASA in ognuna delle posizioni centrali in base a un .csv con latitudine e longitudine. Tuttavia, non riesco a trovare un modo elegante per disegnare linee/collegamenti/archi/connessioni tra i punti sulla mappa.Come disegnare una linea/collegamento tra due punti su una mappa D3 in base alla latitudine/longitudine?

Nel codice seguente, ho tracciato una linea tra GSFC e KSC (utilizzando 'var = places', 'var = route' e 'svg.append ("path")') ma è su un Livello SVG, quindi è in cima al logos (che sembra terribile) e non scala (o va via andrebbe bene anche) quando si fa clic per ingrandire uno stato. Vorrei poter disegnare collegamenti tra i centri in base ai dati di latitudine e longitudine di .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> 

Il file .csv è nel seguente 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 

risposta

25

ho modificato il vostro esempio un po 'sulla base dei problemi che hai descritto: http://bl.ocks.org/erikhazzard/6201948

Sembra che ci sono tre questioni:

  1. Percorsi disegnare in cima all'icona. Per risolvere questo problema, puoi modificare l'ordine di quando aggiungi elementi al gruppo o aggiungi sottogruppi al gruppo principale g, assicurandoti che l'ordine con cui aggiungi i gruppi corrisponda all'ordine in cui desideri vengano visualizzati.

  2. I percorsi tra i punti non effettuano lo zoom quando si ingrandisce la mappa. Per risolvere questo problema, assicurati di aggiungere tutto al gruppo che stai modificando la funzione clic su(). In questo caso, il tuo gruppo g viene ingrandito, quindi se aggiungi i percorsi al gruppo g invece che allo svg direttamente, anche i percorsi verranno ingranditi. Nell'esempio fornito, anche il testo non esegue lo zoom in avanti, poiché viene aggiunto direttamente all'SVG e non al gruppo g che viene trasformato.

  3. I percorsi non vengono creati automaticamente dai dati. Per risolvere questo problema, è possibile generare un array contenente oggetti LineString dai dati. Ad esempio,

    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 ] 
          ] 
         }); 
        } 
    

    Poi, fanno i dati standard uniscono modello e passano nella lista links ai dati. Quando si passa nel path come attributo d, verrà generato un grande arco in base alle coordinate per ogni 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' 
        }) 
    

nel mio esempio (http://bl.ocks.org/enoex/6201948) ho aggiunto una transizione sui percorsi grande arco a illustra come viene tracciato il percorso in base all'ordine delle coppie di coordinate passate nell'oggetto link.

Spero che questo aiuti!

+0

Questo è assolutamente geniale. Grazie! Seriamente, non posso ringraziarti abbastanza. Non solo ha risolto il problema per me, ma i tuoi commenti sono estremamente istruttivi e mi aiuteranno a capire come risolvere questi problemi in futuro. – Lokitez

+0

Does ... imageGroup.exit(). Remove() ... non funziona quando incluso all'interno della ... funzione cliccata (d) ...? – Lokitez

+0

potrebbe essere necessario chiamare exit() sull'oggetto di selezione ... ad esempio, var pathArcs = arcGroup.selectAll (". Arc") .data (collegamenti); ... pathArcs.exit(). Remove(); – ErikHazzard

Problemi correlati