I'm attempting to follow Mike Bostock's process to project to the bounding box of a feature in a topojson file. My topojson file is already projected to Texas State Mapping System (EPSG 3081) from the command line using geoproject:
d3.geoConicConformal().parallels([34 + 55 / 60, 27 + 25 / 60]).rotate([100, -31 - 10 / 60])
However, copying his code exactly and modifying the relevant bits to match my data set results in the error "Uncaught TypeError: path.bounds is not a function" on this line:
var b = path.bounds(state),
Here is my complete code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JS Mapping Project</title>
<script type="text/javascript" src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v1.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<style type="text/css">
body {
background-color: #eee;
}
svg {
background-color: #fff;
border: 1px solid #000;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 1000;
var h = 850;
var projection = d3.geoProjection( function(x, y) {
return [x, y];
});
// Create a path generator.
var path = d3.geo.path()
.projection();
//Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
//Load in GeoJSON data
d3.json("data/topojson/boundary_quantize.json", function(error, json) {
//Add error handling
if (error) throw error;
var states = topojson.feature(json, json.objects.state),
state = states.features.filter(function(d) { return d.properties.NAME === "Texas"; })[0];
projection
.scale(1)
.translate([0, 0]);
// Compute the bounds of a feature of interest, then derive scale & translate.
var b = path.bounds(state),
s = .95 / Math.max((b[1][0] - b[0][0]) / w, (b[1][1] - b[0][1]) / h),
t = [(w - s * (b[1][0] + b[0][0])) / 2, (h - s * (b[1][1] + b[0][1])) / 2];
// Update the projection to use computed scale & translate.
projection
.scale(s)
.translate(t);
svg.append("path")
.attr("stroke"," #000")
.attr("stroke-width", "2")
.attr("d", path(topojson.mesh(json, json.objects.national)));
svg.append("path")
.attr("stroke"," #000")
.attr("stroke-width", "1")
.attr("d", path(topojson.mesh(json, json.objects.state)));
svg.append("path")
.attr("stroke"," #000")
.attr("stroke-width", "0.5")
.attr("d", path(topojson.mesh(json, json.objects.county)));
});
</script>
</body>
</html>
A few things that I've discovered manipulating the code:
If I remove the projection by changing var path = d3.geo.path().projection(); to var path = d3.geo.path();, the error goes away (because the call to the broken code goes away) but the svg draw is broken:
If I change the path definition to var path = d3.geoPath();, suddenly the geometry draws correctly:
This won't work (and I don't know why geoPath() works in the first place) because then the rest of my calls to path fail.
As I was typing this, I realized that I forget the call to my projection variable. I changed .projection(); to .projection(projection);. Now my map looks really weird, but there aren't errors on the path.bounds line as before:
It would seem that my projection definition is wrong, despite using the formula from this StackExchange answer.
I changed the code from geoProjection() to geoIdentity() based on Mike Bostock's response to a comment on his Medium article. My map appeared to be projected, scaled, and centered correctly, but the all black and white color scheme didn't help. I added some quick coloring to the various layers and it now looks very broken:
I then thought maybe it was because I didn't add the ", function(a,b) { return a !== b; }" to the topojson.mesh function, but doing that made things more broken:
I double-checked my topojson file again at mapshaper.org, but my geometry is correct:
At this point I'm stumped. Something is wrong with the way I'm implementing topojson to render my data, but it matches the code I'm seeing in examples.