2013-08-10 12 views
17

Próbuję utworzyć mapę 10 głównych obiektów NASA w D3. Udało mi się wygenerować podstawową mapę Stanów Zjednoczonych i dołączone logo NASA w każdej z centralnych lokalizacji na podstawie .csv z szerokością i długością geograficzną. Jednak nie mogę wymyślić żadnego eleganckiego sposobu rysowania linii/ogniw/łuków/połączeń między punktami na mapie.Jak narysować linię/połączenie między dwoma punktami na mapie D3 na podstawie szerokości/długości geograficznej?

W poniższym kodzie narysowałem linię między GSFC i KSC (używając "var = places", "var = route" i "svg.append (" path ")"), ale jest on na Warstwa SVG, więc jest na wierzchu logo (które wygląda okropnie) i nie skaluje się (lub zniknie też będzie dobrze) po kliknięciu, aby powiększyć stan. Chciałbym móc rysować połączenia między centrami na podstawie danych szerokości i długości geograficznej z pliku .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> 

Plik .csv jest w następującym formacie:

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 

Odpowiedz

25

I nieco zmodyfikowany przykład oparte na problemach opisałeś: http://bl.ocks.org/erikhazzard/6201948

Wygląda jak istnieją trzy kwestie:

  1. Ścieżki rysowania na górze ikony. Aby to naprawić, możesz zmienić kolejność dodawania elementów do grupy lub dodawać podgrupy do głównej grupy g, upewniając się, że kolejność dodawania grup jest zgodna z kolejnością, w jakiej mają się pojawiać.

  2. Ścieżki między punktami nie powiększają się po powiększeniu mapy. Aby to naprawić, dodaj wszystko do grupy, w której modyfikujesz funkcję clicked(). W tym przypadku grupa g jest powiększana, więc jeśli dodasz ścieżki do grupy g zamiast svg, ścieżki również zostaną powiększone. W podanym przykładzie tekst również nie jest powiększany - wynika to z tego, że jest on dodawany bezpośrednio do SVG, a nie do przekształcanej grupy g.

  3. Ścieżki nie są tworzone automatycznie z danych. Aby to naprawić, możesz wygenerować tablicę zawierającą obiekty LineString z danych. Na przykład,

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

    Następnie należy wykonać standardowe dane dołączyć wzór i przekazać w liście do danych links. Po przejechaniu w path jako atrybut d będzie generować wielkie łuku na podstawie współrzędnych dla każdej pozycji:

    // 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' 
        }) 
    

w moim przykładzie (http://bl.ocks.org/enoex/6201948) Dodałem przejście na wielkich ścieżek ARC ilustruje sposób rysowania ścieżki w oparciu o kolejność par współrzędnych przekazywanych do obiektu odsyłaczy.

Nadzieję, że pomaga!

+0

To jest absolutnie genialne. Dziękuję Ci! Poważnie, nie mogę ci wystarczająco podziękować. Nie tylko rozwiązało to problem, ale Twoje komentarze są bardzo pouczające i pomogą mi nauczyć się rozwiązywać te problemy w przyszłości. – Lokitez

+0

Czy ... imageGroup.exit(). Remove() ... nie działa, gdy jest zawarty wewnątrz ... funkcji kliknięty (d) ...? – Lokitez

+0

konieczne może być wywołanie metody exit() w obiekcie wyboru ... np., var pathArcs = arcGroup.selectAll (". Arc") .data (linki); ... pathArcs.exit(). Remove(); – ErikHazzard

Powiązane problemy