block by enjalot 11cba0c5c3a9e1841865a032a31fcc3d

Georgia counties

Full Screen

Adapting the state grid for use as an overview/minimap. When you zoom in on a state the counties for that state are rendered. This technique could be used to render subsets of larget datasets at the appropriate zoom level.

Map zooming from http://bl.ocks.org/mbostock/2206590

forked from mbostock‘s block: State Grid

forked from enjalot‘s block: State Grid Minimap

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<link rel="stylesheet" href="style.css"></link>
<style>
</style>
<svg id="map"></svg>
<svg id="state-grid" width=400 height=200></svg>
<script id="grid" type="text/plain">
                              ME
               WI          VT NH
WA ID MT ND MN IL MI    NY MA
OR NV WY SD IA IN OH PA NJ CT RI
CA UT CO NE MO KY WV VA MD DE
   AZ NM KS AR TN NC SC
         OK LA MS AL GA
HI AK    TX             FL
</script>

<script src="stategrid.js"></script>
<script>

var mapsvg = d3.select("#map").append("g");
var mapwidth = 960;
var mapheight = 500;
var scale0 = 1000;

var centered;
var selected;
  
var allCounties = []

var zoom = d3.behavior.zoom()
  .translate([mapwidth / 2, mapheight / 2])
  .scale(scale0)
  .scaleExtent([scale0, 10 * scale0])
  .on("zoom", zoomed);

var projection = d3.geo.albersUsa()
  .scale(scale0)
  .translate([mapwidth / 2, mapheight / 2]);

var path = d3.geo.path()
  .projection(projection);
  
function zoomed() {
  projection
    .translate(zoom.translate())
    .scale(zoom.scale());

  mapsvg.selectAll("path")
    .attr("d", path);
  mapsvg.selectAll("circle.city")
    .attr({
      cx: getX,
      cy: getY
    })
}
  
mapsvg.call(zoom)


queue()
    .defer(d3.json, "us-named.json")
    //.defer(d3.csv, "unemployment.csv")
    .await(ready);
  
function ready(error, us) {
  var states = topojson.feature(us, us.objects.states).features
  allCounties = topojson.feature(us, us.objects.counties).features;
  mapsvg
  .selectAll("path")
    .data(states)
  .enter().append("path").classed("state-boundary", true)
    .attr("d", path)
  
  var georgia = states.filter(function(d) {
    // list of state FIPS codes
    return d.id === 13; //Georgia
  })
  console.log("georgia", georgia)
  clicked(georgia[0]);
}

</script>

stategrid.js


var minisvg = d3.select("#state-grid");
var miniwidth = 400;
var miniheight = 200;

var states = [];
d3.select("#grid").text().split("\n").forEach(function(line, i) {
  var re = /\w+/g, m;
  while (m = re.exec(line)) states.push({
    name: m[0],
    x: m.index / 3,
    y: i
  });
});

var gridWidth = d3.max(states, function(d) { return d.x; }) + 1;
var gridHeight = d3.max(states, function(d) { return d.y; }) + 1;
var cellSize = 25;

var state = minisvg.append("g")
    .attr("transform", "translate(" + miniwidth / 2 + "," + miniheight / 2 + ")scale(1)")
  .selectAll(".state")
    .data(states)
  .enter().append("g")
    .classed("state", true)
    .attr("transform", function(d) { return "translate(" + (d.x - gridWidth / 2) * cellSize + "," + (d.y - gridHeight / 2) * cellSize + ")"; });

state.append("rect")
  .attr("x", -cellSize / 2)
  .attr("y", -cellSize / 2)
  .attr("width", cellSize - 2)
  .attr("height", cellSize - 2);

state.append("text")
  .attr("dy", ".35em")
  .attr("dx", "-.1em")
  .text(function(d) { return d.name; });
  
state.on("click", function(d) {
  console.log("clicked", d) 
  var sel = d3.selectAll(".state-boundary").filter(function(a) { return a.properties.code === d.name})
  var state = sel.data()[0]
  console.log("state", state)
  clicked(state);
  if(d3.select(this).classed("selected")) {
    d3.select(this).classed("selected", false)
  } else {
    minisvg.selectAll(".state").classed("selected", false)
    d3.select(this).classed("selected", true)
  }

});



function clicked(d) {
  var x, y, k;
  console.log("clicked", d)

  if (d && centered !== d) {
    var centroid = path.centroid(d);
    x = centroid[0];
    y = centroid[1] - 3;
    k = 5.9;
    centered = d;
  } else {
    x = mapwidth / 2;
    y = mapheight / 2;
    k = 1;
    centered = null;
  }
  console.log("x,y", x,y)

  mapsvg.selectAll("path.state-boundary")
      .classed("active", centered && function(d) { return d === centered; });

  mapsvg.transition()
      .duration(450)
      .attr("transform", "translate(" + mapwidth / 2 + "," + mapheight / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
      .style("stroke-width", 1.5 / k + "px");
  
  mapsvg.selectAll("path.county")
  .transition().duration(300)
  .style("opacity", 0)
  .remove();
  
  var counties = [];
  allCounties.forEach(function(c) {
    var sid = d.id+"";
    var cid = c.id+"";
    if(cid.slice(0,sid.length) === sid && cid.length - sid.length == 3) counties.push(c);
  })
  console.log("counties", counties)
  var countyPaths = mapsvg
  	.selectAll("path.county")
    .data(counties)
  countyPaths
  	.enter().append("path").classed("county", true)
    .style("opacity", 0)
  
  countyPaths.attr("d", path)
  .transition()
  .duration(800)
  .style("opacity", 0.6)
}

style.css


body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
#state-grid {
  position:absolute;
  top: 0;
  left: 0;
  display: none;
}
#map {
  position: absolute;
  width: 100%;
  height: 100%;
}
.state {
  cursor: pointer;
}
.state rect {
  fill: #dedede;
  fill-opacity: 0.4;
  rx: 3;
  ry: 3;
}
.selected rect {
  fill: steelblue;
}

.state text {
  font: 12px sans-serif;
  text-anchor: middle;
}
.state-boundary {
  fill: none;
  stroke: #111;
}
.county {
  fill: #d6fef1;
  stroke: #111;
}