Skip to content Skip to sidebar Skip to footer

D3.geo.orthographic Drawing Circles

This chapter looks at D3'southward approach to rendering geographic information.

As an example, the globe below is drawn using D3. A GeoJSON file is loaded and D3 is used to project the geographic data and draw it on a Canvas element:

D3's approach differs to and then called raster methods such every bit Leaflet and Google Maps. These pre-render map features as epitome tiles and these are loaded from a web server and pieced together in the browser to course a map.

Typically D3 requests vector geographic information in the grade of GeoJSON and renders this to SVG or Sheet in the browser.

Raster maps often expect more than similar traditional print maps where a lot of item (e.1000. place names, roads, rivers etc.) tin can be shown without an impact on functioning. Even so, dynamic content such as animation and interaction is more easily implemented using a vector arroyo. (It's also quite common to combine the two approaches.)

D3 mapping concepts

The three concepts that are key to understanding map creation using D3 are:

  • GeoJSON (a JSON-based format for specifying geographic data)
  • projections (functions that convert from latitude/longitude co-ordinates to ten & y co-ordinates)
  • geographic path generators (functions that convert GeoJSON shapes into SVG or Canvass paths)

GeoJSON

GeoJSON is a standard for representing geographic information using the JSON format and the full specification is at geojson.org.

Here's a typical GeoJSON object:

                          {                                          "type"              :                                          "FeatureCollection"              ,                                          "features"              :                                          [                                          {                                          "blazon"              :                                          "Characteristic"              ,                                          "properties"              :                                          {                                          "proper noun"              :                                          "Africa"                                          },                                          "geometry"              :                                          {                                          "type"              :                                          "Polygon"              ,                                          "coordinates"              :                                          [[[              -half-dozen              ,                                          36              ],                                          [              33              ,                                          xxx              ],                                          ...                                          ,                                          [              -6              ,                                          36              ]]]                                          }                                          },                                          {                                          "type"              :                                          "Feature"              ,                                          "properties"              :                                          {                                          "name"              :                                          "Commonwealth of australia"                                          },                                          "geometry"              :                                          {                                          "type"              :                                          "Polygon"              ,                                          "coordinates"              :                                          [[[              143              ,                                          -eleven              ],                                          [              153              ,                                          -28              ],                                          ...                                          ,                                          [              143              ,                                          -xi              ]]]                                          }                                          },                                          {                                          "type"              :                                          "Characteristic"              ,                                          "backdrop"              :                                          {                                          "name"              :                                          "Timbuktu"                                          },                                          "geometry"              :                                          {                                          "type"              :                                          "Bespeak"              ,                                          "coordinates"              :                                          [              -3.0026              ,                                          16.7666              ]                                          }                                          }                                          ]                                          }                      

In the above object in that location'southward a FeatureCollection containing an assortment of 3 features:

  • Africa
  • Australia
  • the metropolis of Timbuktu

Each feature consists of geometry (uncomplicated polygons in the instance of the countries and a bespeak for Timbuktu) and backdrop.

Backdrop can contain any information virtually the feature such as name, id, and other data such equally population, Gdp etc.

D3 takes care of well-nigh of the item when rendering GeoJSON so you only demand a basic understanding of GeoJSON to become started with D3 mapping.

Projections

A projection role takes a longitude and latitude according (in the course of an array [lon, lat]) and transforms it into an x and y co-ordinate:

                          role              projection              (              lonLat              )              {              permit              x              =              ...              // some formula here to summate ten              let              y              =              ...              // some formula here to summate y              render              [              x              ,              y              ];              }              projection              (              [              -              3.0026              ,              16.7666              ]              )              // returns [474.7594743879618, 220.7367625635119]                      

Project mathematics can get quite complex just fortunately D3 provides a large number of projection functions.

For instance y'all can create an equi-rectangular projection function using:

                          let              projection              =              d3              .              geoEquirectangular              ();              projection              (              [              -              3.0026              ,              16.7666              ]              )              // returns [474.7594743879618, 220.7367625635119]                      

We'll look at projections in more than detail later.

Geographic path generators

A geographic path generator is a function that takes a GeoJSON object and converts it into an SVG path string. (In fact, it's just another type of shape generator.)

You can create a generator using the method .geoPath and configure it with a project function:

                          let              project              =              d3              .              geoEquirectangular              ();              let              geoGenerator              =              d3              .              geoPath              ()              .              project              (              projection              );              permit              geoJson              =              {              "              blazon              "              :              "              Feature              "              ,              "              properties              "              :              {              "              proper noun              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              6              ,              36              ],              [              33              ,              30              ],              ...              ,              [              -              vi              ,              36              ]]]              }              }              geoGenerator              (              geoJson              );              // returns "M464.0166237760863,154.09974265651798L491.1506253268278,154.8895088551978 ... L448.03311471280136,183.1346693994119Z"                      

As usual with shape generators the generated path string is used to gear up the d aspect on an SVG path chemical element.

Putting it all together

Given some GeoJSON, a projection function and a geographic path generator you can create a basic map:

                          let              geoJson              =              {              "              blazon              "              :              "              FeatureCollection              "              ,              "              features              "              :              [              {              "              type              "              :              "              Feature              "              ,              "              properties              "              :              {              "              name              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              6              ,              36              ],              [              33              ,              xxx              ],              ...              ,              [              -              6              ,              36              ]]]              }              },              ...              ]              }              let              project              =              d3              .              geoEquirectangular              ();              let              geoGenerator              =              d3              .              geoPath              ()              .              projection              (              projection              );              // Join the FeatureCollection's features array to path elements              let              u              =              d3              .              select              (              '              #content thou.map              '              )              .              selectAll              (              '              path              '              )              .              data              (              geojson              .              features              )              .              bring together              (              '              path              '              )              .              attr              (              '              d              '              ,              geoGenerator              );                      

geoJson.features is an array of features. This assortment is joined to path elements. The d aspect is gear up using the function geoGenerator. This receives a feature equally its first parameter and outputs a path string.

The last line may look similar magic only is the equivalent of:

                          .attr('d', part(d) {     render geoGenerator(d);   });                      

In this case the parameter d is a GeoJSON characteristic.

To keep things elementary the GeoJSON in the in a higher place example uses simply a few co-ordinates to define the country boundaries.

The above example shows the essence of creating maps using D3 and I recommend spending time to understand each concept (GeoJSON, projections and geo generators) and how they fit together.

Now that we've covered the basics nosotros'll look at each concept in more detail.

GeoJSON

GeoJSON is a JSON-based construction for specifying geographic data. By and large it'southward converted from shapefile data (a geospatial vector data format widely used in the GIS field) using tools such as mapshaper, ogr2ogr, shp2json or QGIS.

A pop source of world map shapefiles is Natural World and if starting out I recommend trying out mapshaper for importing shapefiles and exporting as GeoJSON. It can also filter past backdrop (due east.g. if you wanted to filter countries by continent). For a more in depth look at conversion look at Mike Bostock's Let'southward Make a Map tutorial.

Yous tin create maps without understanding the GeoJSON specification in minute detail because tools such every bit mapshaper and D3 do such a good job of abstracting away the detail. Still, if y'all did want to empathise GeoJSON in greater depth I recommend checking out the official specification.

So far we've embedded a GeoJSON object in our example files. In practise the GeoJSON would be in a separate file and loaded using an ajax asking. We encompass requests in more detail in the requests chapter merely for the remainder of this chapter we'll load a GeoJSON file using:

                          d3              .              json              (              '              ne_110m_land.json              '              ,              function              (              err              ,              json              )              {              createMap              (              json              );              })                      

Information technology'south worth mentioning TopoJSON which is another JSON based standard for describing geographic information and tends to consequence in significantly smaller file sizes. It requires a fleck more work to use, and we don't cover it in this chapter. However for farther information cheque out the documentation.

Projections

At that place are numerous (if non infinite) ways of converting (or 'projecting') a point on a sphere (e.m. the earth) to a point on a flat surface (e.grand. a screen) and people accept written countless articles (such equally this one) on the pros and cons of unlike projections.

In brusque there is no perfect projection as every projection will distort shape, expanse, distance and/or direction. Choosing a project is a case of choosing which property you lot don't want to be distorted and accepting that there'll be distortion in the other properties (or cull a projection that strives for a balanced approach). For example, if it's important that the size of countries are represented accurately and so choose a project that strives to preserve area (probably to the toll of shape, altitude and management).

D3 has a number of core projections that should comprehend about use cases:

  • geoAzimuthalEqualArea
  • geoAzimuthalEquidistant
  • geoGnomonic
  • geoOrthographic
  • geoStereographic
  • geoAlbers
  • geoConicConformal
  • geoConicEqualArea
  • geoConicEquidistant
  • geoEquirectangular
  • geoMercator
  • geoTransverseMercator

Some projections preserve expanse (east.1000. geoAzimuthalEqualArea & geoConicEqualArea), others distance (e.g. geoAzimuthalEquidistant & geoConicEquidistant) and others relative angles (eastward.chiliad. geoEquirectangular & geoMercator). For a more than in depth give-and-take of the pros and cons of each projection attempt resource such as Carlos A. Furuti's Map Projection Pages.

The filigree below shows each core projection on a earth map together with a longitude/latitude filigree and equal radius circles.

Projection functions

A project function takes input [longitude, breadth] and outputs a pixel co-ordinate [x, y].

Be careful to note the order of longitude and latitude in the in a higher place array!

Yous're free to write your ain projection functions merely much easier is to ask D3 to brand one for you. To do this choose a projection method (due east.chiliad. d3.geoAzimuthalEqualArea), telephone call it and it'll render a projection function:

                          let              projection              =              d3              .              geoAzimuthalEqualArea              ();              projection              (              [              -              3.0026              ,              xvi.7666              ]              );              // returns [473.67353385539417, 213.6120079887163]                      

The cadre projections have configuration functions for setting the following parameters:

scale Scale cistron of the projection
heart Projection center [longitude, latitude]
interpret Pixel [10,y] location of the projection centre
rotate Rotation of the projection [lambda, phi, gamma] (or [yaw, pitch, roll])

The precise significant of each parameter is dependent on the mathematics behind each projection simply broadly speaking:

  • scale specifies the calibration cistron of the projection. The higher the number the larger the map.
  • center specifies the center of projection (with a [lon, lat] array)
  • interpret specifies where the centre of projection is located on the screen (with a [x, y] array)
  • rotate specifies the rotation of the project (with a [λ, φ, γ] array) where the parameters correspond to yaw, pitch and gyre, respectively:

For example you can create and configure a projection function such that Timbuktu is centred in a 960x500 map using:

                          let              projection              =              d3              .              geoAzimuthalEqualArea              ()              .              scale              (              300              )              .              center              ([              -              3.0026              ,              xvi.7666              ])              .              translate              ([              480              ,              250              ]);                      

To get a experience for how each parameter behaves utilise the projection explorer below. The (equal radius) circles and grid allow you to appraise the projection's distortion of area and angle.

.invert()

You can convert a pixel co-ordinate [x, y] to a longitude/breadth array using the projection's .capsize() method:

                          permit              project              =              d3              .              geoAzimuthalEqualArea              ();              projection              (              [              -              3.0026              ,              xvi.7666              ]              )              // returns [473.67353385539417, 213.6120079887163]              projection              .              capsize              (              [              473.67353385539417              ,              213.6120079887163              ]              )              // returns [-3.0026, xvi.766]                      

Fitting

Given a GeoJSON object, a projection's .fitExtent() method sets the projection's calibration and translate such that the geometry fits within a given bounding box:

                          project              .              fitExtent              ([[              0              ,              0              ],              [              900              ,              500              ]],              geojson              );                      

The commencement argument of .fitExtent is an array containing ii coordinates: the top left point ([x, y]) of the bounding box and the size ([width, height]) of the bounding box. The second argument is a GeoJSON object.

In the example beneath the sheet element has a light grey background and the bounding box into which we're fitting the geoJSON is shown equally a dotted outline. The following code is used to fit the geometry within the bounding box:

                          projection              .              fitExtent              ([[              20              ,              twenty              ],              [              620              ,              420              ]],              geojson              );                      

If your bounding box'due south top left corner is at [0, 0] you can omit the top left coordinate and simply supply the width and top:

                          projection              .              fitSize              ([              900              ,              500              ],              geojson              );                      

Geographic path generators

A geographic path generator is a office that transforms GeoJSON into an SVG path string (or into canvas element calls):

                          geoGenerator              (              geoJson              );              // e.chiliad. returns a SVG path string "M464.01,154.09L491.fifteen,154.88 ... L448.03,183.13Z"                      

You create the generator using d3.geoPath() and must configure information technology's projection type:

                          let              projection              =              d3              .              geoEquirectangular              ();              permit              geoGenerator              =              d3              .              geoPath              ()              .              projection              (              projection              );                      

You tin now use the generator to assist create an SVG or canvass map. The SVG option is a bit easier to implement, especially when it comes to user interaction (because consequence handlers and hover states tin can be added).

The canvas approach requires a flake more piece of work but is typically faster to render (and more retentivity efficient).

Rendering SVG

To return an SVG map yous:

  • join a GeoJSON features array to SVG path elements
  • update each path element's d aspect using the geographic path generator

For example:

                          let              geoJson              =              {              "              type              "              :              "              FeatureCollection              "              ,              "              features              "              :              [              {              "              type              "              :              "              Feature              "              ,              "              properties              "              :              {              "              name              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              half-dozen              ,              36              ],              [              33              ,              30              ],              ...              ,              [              -              6              ,              36              ]]]              }              },              {              "              type              "              :              "              Feature              "              ,              "              properties              "              :              {              "              name              "              :              "              Australia              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              143              ,              -              11              ],              [              153              ,              -              28              ],              ...              ,              [              143              ,              -              eleven              ]]]              }              },              {              "              blazon              "              :              "              Feature              "              ,              "              properties              "              :              {              "              name              "              :              "              Timbuktu              "              },              "              geometry              "              :              {              "              blazon              "              :              "              Point              "              ,              "              coordinates              "              :              [              -              three.0026              ,              xvi.7666              ]              }              }              ]              }              let              projection              =              d3              .              geoEquirectangular              ();              let              geoGenerator              =              d3              .              geoPath              ()              .              projection              (              projection              );              // Join the FeatureCollection'southward features array to path elements              let              u              =              d3              .              select              (              '              #content g.map              '              )              .              selectAll              (              '              path              '              )              .              data              (              geojson              .              features              )              .              join              (              '              path              '              )              .              attr              (              '              d              '              ,              geoGenerator              );                      

geoJson.features is an array of features. This array is joined to path elements. The d aspect is set using the part geoGenerator. This receives a feature as its first parameter and outputs a path string.

Rendering to canvas

To render to a canvas chemical element you laissez passer the canvas DOM element into the generator'due south context method:

                          permit              context              =              d3              .              select              (              '              #content sheet              '              )              .              node              ()              .              getContext              (              '              2d              '              );              let              geoGenerator              =              d3              .              geoPath              ()              .              project              (              project              )              .              context              (              context              );                      

The .node method returns the first DOM chemical element of a selection.

You and then begin a canvass path (using context.beginPath()) and call geoGenerator which will produce the necessary canvas calls:

                          context              .              beginPath              ();              geoGenerator              ({              type              :              '              FeatureCollection              '              ,              features              :              geojson              .              features              })              context              .              stroke              ();                      

Lines and arcs

The geographic path generator is clever plenty to distinguish betwixt polygonal (typically for geographic areas) and point (typically for lon/lat locations) features. As can be seen in the above examples information technology renders polygons as line segments and points as arcs.

Y'all can ready the radius of the circles using .pointRadius():

                          let              geoGenerator              =              d3              .              geoPath              ()              .              pointRadius              (              5              )              .              projection              (              projection              );                      

Path geometry

The geographic path generator can as well be used to compute the area (in pixels), centroid, bounding box and path length (in pixels) of a projected GeoJSON feature:

                          let              feature              =              geojson              .              features              [              0              ];              // Compute the feature's expanse (in pixels)              geoGenerator              .              surface area              (              characteristic              );              // returns 30324.86518469876              // Compute the feature's centroid (in pixel co-ordinates)              geoGenerator              .              centroid              (              feature              );              // returns [266.9510120424504, 127.35819206325564]              // Compute the feature's bounds (in pixel co-ordinates)              geoGenerator              .              bounds              (              feature              );              // returns [[140.6588054321928, 24.336293856408275], [378.02358370342165, 272.17304763960306]]              // Compute the path length (in pixels)              geoGenerator              .              measure              (              characteristic              );              // returns 775.7895349902461                      

This example shows the area and length of a hovered path. It also draws the path's centroid and bounding box:

Shapes

If you need to add lines and/or circles to a map you lot can add together features to the GeoJSON.

Lines can exist added equally a LineString feature and will be projected into great-arcs (i.e. the shortest distance beyond the surface of the globe).

Here's an example where a line is added betwixt London and New York:

                          geoGenerator              ({              blazon              :              '              Characteristic              '              ,              geometry              :              {              type              :              '              LineString              '              ,              coordinates              :              [[              0.1278              ,              51.5074              ],              [              -              74.0059              ,              twoscore.7128              ]]              }              });                      

Circle features tin can be generated using d3.geoCircle(). This creates a circle generator which returns a GeoJSON object representing a circumvolve.

Typically the eye ([lon, lat]) and the radius (in degrees) are set:

                          let              circleGenerator              =              d3              .              geoCircle              ()              .              center              ([              0.1278              ,              51.5074              ])              .              radius              (              5              );              permit              circle              =              circleGenerator              ();              // returns a GeoJSON object representing a circumvolve              geoGenerator              (              circle              );              // returns a path string representing the projected circle                      

A GeoJSON filigree of longitude and latitude lines (known every bit a graticule) can be generated using d3.graticule(). This creates a graticule generator which returns a GeoJSON object representing the graticules:

                          let              graticuleGenerator              =              d3              .              geoGraticule              ();              let              graticules              =              graticuleGenerator              ();              // returns a GeoJSON object representing the graticule              geoGenerator              (              graticules              );              // returns a path string representing the projected graticule                      

(See the official documentation for detailed information on graticule configuration.)

Hither'due south an example where a line, a circumvolve and graticules are added to a map:

Spherical geometry

There's a scattering of D3 methods that may come up in useful from fourth dimension to time. The first of these .geoArea(), .geoBounds(), .geoCentroid(), .geoDistance() and geoLength() are like to the path geometry methods described above but operate in spherical space.

Interpolation

The d3.geoInterpolate() method creates a function that accepts input between 0 and ane and interpolates between two [lon, lat] locations:

                          let              londonLonLat              =              [              0.1278              ,              51.5074              ];              let              newYorkLonLat              =              [              -              74.0059              ,              40.7128              ];              allow              geoInterpolator              =              d3              .              geoInterpolate              (              londonLonLat              ,              newYorkLonLat              );              geoInterpolator              (              0              );              // returns [0.1278, 51.5074]              geoInterpolator              (              0.v              );              // returns [-41.182023242967695, 52.41428456719971] (halfway between the two locations)                      

geoContains

If yous're using a canvass element to render your geometry you don't accept the luxury of being able to add event handlers onto SVG path elements. Instead y'all can check whether mouse or touch events occur inside the boundary of a feature. You can do this using d3.geoContains which accepts a GeoJSON feature and a [lon, lat] assortment and returns a boolean:

                          d3              .              geoContains              (              ukFeature              ,              [              0.1278              ,              51.5074              ]);              // returns true                      

paynefooked.blogspot.com

Source: https://www.d3indepth.com/geographic/

Post a Comment for "D3.geo.orthographic Drawing Circles"