Search code examples
d3.jsgeojsontopojson

shp2json produces GeoJSON with bounding and coordinates not in (-180,180) range


I am doing @mbostock's tutorial on Command Line Cartography 1. Here is what I've done:

1) Download the zip file

wget https://www.arb.ca.gov/ei/gislib/gislib.htm/ca_air_basins.zip (from California Air Resources Board site)

2) Manually unzip by clicking .zip file (unzip command did not work because downloaded file didn't have 'End-of-central-directory signature')

2) Convert a shapefile to GeoJSON shp2json CaAirBasin.shp -o ca.json

3) Make a GeoJSON projection using California Albers (specified in CaAirBasin.prj file):

geoproject 'd3.geoConicEqualArea().parallels([34, 40.5]).rotate([120, 0]).fitSize([960, 960], d)' < ca.json > ca-albers.json

Result: I get ca-albers.json (223M) that is much bigger than ca.json (6.3M). In the census tract shapefile used in the tutorial, the file size increases from 10 to 14 M.

Update: It seems that problem is with shp2json step instead of geoProject step. Looking at CaAirBasin.shp.xml, there are two sets of bounding,bounding and lbounding. In my case,lbounding and coordinate values that are in 100,000s range is used as opposed to (-180, 180) in the tutorial, making projection fail.

What is a good next step - is there an alternative to shp2json that will work for my case? OR how can I translate my bounding/coordinates to appropriate (-180,180) ones?

Thank you.


Solution

  • Your shapefile is already projected. By passing it through d3.geoConicEqualArea, you are projecting it twice; d3.geoConicEqualArea expects WGS84 (longitude and latitude in degrees) as input, but you’re giving it projected coordinates.

    You can tell that your shapefile is projected by looking at the PRJ file, or by looking at the coordinates of the geometry generated by shp2json: the first point is [-298228.39936644124, 437516.87775637675], which is well outside the expected range of [±180°, ±90°] for longitude and latitude.

    Rather than re-projecting your geometry to a new projection, the easiest thing to do is to just use the projection it’s already in. All you need to do is to translate and scale to fit the desired display size of 960×960. You can use d3.geoIdentity for this.

    geoproject 'd3.geoIdentity().reflectY(true).fitSize([960, 960], d)' < ca.json > ca-albers.json
    

    Setting identity.reflectY will also fix the vertical orientation of the geometry: GIS convention is that +y points up, but Canvas and SVG convention is that +y points down.