Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rendering only a portion of a topojson map using D3.js

I'm using the D3.js library to create maps from US Census shapefiles. I'm looking to create an entire US map, which is no problem, and a map for each state.

My workflow uses the census data, altered as necessary by ogr2ogr at the command line, then converted into topojson or geojson by shpescape.com, due to errors in the node.js downloading of the topojson module (see below for edited solution to this particular problem).

My question is more of a PRACTICAL question than anything else--when presented with this code (modeled off of http://bl.ocks.org/mbostock/4707858):

            var width = 640,
                height = 500;

            var projection = d3.geo.albers();

            var path = d3.geo.path()
                .projection(projection);

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

            d3.json("mt_geo.json", function(error, mt_topo) {
                var states = topojson.feature(mt_topo, mt_topo.objects.states),
                    state = states.features.filter(function(d) { return d.id === 34; })[0];
                projection
                    .scale(1)
                    .translate([0,0]);
                var b = path.bounds(state),
                    s = .95 / Math.max ((b[1][0]-b[0][0])/width, (b[1][1]-b[0][1])/height),
                    t = [(width-s*(b[1][0]+b[0][0]))/2, (height-s*(b[1][1]+b[0][1]))/2];
                projection
                    .scale(s)
                    .translate(t);
                svg.append("path")
                    .datum(states)
                    .attr("class", "feature")
                    .attr("d", path);
                svg.append("path")
                    .datum(topojson.mesh(us, us.objects.states,function(a, b) {return a !== b;}))
                    .attr("d", path);
                svg.append("path")
                    .datum(state)
                    .attr("class", "outline")
                    .attr("d", path);

Not only does it throw an error at the "var states" line which says "cannot read type of property undefined" -- but I also have no idea what I'm supposed to be passing into the anonymous function, or what mt_topo.objects.states is supposed to refer to. There is no good documentation on this sort of GIS thing. Do all census maps have "state" features? Do you lose that information when you compress the .shp to topojson?

Simply, if d3.json takes (object, function(error, json)), what would an example of that which works actually look like?

EDIT: WORKAROUND AND WINDOWS 7 IDIOSYNCRASIES -----

Most tutorials tell you to use a module from node.js, but I'm on Windows7, and the canonical command-line "npm install -g topojson" fails "at contextify". The creator sent me a nice link to work around said problem.

This is important, because there is a flag in the command line for topojson in which you can package existing features in geojson into an accessible object in topojson. For example, the above code uses "states" in topojson--something that is meaningless and unaccessible unless you use the following command:

topojson -o us.topojson -- states=us_states.json

The space between the double hyphen and states is important. You can then access the states via us.objects.states, as shown in the original code above.

like image 707
kgilvi3 Avatar asked Sep 16 '13 19:09

kgilvi3


1 Answers

You're very close. Without testing your code, I see one major issue. The second parameter of your JSON callback is mt_topo, which you use when defining

var states = topojson.feature(mt_topo, mt_topo.objects.states)

However, later on you use us as your callback object, presumably because that is what Mike Bostock used in the example you cited. Instead, it should be this:

svg.append("path")
 .datum(topojson.mesh(mt_topo, mt_topo.objects.states,function(a, b) {return a !== b;}))
 .attr("d", path);

That said, your question is really getting at whether or not census maps have a "states" feature. My guess is that whatever geometry you are using does not have a states feature, and that is why you are getting an error. When using the topojson command line tool, the feature name (i.e. data.objects.x) is usually whatever the input file was named, so if your file was US_Census_2010.shp, you would want to define states as

var states = topojson.feature(mt_topo, mt_topo.objects.US_Census_2010)

Open up your mt_geo.json file and see what your features are named. Hope that helps!

like image 135
jczaplew Avatar answered Oct 27 '22 13:10

jczaplew