Help with Lambert Conformal Conic projection

Hello!

I have a beginners question about geo projections, I hoped I could get some help with :slight_smile:

I’m trying to emulate the projection used in this map: http://www.maps-of-the-world.net/maps/maps-of-europe/large-detailed-political-map-of-Europe-with-capitals-and-major-cities-2008.jpg, and currently have the following:

The map itself says it uses the Lambert Conformal Conic projection with standard parallels 40N and 68N, but if I use d3.geoConicConformal().parallels([40, 68]), it doesn’t match up exactly. The map still have to be rotated a bit.

Is this because I’m misunderstanding the projection? I would think “Lam. Conf. Conic” + the standard parallels fully specifies the projection. Is this correct and did the map-makers simply rotate afterwards? (I’m able to “manually” rotate the thing together, but that doesn’t feel right.)

I’m also unsure whether I should be using the projection’s .translate(...) or .center(...)

Ok, it turns out that I essentially just had to ‘pre-compose’ the projection with a rotation, i.e. the final projection is to first ([lon, lat]) => [lon-10, lat], and then d3.geoConicConformal().parallels([40, 68]). (I’m not sure what’s the canonical way to do this in d3, because these projections both have to implement a stream method, as well as being functions themselves, so I had to manually write out the composition.) See the updated notebook.

(Also, the map seems just a slight little bit off — I believe this is a ‘problem’ with the map (being every so slightly skewed), not with the projection.)

You shouldn’t need to write a custom projection for this; projection.rotate seems to do the trick:

projection = d3.geoConicConformal()
    .parallels([40, 68])
    .rotate([-9.05, 0])
    .scale(1690)
    .translate([472, 2390])

As to projection.translate and projection.center, they work together to specify where a point in spherical coordinates: the (rotated) center in [longitude, latitude]) appears in the viewpoint at the translate position in [x, y]. So for example, ignoring rotation for a moment, if you wanted to position Bremen 53.0793° N, 8.8017° E at the middle of the viewport, you would say:

projection = d3.geoConicConformal()
    .parallels([40, 68])
    .center([8.8017, 53.0793])
    .scale(1690)
    .translate([width / 2, height / 2])

If you have a rotation, the center is in pre-rotation coordinates, so you have to make a little adjustment:

projection = d3.geoConicConformal()
    .parallels([40, 68])
    .rotate([-9.05, 0])
    .center([8.8017 - 9.05, 53.0793])
    .scale(1690)
    .translate([width / 2, height / 2])

But often projections have a central meridian that is used both for the rotation and the center, which means that you typically end up with a center longitude of 0, and a center latitude half-way between the two standard parallels. You can then nudge the translate slightly to align your maps:

projection = d3.geoConicConformal()
    .parallels([40, 68])
    .rotate([-9.05, 0])
    .center([0, 54])
    .scale(1690)
    .translate([width / 2 - 7, height / 2 + 4])

I probably have some code around somewhere to determine the translate, center and scale automatically from two points…

2 Likes

Thanks so much!

I apparently didn’t get the .rotate() api, and was just trying to put in a single number /: Also, the way .center() and .translate() work on the two respective coordinate systems is actually quite obvious/logical, I just hadn’t thought of it. Thanks for pointing this out :slight_smile: