Come disegnare una linea / link tra due punti su una D3 mappa basata sulla latitudine / longitudine?

Sto tentando di creare una mappa delle 10 principali NASA strutture in D3. Ho generato la base, Stati Uniti, mappa e aggiunto la NASA loghi a ciascuno, il centro località basato su una .csv con le coordinate di latitudine e longitudine. Tuttavia, non riesco a capire qualsiasi modo elegante per disegnare linee /links /archi /connessioni tra i punti sulla mappa.

Nel codice qui di seguito, ho tracciato una linea tra GSFC e KSC (utilizzando il ‘var = luoghi’, ‘var =’ itinerario e ‘svg.append(“percorso”)’) ma è su un file SVG livello, quindi è sulla parte superiore del logo (che è orribile) e non si adatta (o andare via sarebbe un bene, troppo) quando si clicca per ingrandire uno stato. Vorrei essere in grado di disegnare i collegamenti tra i centri basato sulla latitudine e longitudine di dati dal .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>

L’ .file csv formato è il seguente:

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
InformationsquelleAutor Lokitez | 2013-08-10



One Reply
  1. 26

    Ho modificato il tuo esempio è leggermente in base ai problemi che avete descritto: http://bl.ocks.org/erikhazzard/6201948

    Sembra che ci sono tre problemi:

    1. Percorsi di disegnare sopra l’icona. Per risolvere questo problema, è possibile modificare l’ordine di quando si aggiungono elementi al gruppo, o aggiungere i gruppi per il principale g gruppo, garantendo l’ordine che si aggiunge gruppi partite ordine in cui si desidera che le cose appaiono.

    2. I sentieri tra i punti non zoom quando si fa lo zoom della mappa. Per risolvere questo problema, assicurarsi di aggiungere il tutto al gruppo che si sta modificando il clicked() funzione. In questo caso, il g gruppo è ingrandita, quindi se si aggiungono i percorsi per le g gruppo invece di svg direttamente i percorsi di zoom così. Nell’esempio fornito, il testo, inoltre, non zoom in – questo perché è stato aggiunto direttamente al SVG e non g gruppo che si sta trasformando.

    3. Percorsi non vengono creati automaticamente dai dati. Per risolvere questo problema, è possibile generare un array contenente oggetti LineString dai dati. Per 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 ]
                  ]
              });
          }

      Quindi, fare di dati standard join modello e passare il links elenco di dati. Quando si passa in path come d attributo, si genererà un grande arco sulla base delle 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 sul grande arco percorsi per illustrare il percorso è redatto in base all’ordine di coppie di coordinate passati i collegamenti in oggetto.

    Speranza che aiuta!

    • Che è assolutamente brillante. Grazie! Seriamente, non lo ringrazierò mai abbastanza. Non solo non si risolve il problema per me, ma i tuoi commenti sono estremamente informativo e mi aiuterà a capire come risolvere questi problemi in futuro.
    • Non … imageGroup.exit().remove() … non funziona se inserito all’interno di … funzione cliccato(d) … ?
    • potrebbe essere necessario chiamare exit() sull’oggetto di selezione…ad esempio, var pathArcs = arcGroup.selectAll(“.arc”) .dati(collegamenti); … pathArcs.exit().remove();
    • Sì, sto cercando di capire come selezionare il logos. Non so come selezionare gli oggetti anziché l’intero imageGroup.
    • Ah, per selezionare le immagini se sono in imageGroup, si può fare imageGroup.selectAll(‘immagine’); Se si desidera rimuovere il tutto, per esempio, si potrebbe cercare qualcosa di simile imageGroup.selectAll(‘immagine’).dati([]).exit().remove(); (che permetterebbe di eliminare tutte le immagini, come la matrice di dati si sta passando è vuota)
    • Grande esempio. Io in realtà non ho provato il tuo codice che hai fornito al @bl.ocks.org ma suppongo che se volevo disegnare le rotte che vanno dal punto a al punto C punto B mi piacerebbe estendere le coordinate array con lat/lon valori per il punto B?
    • sì, sarebbe quindi disegnare linee da a a B, da B a C

Lascia un commento