2

I'm trying to use geoproject to set the projection on a geojson file. Specifically I'm trying to set the projection to BCalbers (http://spatialreference.org/ref/epsg/3005/)

I see geoproject has a number of projections options i.e.

geoproject 'd3.geoAlbersUsa()' us.json \
  > us-albers.json

but it is possible to set a custom projection using the command line tool? I was hoping something like this would be possible:

geoproject '+proj=aea +lat_1=50 +lat_2=58.5 +lat_0=45 +lon_0=-126 +x_0=1000000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs' build/airzones.geojson \
  > bc-albers.json

but no dice. This works with ogr2ogr

ogr2ogr -f GeoJSON -t_srs "+proj=aea +lat_1=50 +lat_2=58.5 +lat_0=45 +lon_0=-126 +x_0=1000000 +y_0=0 +ellps=GRS80 +datum=NAD83 +units=m +no_defs  " \
Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
brownshoes
  • 321
  • 2
  • 10

1 Answers1

4

D3 geo projections support a range of generic projections (and custom projections), recreating any given specific projection is generally possible. However, when re-creating a projection, d3 projections don't replicate map units*. D3 projections create units in svg coordinate space (the projected coordinates will start from [0,0] which is the top left corner). This allows skipping the step of projecting data (often on the fly with d3), and then rescaling and translating it to show a map.

Compare: Using the referenced d3.geoAlbersUsa(), d3 will project data across roughly 960 pixels along the x axis if using the default scale. Perfect for unmodified use in an SVG - coordinates could be used as straight SVG coordinates. Using a BC Albers in ArcGIS or QGIS will project data across millions of meters.

Units aside, however, you can recreate a BC Albers proportionally scaled for web preserving the shape, distance, direction, and area of a regular BC Albers. But as d3 doesn't take projection definitions such as .prj files or other definitions, you need to use a d3's projection methods and the appropriate parameters.

For a BC Albers your parallels are: 50 and 58.5, your central longitude is -126, and your projection type is an Albers. This is all you need - the (false) easting/northing reference is to recreate map units - which should not generally be needed in a web scenario (if you do, using a more complete GIS platform would be more appropriate).

So, to set the projection, you would use:

d3.geoAlbers()
  .center([0,y])
  .rotate([-x,0])
  .parallels([a,b])
  .scale(k)

Where

x = center longitude (negative because we rotate the globe under the map)

a, b = standard parallels

k = scale factor(for a d3.geoAlbers(): whole world is 960 px across with a scale of 1070, the default scale, larger numbers expand this/zoom in)

y = centering latitude.

Note: y does not alter the projection, it merely translates it - the y reference for a BC Albers is south of BC, it is just a reference for northings, as it has no impact on map shape, area, distance, or direction. For a BC Albers, I would probably choose 50.5 as it is half way between the Yukon border and the Washington border which are the northern and southern limits of BC (well, excepting Vancouver Island and some of the Gulf Islands, so let's say 50 degrees north, sorry I forgot about you Victoria).

Also remember the the projection functionality of d3 assumes data that is unprojected (or "projected" in WGS84), consisting of long lat pairs.

You can see how Mike Bostock uses these methods in his command line cartography article here:

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

fitSize in this exmaple scales and translates the features to the specified bounding box - this translate and scale does not alter the projection parameters, and like the y coordinate in the center method, does not alter distance, area, shape, or angle (well, distance and area remain proportional to a proper BC Albers).

*You could recreate map units (false eastings/northings might require some custom projection work), but this is not the platform for it really, it would be easier to use many other platforms.

See also this question and answer:Converting EPSG projection bounds to a D3.js.

Andrew Reid
  • 37,021
  • 7
  • 64
  • 83
  • 1
    I told you once and I'll tell you again: create a tag `d3-geo`, or anything along this line, with a good description. Not only this is nice for future users ask/locate D3 questions related to geography, but you will quickly become the top answerer. – Gerardo Furtado Aug 29 '17 at 03:14
  • I remember, I'm getting around to it - that and removing links to the deceased SO documentation (which are generally d3 projection topics as well), will, of course, dig up the excellent answers from others for a d3 projection/geo tag too. – Andrew Reid Aug 29 '17 at 03:39
  • Thanks for your thoughtful answer, and not forgetting about us folk in Victoria! Very nice to have a clear explanation. I was over thinking things. – brownshoes Aug 29 '17 at 03:58
  • @AndrewReid What a nice answer, again! However, I don't think the interpretation of the scale factor `k` being the *"width of projection in pixels"* is correct or am I missing something? For the `d3.geoAlbers()` projection the scale defaults to *1070* whereas the default width equals *960*. – altocumulus Aug 29 '17 at 08:48
  • @altocumulus, yep, you are right, updating the answer now, thanks for noting it. – Andrew Reid Aug 29 '17 at 14:12
  • @AndrewReid I'm afraid, this is not correct either. I remember the scale factor causing lots of confusion resulting in various questions on SO. Have a look at this [example](http://blockbuilder.org/anonymous/53d2d40c1cd989b212ce3d64fc422858) requiring a scale of roughly 110 to render the entire world map. Additionally, the [docs](https://github.com/d3/d3-geo#projection_scale) state that *"absolute scale factors are not equivalent across projections"*. Actually, I have never come across an explanation I considered helpful or plausible. There is still some magic to it... – altocumulus Aug 29 '17 at 14:51
  • thanks again. I created a follow up question regarding aligning preset projections (using command line tools) with projections made on the fly in d3. https://stackoverflow.com/questions/45947562/aligning-topojson-file-with-preset-projection-with-points-with-point-locations-l – brownshoes Aug 29 '17 at 20:08
  • @altocumulus, yes, scales are not consistent across projection types, my edit about scale here is specific to Albers projections (likewise, the parallels method is only relevant for conical projections). This makes my initial statement rather embarrassing, as not only was I recalling a Mercator projection, I recalled it incorrectly (default scale is 360 degrees mapped to 961 pixels: 961/2π). The only constant I know of in terms of scale is larger values are more zoomed in. If I understand right, I'm unclear in the default being specific to Albers? I've updated based on this understanding. – Andrew Reid Aug 30 '17 at 02:42