Percentage of Female Residents Relative to 50/50 Split

Source Code

demo.html

<div class="row">
  <div id="plot" class="col-md-12"></div>
</div>
<link rel="stylesheet" type="text/css" href="demo.css">
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script type="text/javascript" src="demo.js"></script>

demo.css

.land {
  fill: white;
}

.state-boundary {
  fill: none;
  stroke: #000;
  stroke-width: 0.5px;
}

.county-boundary {
  fill: none;
  stroke: #000;
  stroke-width: .15px;
}

demo.js

// Set up the SVG
var svg_width = 1000;
var svg_height = 500;

var projection = d3.geoAlbersUsa();
var path = d3.geoPath().projection(projection);

// Generate an SVG element on the page
var svg = d3.select("#plot").append("svg")
    .attr("width", svg_width)
    .attr("height", svg_height);

// Keep track of county populations
var population = d3.map();

// Make a scale for colors
var colorScale = d3.scalePow().exponent(.75)
                   .range(['#0571c0', '#fff', '#e01c8b']);

// Defer our actual code until we have both the map and county data loaded
d3.queue()
  .defer(d3.json, 'us.json')
  .defer(d3.csv, 'county_facts.csv', function(d) {
    // This code runs for each row of county_facts.csv
    var county_id = parseInt(d.fips);
    var value = parseInt(d.SEX255214);
    
    // Exclude states and the whole county
    if(county_id % 1000 != 0 && value != 0) {
      population.set(county_id, (value - 50)/100);
    }
  })
  .await(function(error, us) {
    // This code runs when both data files are loaded
    
    var min = d3.min(population.values());
    var max = d3.max(population.values());
    
    // Compute the range of values for the legend
    var legend_max = Math.max(Math.abs(min), max);
    var legend_min = -legend_max;
    
    // Set the domain of the color scale
    colorScale.domain([legend_min, 0, legend_max]);
    
    // Create a scale to choose legend values
    var legendScale = d3.scaleLinear().domain([0, 8]).range([legend_max, legend_min]);
    
    // Generate a legend
    for(var i=0; i<=8; i++) {
      svg.append('rect')
        .attr('x', svg_width - 100)
        .attr('y', 100 + 30 * i)
        .attr('width', 25)
        .attr('height', 25)
        .attr('stroke', '#000')
        .attr('stroke-width', '0.5px')
        .attr('fill', colorScale(legendScale(i)));
      
      svg.append('text')
        .attr('x', svg_width - 70)
        .attr('y', 118 + 30 * i)
        .attr('font-family', 'sans-serif')
        .attr('font-size', '10pt')
        .text(d3.format('+.1%')(legendScale(i)));
    }

    // Create land area
    svg.append('path')
      .datum(topojson.feature(us, us.objects.land))
      .attr('class', 'land')
      .attr('d', path);
      
    // Generate counties and color them based on population
    svg.append('g')
      .attr('class', 'counties')
      .selectAll('path')
      .data(topojson.feature(us, us.objects.counties).features)
      .enter()
      .append('path')
        .attr('fill', d => colorScale(population.get(d.id)))
        .on('mouseover', d => console.log(d))
        .attr('d', path);
  
    // Create county boundaries
    svg.append('path')
      .datum(topojson.mesh(us, us.objects.counties))
      .attr('class', 'county-boundary')
      .attr('d', path);
  
    // Create state boundaries
    svg.append('path')
      .datum(topojson.mesh(us, us.objects.states))
      .attr('class', 'state-boundary')
      .attr('d', path);
  
    // Grinnell's location: 41.743409, -92.723246
    var coordinates = projection([-92.723246, 41.743409]);
  
    // Make a circle at Grinnell's location
    svg.append('circle')
      .attr('r', 4)
      .attr('cx', coordinates[0])
      .attr('cy', coordinates[1])
      .attr('fill', 'none')
      .attr('stroke', 'black')
      .attr('stroke-width', '1.5');
});