Skip to content

Instantly share code, notes, and snippets.

@sjengle
Last active January 26, 2016 23:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sjengle/ac9c5e4ad3a750197149 to your computer and use it in GitHub Desktop.
Save sjengle/ac9c5e4ad3a750197149 to your computer and use it in GitHub Desktop.
TopoJSON Demo
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>TopoJSON Demo</title>
<!-- load D3 and TopoJSON //-->
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<!-- load custom CSS and JavaScript //-->
<link rel="stylesheet" href="style.css">
<script src="script.js"></script>
</head>
<body>
<!-- placeholder SVGs //-->
<div id="block">
<svg id="final" width="10" height="10"></svg>
<svg id="country" class="small" width="10" height="10"></svg>
<svg id="states" class="small" width="10" height="10"></svg>
<svg id="counties" class="small" width="10" height="10"></svg>
</div>
<!-- minimal JavaScript //-->
<!-- see script.js for function definitions //-->
<script>
// where to load the files
var baseURL = "https://gist.githubusercontent.com/mbostock/4090846/raw/";
var countryJSON = "us.json";
var stateTSV = "us-state-names.tsv";
var countyTSV = "us-county-names.tsv";
// stores names for all the states and counties
var nameLookup = {};
// select SVG by ID
d3.select("svg#final")
.attr("width", 720)
.attr("height", 484);
// select SVGs by Class
d3.selectAll("svg.small")
.attr("width", 220)
.attr("height", 160);
d3.json(baseURL + countryJSON, function(error, data) {
if (error) {
console.warn(countryJSON, error);
return;
}
console.log(countryJSON, "loaded");
// call draw functions defined in script.js
drawCountry(data, "country");
drawStates(data, "states");
drawCounties(data, "counties");
drawFinal(data, "final");
});
d3.tsv(
baseURL + stateTSV, // url to file in gist
parseStateName, // defined in script.js
function(error, data) {
if (error) {
console.warn(stateTSV, error);
return;
}
// update name lookup
// function defined in script.js
console.log(stateTSV, "loaded");
nameLookup = updateNameLookup(nameLookup, data);
}
);
d3.tsv(
baseURL + countyTSV, // url to file in gist
parseCountyName, // defined in script.js
function(error, data) {
if (error) {
console.warn(countyTSV, error);
return;
}
console.log(countyTSV, "loaded");
// update name lookup
// function defined in script.js
nameLookup = updateNameLookup(nameLookup, data);
}
);
</script>
</body>
</html>
/*
Parses us-county-names.tsv into components.
Used by d3.tsv() function.
*/
function parseCountyName(row) {
return {
id: +row.id,
name: row.name.trim()
};
}
/*
Parses us-state-names.tsv into components.
Used by d3.tsv() function.
*/
function parseStateName(row) {
return {
id: +row.id,
name: row.name.trim(),
code: row.code.trim().toUpperCase()
};
}
/*
Non-robust function to update lookup IDs based
on passed in data. Does no error checking!
*/
function updateNameLookup(lookup, data) {
data.forEach(function(element) {
lookup[element.id] = element.name;
// Lets you lookup the ID of a state
// by its code (2-letter abbreviation)
if (element.hasOwnProperty("code")) {
lookup[element.code] = element.id;
}
});
return lookup;
}
function drawCountry(data, id) {
// get the svg by id
var svg = d3.select("svg#" + id);
// gets the width and height of an html-level element
var bbox = svg.node().getBoundingClientRect();
// get the default albers USA projection
var projection = d3.geo.albersUsa()
.scale(bbox.width)
.translate([bbox.width / 2, bbox.height / 2]);
// get a path generator function for our projection
var path = d3.geo.path().projection(projection);
// add a plot group
var plot = svg.append("g");
// append the land path
plot.append("path")
// get the data from topojson
.datum(topojson.feature(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed({"land": true, "outline": true});
}
function drawStates(data, id) {
// get the svg by id
var svg = d3.select("svg#" + id);
// gets the width and height of an html-level element
var bbox = svg.node().getBoundingClientRect();
// get the default albers USA projection
var projection = d3.geo.albersUsa()
.scale(bbox.width)
.translate([bbox.width / 2, bbox.height / 2]);
// get a path generator function for our projection
var path = d3.geo.path().projection(projection);
// add a plot group
var plot = svg.append("g");
// append the land path
plot.append("path")
// get the data from topojson
.datum(topojson.feature(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed({"land": true, "outline": true});
// use this to plot non-overlapping boundaries only (no fill)
plot.append("path")
.datum(topojson.mesh(data, data.objects.states,
function(a, b) { return a !== b; }))
.attr("d", path)
.classed("state", true);
}
function drawCounties(data, id) {
// get the svg by id
var svg = d3.select("svg#" + id);
// gets the width and height of an html-level element
var bbox = svg.node().getBoundingClientRect();
// get the default albers USA projection
var projection = d3.geo.albersUsa()
.scale(bbox.width)
.translate([bbox.width / 2, bbox.height / 2]);
// get a path generator function for our projection
var path = d3.geo.path().projection(projection);
// add a plot group
var plot = svg.append("g");
// append the land background
plot.append("path")
// get the data from topojson
.datum(topojson.feature(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed("land", true);
// use this to plot fillable shapes
plot.selectAll("path")
.data(topojson.feature(data, data.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
.classed("county", true);
// plot land outline on top
plot.append("path")
// get the data from topojson
.datum(topojson.mesh(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed("outline", true)
.style("fill", "none");
}
function drawFinal(data, id) {
// get the svg by id
var svg = d3.select("svg#" + id);
// gets the width and height of an html-level element
var bbox = svg.node().getBoundingClientRect();
// get the default albers USA projection
var projection = d3.geo.albersUsa()
.scale(bbox.width)
.translate([bbox.width / 2, bbox.height / 2]);
// get a path generator function for our projection
var path = d3.geo.path().projection(projection);
// add a plot group
var plot = svg.append("g");
// append the land background
plot.append("path")
// get the data from topojson
.datum(topojson.feature(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed("land", true);
// use this to plot fillable shapes
plot.selectAll("path")
.data(topojson.feature(data, data.objects.counties).features)
.enter()
.append("path")
.attr("d", path)
.classed("county", true)
.on("click", function(d) {
// assumes nameLookup is defined somewhere
console.log(nameLookup[d.id], d);
});
// plot land outline on top
plot.append("path")
// get the data from topojson
.datum(topojson.mesh(data, data.objects.land))
// call the path generator
.attr("d", path)
.classed("outline", true)
.style("fill", "none");
}
body {
background-color: oldlace;
margin: 8px;
}
div#block {
max-width: 950px;
}
svg {
background-color: white;
margin: 1px;
float: left;
}
.land {
fill: whitesmoke;
}
.outline {
stroke: black;
stroke-width: 1px;
}
.state {
fill: none;
stroke: dimgray;
stroke-width: 1px;
}
.county {
fill: lightgrey;
stroke: whitesmoke;
stroke-width: 1px;
}
path.county:hover {
fill: red;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment