Skip to content

Instantly share code, notes, and snippets.

@arrayjam
Last active Nov 16, 2019
Embed
What would you like to do?
Australia Postcode Decoder
<!DOCTYPE html>
<meta charset="utf-8">
<style>
text, label {
font-family: sans-serif;
}
label {
position: absolute;
text-align: center;
font-weight: bold;
padding: 0 10px;
line-height: 50px;
}
input {
text-align: center;
width: 5em;
padding: 5px;
}
.feature {
stroke: none;
}
.divider {
stroke: #dedede;
stroke-width: 0.5px;
}
</style>
<body>
<label for="postcode">
Enter a Postcode:
<input id="postcode" autofocus maxlength=4 placeholder="Postcode" type="text" />
</label>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v0.min.js"></script>
<script>
var width = 750,
height = 700;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height + 50);
var g = svg.append("g")
.attr("transform", "translate(0, 50)")
.append("g");
var color = d3.scale.category10();
var loading = svg.append("text")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("font-size", 40)
.text("Loading...");
d3.json("b53aa41b4c61df6272fd67fd2691517e87af63a5/postcodes.json", function(error, postcodes) {
var postcodesgeo = topojson.object(postcodes, postcodes.objects.postcodesgeo).geometries;
var mainlandWidth = 236;
var leftMargin = 186;
var rightMargin = 64;
var allAus = mainlandWidth + leftMargin + rightMargin;
var translateWidth = width / (allAus / mainlandWidth);
var projection = d3.geo.albers()
.translate([translateWidth, height / 2])
.scale(1100)
.rotate([-132.5, 0])
.center([0, -26.5]) // Center of Australia, accounting for tasmania
.parallels([-36, -18]);
var path = d3.geo.path()
.projection(projection);
var input = d3.select("input")
.on("cut", change)
.on("paste", change)
.on("change", change)
.on("keyup", change);
var feature = g.selectAll("path")
.data(postcodesgeo)
.enter().append("path")
.attr("class", "feature")
.attr("id", function (d, i) {
// Here we can compute "id" using datums
console.log(d);
return i;
})
.attr("d", path);
loading.remove();
svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("height", 50)
.attr("width", width)
.style("fill", "white");
svg.append("line")
.attr("class", "divider")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", 51)
.attr("y2", 51);
var legends = svg.append("g").attr("class", "legends");
svg.selectAll("text.legend")
.data([0,1,2,3,4,5,6,7,8,9])
.enter().append("text")
.attr("class", "legend")
.attr("y", 25)
.attr("x", function(d) { return width - (10 - d) * 50 + 25; })
.style("fill", "white")
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("font-size", 20)
.text(String);
var prefix = "POA";
var selectedPostcode;
change();
function updateLegend(legend) {
var container = legends.selectAll("rect.legend")
.data(legend, function(d) { return d; });
container
.transition().duration(750)
.attr("x", function(d) { return width - (10 - d) * 50; })
.style("fill", function(d) { return color(d); })
.attr("y", 0);
container.enter().append("rect")
.attr("class", "legend")
.attr("y", -50)
.attr("width", 50)
.attr("height", 50)
.attr("x", function(d) { return width - (10 - d) * 50; })
.transition()
.duration(750)
.ease("bounce")
.style("fill", function(d) { return color(d); })
.attr("y", 0);
container.exit().transition().duration(400)
.ease("backs")
.attr("y", -50)
.remove();
}
function setFoundStyle(selection, postcode) {
selection.style("fill", color(postcode.slice(-1)));
}
function unzoom(selection) {
selection.transition().duration(750).attr("transform", "");
}
function zoom(selection, b, maxScale) {
var scale = 0.95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height);
var usedScale = Math.min(scale, maxScale);
selection.transition().duration(750).attr("transform",
"translate(" + projection.translate() + ")" +
"scale(" + usedScale + ")" +
"translate(" + -(b[1][0] + b[0][0]) / 2 + "," + -(b[1][1] + b[0][1]) / 2 + ")");
}
function change() {
var lastPostcode = selectedPostcode;
selectedPostcode = input.property("value");
if (lastPostcode === selectedPostcode) {
return;
}
var shouldUnzoom = selectedPostcode === "";
var shouldZoom = selectedPostcode.match(/\d{1,4}/);
if (!shouldUnzoom && !shouldZoom) {
return;
}
var matchString = prefix + selectedPostcode;
var findBounds = findMinMaxBounds();
var bounds;
var legendObj = {};
var matching = feature.filter(function(d) {
var match = matchingFeature(d, matchString);
var next = nextDigit(d, matchString);
this.style.fill = match ? color(+next) : "#222";
if (match) {
if (next) { legendObj[+next] = true; }
bounds = findBounds(path.bounds(d));
}
return match;
});
updateLegend(d3.keys(legendObj).map(function(d) { return +d; }));
if (matching[0].length === 0) { return; }
if (selectedPostcode.length === 4 && matching[0].length === 1) { matching.call(setFoundStyle, matchString); }
var zoomers = function() {
if (shouldZoom) { g.call(zoom, bounds, 100); }
if (shouldUnzoom) { g.call(unzoom); }
};
setTimeout(zoomers, 0);
}
function nextDigit(feature, string) {
return feature.id.charAt(string.length);
}
function matchingFeature(feature, string) {
return feature.id.indexOf(string) !== -1;
}
function findMinMaxBounds() {
var topBound = -Infinity;
var rightBound = -Infinity;
var bottomBound = Infinity;
var leftBound = Infinity;
return function (bound) {
leftBound = Math.min(bound[0][0], leftBound);
bottomBound = Math.min(bound[0][1], bottomBound);
rightBound = Math.max(bound[1][0], rightBound);
topBound = Math.max(bound[1][1], topBound);
return [[leftBound, bottomBound], [rightBound, topBound]];
};
}
});
d3.select(self.frameElement).style("height", height + "px");
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment