block by mpmckenna8 2fb0ffd4aa38990aff57

2fb0ffd4aa38990aff57

Full Screen

US Federal Judicial Districts

Sources:

index.html

<!doctype html>
<meta charset="utf-8">
<style type="text/css" media="screen">
  body {
    margin: 0;
  }

  .district {
    stroke: #fff;
    stroke-width: 1;
    fill-opacity: 0.6;
  }

  .district:hover {
    fill-opacity: 0.8;
  }

  .county {
    fill: none;
    stroke: #333;
    stroke-width: 0.5;
    stroke-opacity: 0.6;
  }

  .label {
    font-family: Helvetica;
    font-size: 11px;
    font-weight: bold;
    fill-opacity: 1;
    text-anchor: middle;
    stroke: none;
    pointer-events: none;
  }
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script type="text/javascript" charset="utf-8">
  var width = 960,
      height = 600

  var path = d3.geo.path()
  var color = d3.scale.category20()

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

  d3.json('us.json', function(error, us) {
    if(error) return console.error("Boom goes the dynamite: " + error)

    var dem = 0, rep = 0
    us.objects.districts.geometries.forEach(function(d) {
      d.properties.judges.forEach(function(j) {
        if(j.ppa === 'Democratic') dem++
        if(j.ppa === 'Republican') rep++
      })
    })

    console.log(dem + " Democrat appointments", rep + " Republican appointments");

    svg.append('path')
      .datum(topojson.mesh(us, us.objects.counties))
      .attr('class', 'county')
      .attr('d', path)

    districts = svg.selectAll('.district')
      .data(topojson.feature(us, us.objects.districts).features)
    .enter().append('path')
      .attr('class', 'district')
      .attr('d', path)
      .style('fill', function(d, i) { return color(i) })
      .on('click', function(district) {
        var d = 0, r = 0

        var names = district.properties.judges.map(function(j) {
          if(j.ppa === 'Democratic') d++
          if(j.ppa === 'Republican') r++

          return j.ppa + ": " + j.name
        }).join("\n")

        console.log("D: " + d, "R: " + r, "\n\n" + names);
      })

    var labels = svg.append('g')
    districts.each(function(d) {
      var center = path.centroid(d)
      labels.append('text')
        .attr('class', 'label')
        .attr('dx', function(d) { return center[0] })
        .attr('dy', function(d) { return center[1] })
        .text(d.properties.jdcode)
    })

  })
</script>
</body>

Makefile

all: us.json

clean:
	rm -rf -- us.json \
		build/*-ungrouped.* \
		build/*.json \
		build/judicial-districts.shp \
		build/judicial-districts.sbn \
		build/judicial-districts.sbx \
		build/judicial-districts.shx \
		build/judicial-districts.prj \
		build/judicial-districts.dbf

.PHONY: all clean

build/judicial-districts.shp:
	rm -rf $(basename $@)
	mkdir -p $(basename $@)
	tar -xzm -C build -f ./build/judicial-districts.tar.gz
	for file in $(basename $@)/*; do chmod 644 $$file; mv $$file $(basename $@).$${file##*.}; done
	rmdir $(basename $@)

build/counties.json: build/judicial-districts.shp
	node_modules/.bin/topojson \
		-o $@ \
		--no-pre-quantization \
		--post-quantization=1e6 \
		--simplify=7e-7 \
		-p jdcode=+JDCODE,state=State,name=JD_NAME \
		--id-property=+FIPS \
		-- counties=build/judicial-districts.shp

build/districts.json: build/counties.json
	rm $(basename $@)-unreconciled.json
	node_modules/.bin/topojson-merge \
		-o $(basename $@)-unreconciled.json \
		--in-object=counties \
		--out-object=districts \
		--key='d.properties.jdcode' \
		-- $<
		./reconcile-judges < $(basename $@)-unreconciled.json > $@

us.json: build/districts.json
	node_modules/.bin/topojson-merge \
		-o $@ \
		--in-object=districts \
		--out-object=nation \
		-- $<

package.json

{
  "name": "anonymous",
  "version": "0.0.1",
  "private": true,
  "dependencies": {
    "fast-csv": "0.2.4",
    "topojson": ">=1.6.2 <2"
  }
}

reconcile-judges

#!/usr/bin/env node

// Because no one bothered to standardize on court names or give them an ID and
// assign judges to one, we're left with the cesspool you see below
//
var fs  = require('fs'),
    csv = require('fast-csv')

var topo = JSON.parse(fs.readFileSync('build/districts-unreconciled.json'))

var judges   = [],
    states = [],
    names = [],
    locations = ['Northern', 'Southern', 'Middle', 'Western', 'Eastern']

// Only keep active US District Court judges
function terminated(record) {
  var termination = record['Date of Termination'],
      death       = record['Death year'],
      courtType   = record['Court Type'],
      retired     = record['Retirement from Active Service']

  if(termination === '' && courtType === 'USDC' && retired === '') {
    judges.push(record)
  }
}

function reconcile() {
  topo.objects.districts.geometries.forEach(function(geo) {
    geo.properties.judges = []

    if(geo.properties.name === null) return;

    var parts = geo.properties.name.split(' '),
        geostate = null,
        geolocation = null

    if(parts.length > 1) {
      geolocation = parts.pop()
      geostate    = parts.join(' ')
    } else {
      geostate = parts[0]
    }

    judges.forEach(function(judge) {
      var cname = judge['Court Name'],
          info  = {
            name: judge['Judge First Name'] + " " + judge['Judge Last Name'],
            ppa: judge['Party Affiliation of President']
            // race: judge['Race or Ethnicity'],
            // gender: judge['Gender'],
            // date: judge['Commission Date']
          }

      if(cname.indexOf(geostate) !== -1 && (geolocation && cname.indexOf(geolocation) !== -1)) {
        geo.properties.judges.push(info)
      } else if(!geolocation && cname.indexOf(geostate) != -1) {
        geo.properties.judges.push(info)
      }
    })
  })

  console.log(JSON.stringify(topo));
}

csv.fromPath('build/states.csv', { headers: true })
  .on('record', function(state) { states.push(state) })
  .on('end', function() {
    csv.fromPath('build/judges.csv', { headers: true })
      .on('record', terminated)
      .on('end', reconcile)
  })