Projections ^0.6.1
A projection maps abstract coordinates in x and y to pixel positions on screen. Most often, abstract coordinates are spherical (degrees longitude and latitude), as when rendering a geographic map. For example, below we show earthquakes in the last seven days with a magnitude of 2.5 or higher as reported by the USGS. Use the slider to adjust the orthographic projection’s center of longitude.
ForkPlot.plot({
projection: {type: "orthographic", rotate: [-longitude, -30]},
r: {transform: (d) => Math.pow(10, d)}, // convert Richter to amplitude
marks: [
Plot.geo(land, {fill: "currentColor", fillOpacity: 0.2}),
Plot.sphere(),
Plot.dot(earthquakes, {x: "longitude", y: "latitude", r: "magnitude", stroke: "red", fill: "red", fillOpacity: 0.2})
]
})
Above, a geo mark draws polygons representing land and a sphere mark draws the outline of the globe. A dot mark draws earthquakes as circles sized by magnitude.
The geo mark is “projection aware” so that it can handle all the nuances of projecting spherical polygons to the screen — leaning on d3-geo to provide adaptive sampling with configurable precision, antimeridian cutting, and clipping. The dot mark is not; instead, Plot applies the projection in place of the x and y scales. Hence, projections work with any mark that consumes continuous x and y channels — as well as marks that use x1 & y1 and x2 & y2. Each mark implementation decides whether to handle projections specially or to treat the projection as any other position scale. (For example, the line mark is projection-aware to draw geodesics.)
INFO
Marks that require band scales (bars, cells, and ticks) cannot be used with projections. Likewise one-dimensional marks such as rules cannot be used, though see #1164.
Plot provides a variety of built-in projections. And as above, all world projections can be rotated to show a different aspect.
ForkPlot.plot({
projection: "equirectangular",
marks: [
Plot.graticule(),
Plot.geo(land, {fill: "currentColor"}),
Plot.sphere()
]
})
Why so many? Each projection has its strengths and weaknesses:
- conformal projections preserve angles and local shape,
- equal-area projections preserve area (use these for choropleths),
- equidistant projections preserve distance from one (or two) points,
- azimuthal projections expand radially from a central feature,
- cylindrical projections have symmetry around the axis of rotation,
- the stereographic projection preserves circles, and
- the gnomonic projection displays all great circles as straight lines!
No single projection is best at everything. It is impossible, for example, for a projection to be both conformal and equal-area.
In addition to world projections, Plot provides the U.S.-centric albers-usa conic equal-area projection with an inset of Alaska and Hawaii. (Note that the scale for Alaska is diminished: it is projected at 0.35× its true relative area.)
ForkPlot.plot({
projection: "albers-usa",
marks: [
Plot.geo(nation),
Plot.geo(statemesh, {strokeOpacity: 0.2})
]
})
TIP
Use the albers-usa projection for U.S.-centric choropleth maps.
For maps that focus on a specific region, use the domain option to zoom in. This object should be a GeoJSON object. For example, you can use d3.geoCircle to generate a circle of a given radius centered at a given longitude and latitude. You can also use the inset options for a bit of padding around the domain.
ForkPlot.plot({
projection: {
type: "azimuthal-equidistant",
rotate: [-9, -34],
domain: circle,
inset: 10
},
marks: [
Plot.graticule(),
Plot.geo(land, {fill: "currentColor", fillOpacity: 0.3}),
Plot.geo(circle, {stroke: "red", strokeWidth: 2}),
Plot.frame()
]
})
circle = d3.geoCircle().center([9, 34]).radius(radius)()
If none of Plot’s built-in projections meet your needs, you can use any of D3’s extended projections by specifying the projection option as a function that returns a D3 projection. Below, a map of Antarctica in a polar aspect of the azimuthal-equidistant projection.
ForkPlot.plot({
width: 688,
height: 688,
projection: ({width, height}) => d3.geoAzimuthalEquidistant()
.rotate([0, 90])
.translate([width / 2, height / 2])
.scale(width)
.clipAngle(40),
marks: [
Plot.graticule(),
Plot.geo(land, {fill: "currentColor"}),
Plot.frame()
]
})
While this notebook mostly details spherical projections, you can use the identity projection to display planar geometry. For example, below we draw a schematic of the second floor of the Westport House in Dundee, Ireland.
ForkPlot.geo(westport).plot({projection: {type: "identity", domain: westport}})
TIP
There’s also a reflect-y projection in case y points up↑, which is often the case with projected reference systems.
Naturally, Plot’s projection system is compatible with its faceting system. Below, a comic strip of sorts shows the locations of Walmart store openings in past decades.
ForkPlot.plot({
marginLeft: 0,
marginRight: 0,
projection: "albers",
fx: {
interval: "10 years",
tickFormat: (d) => `${d.getUTCFullYear()}’s`,
label: null
},
marks: [
Plot.geo(statemesh, {strokeOpacity: 0.1}),
Plot.geo(nation),
Plot.dot(walmarts, {fx: "date", x: "longitude", y: "latitude", r: 1, fill: "currentColor"})
]
})
INFO
This uses the interval scale option to bin temporal data into facets by decade.
To learn more about mapping with Plot, see our hands-on tutorials:
Projection options
The projection plot option applies a two-dimensional (often geographic) projection in place of x and y scales. It is typically used in conjunction with a geo mark to produce a map, but can be used with any mark that supports x and y channels, such as dot, text, arrow, and rect. For marks that use x1, y1, x2, and y2 channels, the two projected points are ⟨x1, y1⟩ and ⟨x2, y2⟩; otherwise, the projected point is ⟨x, y⟩.
The following built-in named projections are supported:
- equirectangular - the equirectangular, or plate carrée, projection
- orthographic - the orthographic projection
- stereographic - the stereographic projection
- mercator - the Mercator projection
- equal-earth - the Equal Earth projection by Šavrič et al.
- azimuthal-equal-area - the azimuthal equal-area projection
- azimuthal-equidistant - the azimuthal equidistant projection
- conic-conformal - the conic conformal projection
- conic-equal-area - the conic equal-area projection
- conic-equidistant - the conic equidistant projection
- gnomonic - the gnomonic projection
- transverse-mercator - the transverse Mercator projection
- albers - the Albers’ conic equal-area projection
- albers-usa - a composite Albers conic equal-area projection suitable for the United States
- identity - the identity projection for planar geometry
- reflect-y - like the identity projection, but y points up
- null (default) - the null projection for pre-projected geometry in screen coordinates
In addition to these named projections, the projection option may be specified as a D3 projection, or any custom projection that implements projection.stream, or a function that receives a configuration object ({width, height, ...options}) and returns such a projection. In the last case, the width and height represent the frame dimensions minus any insets.
If the projection option is specified as an object, the following additional projection options are supported:
- type - one of the projection names above
- parallels - the standard parallels (for conic projections only)
- precision - the sampling threshold
- rotate - a two- or three- element array of Euler angles to rotate the sphere
- domain - a GeoJSON object to fit in the center of the (inset) frame
- inset - inset by the given amount in pixels when fitting to the frame (default zero)
- insetLeft - inset from the left edge of the frame (defaults to inset)
- insetRight - inset from the right edge of the frame (defaults to inset)
- insetTop - inset from the top edge of the frame (defaults to inset)
- insetBottom - inset from the bottom edge of the frame (defaults to inset)
- clip - the projection clipping method
The following projection clipping methods are supported for clip:
- frame or true (default) - clip to the extent of the frame (including margins but not insets)
- a number - clip to a great circle of the given radius in degrees centered around the origin
- null or false - do not clip
Whereas the clip mark option is implemented using SVG clipping, the clip projection option affects the generated geometry and typically produces smaller SVG output.