d3-geo

Map projections are sometimes implemented as point transformations. For instance, spherical Mercator:

function mercator(x, y) {
  return [x, Math.log(Math.tan(Math.PI / 4 + y / 2))];
}

This is a reasonable mathematical approach if your geometry consists of continuous, infinite point sets. Yet computers do not have infinite memory, so we must instead work with discrete geometry such as polygons and polylines!

Discrete geometry makes the challenge of projecting from the sphere to the plane much harder. The edges of a spherical polygon are geodesics (segments of great circles), not straight lines. Projected to the plane, geodesics are curves in all map projections except gnomonic, and thus accurate projection requires interpolation along each arc. D3 uses adaptive sampling inspired by a popular line simplification method to balance accuracy and performance.

The projection of polygons and polylines must also deal with the topological differences between the sphere and the plane. Some projections require cutting geometry that crosses the antimeridian, while others require clipping geometry to a great circle.

Spherical polygons also require a winding order convention to determine which side of the polygon is the inside: the exterior ring for polygons smaller than a hemisphere must be clockwise, while the exterior ring for polygons larger than a hemisphere must be anticlockwise. Interior rings representing holes must use the opposite winding order of their exterior ring. This winding order convention is also used by TopoJSON and ESRI shapefiles; however, it is the opposite convention of GeoJSON’s RFC 7946. (Also note that standard GeoJSON WGS84 uses planar equirectangular coordinates, not spherical coordinates, and thus may require stitching to remove antimeridian cuts.)

D3’s approach affords great expressiveness: you can choose the right projection, and the right aspect, for your data. D3 supports a wide variety of common and unusual map projections. For more, see Part 2 of The Toolmaker’s Guide.

D3 uses GeoJSON to represent geographic features in JavaScript. (See also TopoJSON, an extension of GeoJSON that is significantly more compact and encodes topology.) To convert shapefiles to GeoJSON, use shp2json, part of the shapefile package. See Command-Line Cartography for an introduction to d3-geo and related tools.

Installing

If you use NPM, npm install d3-geo. Otherwise, download the latest release. You can also load directly from d3js.org, either as a standalone library or as part of D3. AMD, CommonJS, and vanilla environments are supported. In vanilla, a d3 global is exported:

<script src="https://d3js.org/d3-array.v2.min.js"></script>
<script src="https://d3js.org/d3-geo.v2.min.js"></script>
<script>

var projection = d3.geoEqualEarth(),
    path = d3.geoPath(projection);

</script>

Try d3-geo in your browser.

API Reference

Paths

The geographic path generator, d3.geoPath, is similar to the shape generators in d3-shape: given a GeoJSON geometry or feature object, it generates an SVG path data string or renders the path to a Canvas. Canvas is recommended for dynamic or interactive projections to improve performance. Paths can be used with projections or transforms, or they can be used to render planar geometry directly to Canvas or SVG.

d3.geoPath([projection[, context]]) Source

Creates a new geographic path generator with the default settings. If projection is specified, sets the current projection. If context is specified, sets the current context.

path(object[, arguments…]) Source

Renders the given object, which may be any GeoJSON feature or geometry object:

  • Point - a single position.
  • MultiPoint - an array of positions.
  • LineString - an array of positions forming a continuous line.
  • MultiLineString - an array of arrays of positions forming several lines.
  • Polygon - an array of arrays of positions forming a polygon (possibly with holes).
  • MultiPolygon - a multidimensional array of positions forming multiple polygons.
  • GeometryCollection - an array of geometry objects.
  • Feature - a feature containing one of the above geometry objects.
  • FeatureCollection - an array of feature objects.

The type Sphere is also supported, which is useful for rendering the outline of the globe; a sphere has no coordinates. Any additional arguments are passed along to the pointRadius accessor.

To display multiple features, combine them into a feature collection:

svg.append("path")
    .datum({type: "FeatureCollection", features: features})
    .attr("d", d3.geoPath());

Or use multiple path elements:

svg.selectAll("path")
  .data(features)
  .enter().append("path")
    .attr("d", d3.geoPath());

Separate path elements are typically slower than a single path element. However, distinct path elements are useful for styling and interaction (e.g., click or mouseover). Canvas rendering (see path.context) is typically faster than SVG, but requires more effort to implement styling and interaction.

path.area(object) Source

Returns the projected planar area (typically in square pixels) for the specified GeoJSON object. Point, MultiPoint, LineString and MultiLineString geometries have zero area. For Polygon and MultiPolygon geometries, this method first computes the area of the exterior ring, and then subtracts the area of any interior holes. This method observes any clipping performed by the projection; see projection.clipAngle and projection.clipExtent. This is the planar equivalent of d3.geoArea.

path.bounds(object) Source

Returns the projected planar bounding box (typically in pixels) for the specified GeoJSON object. The bounding box is represented by a two-dimensional array: [[x₀, y₀], [x₁, y₁]], where x₀ is the minimum x-coordinate, y₀ is the minimum y-coordinate, x₁ is maximum x-coordinate, and y₁ is the maximum y-coordinate. This is handy for, say, zooming in to a particular feature. (Note that in projected planar coordinates, the minimum latitude is typically the maximum y-value, and the maximum latitude is typically the minimum y-value.) This method observes any clipping performed by the projection; see projection.clipAngle and projection.clipExtent. This is the planar equivalent of d3.geoBounds.

path.centroid(object) Source

Returns the projected planar centroid (typically in pixels) for the specified GeoJSON object. This is handy for, say, labeling state or county boundaries, or displaying a symbol map. For example, a noncontiguous cartogram might scale each state around its centroid. This method observes any clipping performed by the projection; see projection.clipAngle and projection.clipExtent. This is the planar equivalent of d3.geoCentroid.

path.measure(object) Source

Returns the projected planar length (typically in pixels) for the specified GeoJSON object. Point and MultiPoint geometries have zero length. For Polygon and MultiPolygon geometries, this method computes the summed length of all rings. This method observes any clipping performed by the projection; see projection.clipAngle and projection.clipExtent. This is the planar equivalent of d3.geoLength.

path.projection([projection]) Source

If a projection is specified, sets the current projection to the specified projection. If projection is not specified, returns the current projection, which defaults to null. The null projection represents the identity transformation: the input geometry is not projected and is instead rendered directly in raw coordinates. This can be useful for fast rendering of pre-projected geometry, or for fast rendering of the equirectangular projection.

The given projection is typically one of D3’s built-in geographic projections; however, any object that exposes a projection.stream function can be used, enabling the use of custom projections. See D3’s transforms for more examples of arbitrary geometric transformations.

path.context([context]) Source

If context is specified, sets the current render context and returns the path generator. If the context is null, then the path generator will return an SVG path string; if the context is non-null, the path generator will instead call methods on the specified context to render geometry. The context must implement the following subset of the CanvasRenderingContext2D API:

  • context.beginPath()
  • context.moveTo(x, y)
  • context.lineTo(x, y)
  • context.arc(x, y, radius, startAngle, endAngle)
  • context.closePath()

If a context is not specified, returns the current render context which defaults to null.

path.pointRadius([radius]) Source

If radius is specified, sets the radius used to display Point and MultiPoint geometries to the specified number. If radius is not specified, returns the current radius accessor, which defaults to 4.5. While the radius is commonly specified as a number constant, it may also be specified as a function which is computed per feature, being passed the any arguments passed to the path generator. For example, if your GeoJSON data has additional properties, you might access those properties inside the radius function to vary the point size; alternatively, you could d3.symbol and a projection for greater flexibility.

Projections

Projections transform spherical polygonal geometry to planar polygonal geometry. D3 provides implementations of several classes of standard projections:

For many more projections, see d3-geo-projection. You can implement custom projections using d3.geoProjection or d3.geoProjectionMutator.

projection(point) Source

Returns a new array [x, y] (typically in pixels) representing the projected point of the given point. The point must be specified as a two-element array [longitude, latitude] in degrees. May return null if the specified point has no defined projected position, such as when the point is outside the clipping bounds of the projection.

projection.invert(point) Source

Returns a new array [longitude, latitude] in degrees representing the unprojected point of the given projected point. The point must be specified as a two-element array [x, y] (typically in pixels). May return null if the specified point has no defined projected position, such as when the point is outside the clipping bounds of the projection.

This method is only defined on invertible projections.

projection.stream(stream) Source

Returns a projection stream for the specified output stream. Any input geometry is projected before being streamed to the output stream. A typical projection involves several geometry transformations: the input geometry is first converted to radians, rotated on three axes, clipped to the small circle or cut along the antimeridian, and lastly projected to the plane with adaptive resampling, scale and translation.

projection.preclip([preclip])

If preclip is specified, sets the projection’s spherical clipping to the specified function and returns the projection. If preclip is not specified, returns the current spherical clipping function (see preclip).

projection.postclip([postclip])

If postclip is specified, sets the projection’s cartesian clipping to the specified function and returns the projection. If postclip is not specified, returns the current cartesian clipping function (see postclip).

projection.clipAngle([angle]) Source

If angle is specified, sets the projection’s clipping circle radius to the specified angle in degrees and returns the projection. If angle is null, switches to antimeridian cutting rather than small-circle clipping. If angle is not specified, returns the current clip angle which defaults to null. Small-circle clipping is independent of viewport clipping via projection.clipExtent.

See also projection.preclip, d3.geoClipAntimeridian, d3.geoClipCircle.

projection.clipExtent([extent]) Source

If extent is specified, sets the projection’s viewport clip extent to the specified bounds in pixels and returns the projection. The extent bounds are specified as an array [[x₀, y₀], [x₁, y₁]], where x₀ is the left-side of the viewport, y₀ is the top, x₁ is the right and y₁ is the bottom. If extent is null, no viewport clipping is performed. If extent is not specified, returns the current viewport clip extent which defaults to null. Viewport clipping is independent of small-circle clipping via projection.clipAngle.

See also projection.postclip, d3.geoClipRectangle.

projection.scale([scale]) Source

If scale is specified, sets the projection’s scale factor to the specified value and returns the projection. If scale is not specified, returns the current scale factor; the default scale is projection-specific. The scale factor corresponds linearly to the distance between projected points; however, absolute scale factors are not equivalent across projections.

projection.translate([translate]) Source

If translate is specified, sets the projection’s translation offset to the specified two-element array [tx, ty] and returns the projection. If translate is not specified, returns the current translation offset which defaults to [480, 250]. The translation offset determines the pixel coordinates of the projection’s center. The default translation offset places ⟨0°,0°⟩ at the center of a 960×500 area.

projection.center([center]) Source

If center is specified, sets the projection’s center to the specified center, a two-element array of [longitude, latitude] in degrees and returns the projection. If center is not specified, returns the current center, which defaults to ⟨0°,0°⟩.

projection.angle([angle]) Source

If angle is specified, sets the projection’s post-projection planar rotation angle to the specified angle in degrees and returns the projection. If angle is not specified, returns the projection’s current angle, which defaults to 0°. Note that it may be faster to rotate during rendering (e.g., using context.rotate) rather than during projection.

projection.reflectX([reflect])

If reflect is specified, sets whether or not the x-dimension is reflected (negated) in the output. If reflect is not specified, returns true if x-reflection is enabled, which defaults to false. This can be useful to display sky and astronomical data with the orb seen from below: right ascension (eastern direction) will point to the left when North is pointing up.

projection.reflectY([reflect])

If reflect is specified, sets whether or not the y-dimension is reflected (negated) in the output. If reflect is not specified, returns true if y-reflection is enabled, which defaults to false. This is especially useful for transforming from standard spatial reference systems, which treat positive y as pointing up, to display coordinate systems such as Canvas and SVG, which treat positive y as pointing down.

projection.rotate([angles]) Source

If rotation is specified, sets the projection’s three-axis spherical rotation to the specified angles, which must be a two- or three-element array of numbers [lambda, phi, gamma] specifying the rotation angles in degrees about each spherical axis. (These correspond to yaw, pitch and roll.) If the rotation angle gamma is omitted, it defaults to 0. See also d3.geoRotation. If rotation is not specified, returns the current rotation which defaults [0, 0, 0].

projection.precision([precision]) Source

If precision is specified, sets the threshold for the projection’s adaptive resampling to the specified value in pixels and returns the projection. This value corresponds to the Douglas–Peucker distance. If precision is not specified, returns the projection’s current resampling precision which defaults to √0.5 ≅ 0.70710…

projection.fitExtent(extent, object) Source

Sets the projection’s scale and translate to fit the specified GeoJSON object in the center of the given extent. The extent is specified as an array [[x₀, y₀], [x₁, y₁]], where x₀ is the left side of the bounding box, y₀ is the top, x₁ is the right and y₁ is the bottom. Returns the projection.

For example, to scale and translate the New Jersey State Plane projection to fit a GeoJSON object nj in the center of a 960×500 bounding box with 20 pixels of padding on each side:

var projection = d3.geoTransverseMercator()
    .rotate([74 + 30 / 60, -38 - 50 / 60])
    .fitExtent([[20, 20], [940, 480]], nj);

Any clip extent is ignored when determining the new scale and translate. The precision used to compute the bounding box of the given object is computed at an effective scale of 150.

projection.fitSize(size, object) Source

A convenience method for projection.fitExtent where the top-left corner of the extent is [0, 0]. The following two statements are equivalent:

projection.fitExtent([[0, 0], [width, height]], object);
projection.fitSize([width, height], object);
projection.fitWidth(width, object) Source

A convenience method for projection.fitSize where the height is automatically chosen from the aspect ratio of object and the given constraint on width.

projection.fitHeight(height, object) Source

A convenience method for projection.fitSize where the width is automatically chosen from the aspect ratio of object and the given constraint on height.

Azimuthal Projections

Azimuthal projections project the sphere directly onto a plane.

d3.geoAzimuthalEqualArea() Source
d3.geoAzimuthalEqualAreaRaw

The azimuthal equal-area projection.

d3.geoAzimuthalEquidistant() Source
d3.geoAzimuthalEquidistantRaw

The azimuthal equidistant projection.

d3.geoGnomonic() Source
d3.geoGnomonicRaw

The gnomonic projection.

d3.geoOrthographic() Source
d3.geoOrthographicRaw

The orthographic projection.

d3.geoStereographic() Source
d3.geoStereographicRaw

The stereographic projection.

Equal-Earth

d3.geoEqualEarth() Source
d3.geoEqualEarthRaw

The Equal Earth projection, by Bojan Šavrič et al., 2018.

Composite Projections

Composite consist of several projections that are composed into a single display. The constituent projections have fixed clip, center and rotation, and thus composite projections do not support projection.center, projection.rotate, projection.clipAngle, or projection.clipExtent.

d3.geoAlbersUsa() Source

This is a U.S.-centric composite projection of three d3.geoConicEqualArea projections: d3.geoAlbers is used for the lower forty-eight states, and separate conic equal-area projections are used for Alaska and Hawaii. Note that the scale for Alaska is diminished: it is projected at 0.35× its true relative area. This diagram by Philippe Rivière illustrates how this projection uses two rectangular insets for Alaska and Hawaii:

See Albers USA with Territories for an extension to all US territories, and d3-composite-projections for more examples.

Conic Projections

Conic projections project the sphere onto a cone, and then unroll the cone onto the plane. Conic projections have two standard parallels.

conic.parallels([parallels]) Source

The two standard parallels that define the map layout in conic projections.

d3.geoAlbers() Source

The Albers’ equal area-conic projection. This is a U.S.-centric configuration of d3.geoConicEqualArea.

d3.geoConicConformal() Source
d3.geoConicConformalRaw(phi0, phi1) Source

The conic conformal projection. The parallels default to [30°, 30°] resulting in flat top. See also conic.parallels.

d3.geoConicEqualArea() Source
d3.geoConicEqualAreaRaw(phi0, phi1) Source

The Albers’ equal-area conic projection. See also conic.parallels.

d3.geoConicEquidistant() Source
d3.geoConicEquidistantRaw(phi0, phi1) Source

The conic equidistant projection. See also conic.parallels.

Cylindrical Projections

Cylindrical projections project the sphere onto a containing cylinder, and then unroll the cylinder onto the plane. Pseudocylindrical projections are a generalization of cylindrical projections.

d3.geoEquirectangular() Source
d3.geoEquirectangularRaw