javascriptd3.jstopojson

Group shapes/counties together by a list of attributes with D3.js


Is there a way to group certain counties together with D3.js?

I'm displaying a county level map of the US, and I want to draw a border around groups of counties or highlight groups of counties.

I'd have an array of each county (by d.id which gets assigned when drawing the counties), but I'm not sure how to either target that attribute or draw a border around a group of elements with those attributes.

    <script>

    var svg = d3.select("svg");

    var path = d3.geoPath();

    d3.json("https://d3js.org/us-10m.v1.json", function(error, us) {
        if (error) throw error;

    // draw counties
    svg.append("g")
        .attr("class", "counties")
        .selectAll("path")
        .data(topojson.feature(us, us.objects.counties).features)
        .enter().append("path")
        .attr("d", path)
        .on("click", clicked)

    svg.append("path")
        .attr("class", "county-borders")
        .attr("d", path(topojson.mesh(us, us.objects.counties, function(a, b) { return a !== b; })));

    // draw states
    svg.append("g")
        .attr("class", "states")
        .selectAll("path")
        .data(topojson.feature(us, us.objects.states).features)
        .enter().append("path")
        .attr("d", path);

    svg.append("path")
        .attr("class", "state-borders")
        .attr("d", path(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; })));

    function clicked(d) {
        console.log(d.id);
    }


    var example_group = [45001, 45003, 45005, 45007, 45009, 45011];


});

    </script>

Solution

  • The topojson library features a merge method. Since you're using topojson it is fairly simple to merge features. However, if you are planning on many merges and have static groupings, creating merged data to serve (rather than merging in browser each load), may be preferred, this may just involve saving the geojson created by topojson.merge below.

    First, we make some groupings using feature ids:

      // groups: 
      var groups = [
         [45001, 45003, 45005, 45007, 45009, 45011],
         [32023, 32003, 06027, 32017, 49053, 4015]
      ];
    

    Then we run topojson.merge for each one:

      var groupedCounties = groups.map(function(group) {
        return topojson.merge(us, us.objects.counties.geometries.filter(function(d) {
            return group.indexOf(+d.id) > -1;  // Merge criteria.
        })) 
      })
    

    This returns an array of geojson features, so we can use it directly with d3.geoPath.

    Now we just draw them:

      svg.selectAll(null)
        .data(groupedCounties)
        .enter()
       .append("path")
       .attr("d", path)
       ....
      
    

    As seen below:

    var svg = d3.select("svg");
    
        var path = d3.geoPath();
        
        d3.json("https://d3js.org/us-10m.v1.json", function(error, us) {
            if (error) throw error;
            
     
            
            // for fitting in snippet view (somewhat)
            path.projection(d3.geoIdentity().fitSize([500,300],topojson.feature(us, us.objects.counties)))  
            
           
            svg.append("path")
            .attr("class", "county-borders")
            .attr("d", path(topojson.mesh(us, us.objects.counties, function(a, b) { return a !== b; })));
    
        // draw states
           svg.append("g")
            .attr("class", "states")
            .selectAll("path")
            .data(topojson.feature(us, us.objects.states).features)
            .enter().append("path")
            .attr("d", path);        
            
          // groups: 
          var example_group = [45001, 45003, 45005, 45007, 45009, 45011];
          var example_group2 = [32023,32003,06027,32017,49053,4015]; 
          // make an array of groups:
          var groups = [example_group,example_group2];
    
          // make features out of these:
          var groupedCounties = groups.map(function(group) {
            return topojson.merge(us, us.objects.counties.geometries.filter(function(d) { return group.indexOf(+d.id) > -1; }))
          })
          
          console.log(groupedCounties);
          
          // draw grouped features:
          svg.selectAll(null)
            .data(groupedCounties)
            .enter()
           .append("path")
           .attr("d", path)
           .style("fill", function(d,i) {
             return ["orange","steelblue"][i];
           })
          
          
    
    
    
    
    
    
    });
    .states{
      fill: none;
      stroke: black;
      stroke-width: 1px;
    }
    
    
    .county-borders {
      fill:none;
      stroke: #ccc;
      stroke-width: 1px;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
    <script src="https://d3js.org/topojson.v1.min.js"></script>
    <svg width="500" height="300"></svg>