Skip to content

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.

Fork
js
Plot.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.

Fork
js
Plot.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.)

Fork
js
Plot.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.

Fork
js
Plot.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()
  ]
})
js
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.

Fork
js
Plot.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.

Fork
js
Plot.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.

Fork
js
Plot.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.