How to draw a line / link between two points on a D3 map based on latitude / longitude?

Lokitez

I am attempting to create a map of the 10 major NASA facilities in D3. I have successfully generated the base United States map and appended NASA logos at each one of the center locations based on a .csv with latitude and longitude. However, I cannot figure out any elegant way to draw lines / links / arcs / connections between the points on the map.

In the code below, I have drawn a line between GSFC and KSC (using the 'var = places', 'var = route', and 'svg.append("path")') but it is on an SVG layer, so it is on top of the logos (which looks awful) and does not scale (or go away would be fine, too) when clicking to zoom in on a state. I would like to be able to draw links between the centers based on the latitude and longitude data from the .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>

The .csv file is in the following format:

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
ErikHazzard

I modified your example slightly based on the problems you described: http://bl.ocks.org/erikhazzard/6201948

It looks like there are three issues:

  1. Paths draw on top of icon. To fix this, you can change the order of when you add items to the group, or add sub groups to your main g group, ensuring the order that you add the groups matches the order you want things to appear.

  2. The paths between points doesn't zoom when you zoom the map. To fix this, make sure to add everything to the group that you're modifying the clicked() function. In this case, your g group is being zoomed on, so if you add the paths to the g group instead of the svg directly the paths will zoom as well. In the example provided, text does also not zoom in - that's because it's added directly to the SVG and not the g group that is being transformed.

  3. Paths aren't created automatically from the data. To fix this, you can generate an array containing LineString objects from the data. For example,

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

    Then, do the standard data join pattern and pass in the links list to the data. When you pass in path as the d attribute, it will generate a great arc based on the coordinates for each item:

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

In my example ( http://bl.ocks.org/enoex/6201948 ) I added a transition on the great arc paths to illustrate how the path is drawn based on the order of coordinate pairs passed into the links object.

Hope that helps!

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

Getting distance between two points based on latitude/longitude

Basemap draw line between latitude longitude

How to advance X distance (meters) between two points of latitude and longitude?

Calculating distance between two points (Latitude, Longitude)

How to draw a line between two points over an image in swift 3?

How to get map to show longitude and latitude accurately with d3

PYTHON : Which is the best way to find distance between two points based on latitude/longitude using python?

Google Map Draw Polyline between multiple latitude longitude

topojson / D3 / Map with longitude + latitude

How to draw polyline on react native maps with many points(latitude and longitude)?

How can I measure distance and create a bounding box based on two latitude+longitude points in Java?

Calculating the distance between two latitude and longitude points in android

Calculating distance between two points, using latitude longitude?

Calculate distance between two latitude-longitude points? (Haversine formula)

Minimize distance between two latitude-longitude points?

Calculate distance between two latitude-longitude points?

How can I quickly estimate the distance between two (latitude, longitude) points?

How to find the distance between two points given longitude and latitude without do loop?

How to compute angle between two intersecting lines by having latitude/longitude points in Java?

How to draw the map my current location to particular latitude longitude values

How to draw route on google map with the help of latitude and longitude?

How to draw a bubble map with longitude and latitude using Highcharts in react

How to draw path latitude and longitude values from database in map in android

Draw arrow between on line between two points

Matplotlib how to draw vertical line between two Y points

How to draw a line between two given points in R with plotly?

How to draw a line between two points objective-C

How to draw a line between two points with js and CSS

How to display points defined via latitude and longitude on a Bubble Map?