Search code examples
dictionaryd3.jsgeojsonmercator

D3js. Select different .CSV file based on onclick suggestion on d3.geo.mercator


I am very new to java script and D3. I picked d3.geo.mercator code from online and used a single .csv files to show employees and customers based on Latitude and longitude. My boss wants a options to select employees or customers separately. I made a html as belows to redirect to different html files with same code but different .csv files but when employee option is clicked i get error "attribute cx: Expected length, "NaN"."

<!DOCTYPE html>
<html>
  <head>
    <meta charset="ISO-8859-1">
    <title>MyCompany</Title>
  </head>

  <body>
    <form action="">
      <h2>Select Your Choice..</h2>
      <input type="button" value="Customers" onclick="window.location.href='Customers.html';">
      <input type="button" value="Employees" onclick="window.location.href='Employees.html';">
    </form>
  </body>
</html>

Since the D3 code is same for both instead of using two .html files i wish to pick .csv files based on the option selected and I need help to do that. Thanks and appreciate your help.

<script>
var width = 960,
    height = 960;

var projection = d3.geo.mercator()
    .center([0, 5 ])
    .scale(200)
    .rotate([-180,0]);

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

var path = d3.geo.path()
    .projection(projection);

var g = svg.append("g");

// load and display the World
d3.json("world-110m2.json", function(error, topology) {

// load and display the cities
d3.csv("Customers.csv", function(error, data) {
    g.selectAll("circle")
       .data(data)
       .enter()
     .append("a")
                  .attr("xlink:href", function(d) {
                      return "https://www.google.com/search?q="+d.city;}
                  )
     .append("circle")
       .attr("cx", function(d) {
               return projection([d.lon, d.lat])[0];
       })
       .attr("cy", function(d) {
               return projection([d.lon, d.lat])[1];
       })
       .attr("r", 5)

     .style("fill", function(d) {        
            if (d.category == "Employee") {return "red"}  
            else if (d.category == "Office" ) {return "lawngreen"} // <== Right here 
            else { return "blue" }             
        ;}) 
    g.selectAll("text")
       .data(data)
       .enter()
     .append("text") // append text
       .attr("x", function(d) {
               return projection([d.lon, d.lat])[0];
       })
       .attr("y", function(d) {
               return projection([d.lon, d.lat])[1];
       })
       .attr("dy", -7) // set y position of bottom of text
      .style("fill", "black") // fill the text with the colour black
      .attr("text-anchor", "middle") // set anchor y justification

      .text(function(d) {return d.city;}); // define the text to display

});

g.selectAll("path")
      .data(topojson.object(topology, topology.objects.countries)
          .geometries)
    .enter()
      .append("path")
      .attr("d", path)
});

// zoom and pan
var zoom = d3.behavior.zoom()
    .on("zoom",function() {
        g.attr("transform","translate("+ 
            d3.event.translate.join(",")+")scale("+d3.event.scale+")");
        g.selectAll("circle")
            .attr("d", path.projection(projection));
        g.selectAll("path")  
            .attr("d", path.projection(projection)); 

  });

svg.call(zoom)

</script>

Solution

  • The goal as I understand it is to have one page with one script allow a user to display data from one of 2 (or any) number of csv files.

    There are two primary methods to achieve your goal.

    1. Render all the data but hide/show elements selectively (for example by using a class name to identify what data should be shown).

    2. Load a particular csv file on demand and show it (by either removing the previous data and redrawing or by updating the data points drawn).

    Both approaches can be triggered by one function that passes a) the name of the class that should be shown or b) the name of the csv holding the desired data.

    I've put together two examples that show how this might work for the two options above.

    1. Draw all features first, then toggle what is visible with buttons: here.

    Explanation: Once everything in both CSV files is drawn, then all we need to do is assign an event listener to each button so that on click, the the button's id is passed to an update function that hides everything that doesn't have a class type equal to the button's id.

    To be showy, instead of playing with the visibility attribute of each data point, I've changed the radius of features to zero with a transition when they need to disappear, and used a transition to do the opposite when showing them as well.

    1. Draw only one set of features first, then load each CSV file as needed: here

    Explanation: Draw a CSV file right away. Assign an event listener to each button so that on click, the button's id (filename in this case) is passed to the update function. The update function draws the chosen CSV by doing a enter, update, and exit transition for data (fading out unneeded data points, transitioning points to new locations, and adding new data points as needed).

    The alternative to the second option's implementation is to simply remove all the previous data points, and draw the required csv data as though you are drawing it for the first time.