block by pjsier 8e955d6a770ca0704a44521dbbf131ca

Chicago SSL, ISR Hexbin

Full Screen

Chicago Contact Card, SSL Hexbin

Bivariate map showing hexbins for contact card stops as well as a choropleth map of census tracts by arrest locations with SSL scores.

script.js

// Margin convention from https://bl.ocks.org/mbostock/3019563
var margin = {top: 20, right: 10, bottom: 20, left: 10};
var width = 500 - margin.left - margin.right;
var height = 650 - margin.top - margin.bottom;
var svg = d3.select("#map-container")
  .attr("width", 500)
  .attr("height", 650)
  .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var projection = d3.geoMercator().scale(1).translate([0,0]);
var path = d3.geoPath().projection(projection);
var hexbin = d3.hexbin()
    .size([width, height])
    .radius(8);

function ready(error, json, cards) {
  var bounds = path.bounds(json);
  var s = 0.95 / Math.max((bounds[1][0] - bounds[0][0]) / width, (bounds[1][1] - bounds[0][1]) / height);
  var t = [(width - s * (bounds[1][0] + bounds[0][0])) / 2, (height - s * (bounds[1][1] + bounds[0][1])) / 2];
  projection.scale(s).translate(t);
  var color = d3.scaleQuantize()
    .domain([0, d3.max(json.features, function(d) { return d.properties.ssl_count; })])
    .range(colorbrewer['Blues'][5]);
    // Grayscale
    // .range(colorbrewer['Greys'][5]);

  var g = svg.append("g");
  g.selectAll("path")
    .data(json.features)
    .enter()
    .append("path")
      .attr("d", path)
      .attr("fill", function(d) { return color(d.properties.ssl_count); });

  cards = cards.filter(function(d) {
    return d.lon !== "" && d.lat !== "";
  })
  cards.forEach(function(d) {
    var p = projection([+d.lon, +d.lat]);
    d[0] = p[0], d[1] = p[1];
  });

  var hexbinData = hexbin(cards).sort(function(a, b) { return b.length - a.length; });
  var radius = d3.scaleSqrt()
      .domain([0, hexbinData[0].length])
      .range([0, 6]);

  var cardFeature = svg.append("g")
    .selectAll("circle")
      .data(hexbinData)
    .enter().append("circle")
      .attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
      .attr("r", function(d) { return radius(d.length); });

  var cardLegend = svg.append("g")
      .attr("transform", "translate(25,200)")
      .attr("class", "g-legend");

  cardLegend.append("text")
    .attr("y", -36)
    .style("font-weight", "bold")
    .text("ISR Stops");
  cardLegend.append("text")
    .attr("y", -20)
    .text("Jan 2016-Feb 2017");

  var cardKey = cardLegend.selectAll(".g-key")
      .data([100, 500, 1000, 2000])
    .enter().append("g")
      .attr("class", "g-key");

  cardKey.append("circle")
      .attr("class", "g-cards")
      .attr("cx", function(d) { return 5; })
      .attr("cy", function(d, i) { return i*25; })
      .attr("r", radius);

  cardKey.append("text")
      .attr("x", 25)
      .attr("y", function(d, i) { return i*25; })
      .attr("dy", ".35em")
      .text(function(d) { return d; });

  var sslLegend = svg.append("g")
      .attr("transform", "translate(25,350)")
      .attr("class", "g-legend");

  sslLegend.append("text")
    .attr("y", -26)
    .style("font-weight", "bold")
    .text("# Arrest Locations w/SSL Scores");
  sslLegend.append("text")
    .attr("y", -10)
    .text("Aug 2012-Jul 2016");

  var sslKey = sslLegend.selectAll(".g-key")
      .data(color.range())
    .enter().append("g")
      .attr("class", "g-key");

  sslKey.append("rect")
    .attr("class", "g-ssl-colors")
    .attr("width", 20)
    .attr("height", 20)
    .style("fill", function(d) { return d; })
    .attr("x", 0)
    .attr("y", function(d, i) { return 20*i;});

  sslKey.append("text")
    .attr('x', 35)
    .attr('y', function(d, i) { return (20*i)+10; })
    .style("alignment-baseline", "central")
    .text(function(d) {
      return color.invertExtent(d).map(function(d) { return Math.floor(d); }).join("-");
    });
}

(function() {
  d3.queue()
    .defer(d3.json, "chi_census_tracts.geojson")
    .defer(d3.csv, "https://raw.githubusercontent.com/pjsier/strategic-subject-list-viz/master/data/contact_cards.csv")
    .await(ready);
})()

index.html

<!DOCTYPE html>
<html>
  <head>
    <title>SSL, Contact Cards Hexbin</title>
    <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
    <meta charset='utf-8' />
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3-queue.v3.min.js"></script>
    <script src="https://d3js.org/d3-hexbin.v0.2.min.js"></script>
    <script src="https://d3js.org/colorbrewer.v1.min.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v0.3.min.js"></script>
    <style>
    #map-container {
      display: block;
    }
    path {
      stroke: #fff;
      stroke-width: 0.5;
      opacity: 0.8;
    }
    g text {
      font-family: "Verdana";
      font-size: 10px;
    }
    circle {
      fill: #d95f0e;
      opacity: 0.6;
      /* Grayscale
      fill: #252525; */
    }
    </style>
  </head>
  <body>
  <svg id="map-container"></svg>
  <script src="script.js"></script>
</body>
</html>