D3 Draw Circle on Map

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 information and depict it on a Canvass chemical element:

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

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

Raster maps oft expect more like traditional print maps where a lot of detail (due east.thousand. place names, roads, rivers etc.) can exist shown without an impact on operation. However, dynamic content such as animation and interaction is more easily implemented using a vector approach. (Information technology'southward too quite mutual to combine the two approaches.)

D3 mapping concepts

The iii concepts that are central to understanding map cosmos using D3 are:

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

GeoJSON

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

Here's a typical GeoJSON object:

                          {                                          "type"              :                                          "FeatureCollection"              ,                                          "features"              :                                          [                                          {                                          "type"              :                                          "Feature"              ,                                          "properties"              :                                          {                                          "name"              :                                          "Africa"                                          },                                          "geometry"              :                                          {                                          "type"              :                                          "Polygon"              ,                                          "coordinates"              :                                          [[[              -6              ,                                          36              ],                                          [              33              ,                                          30              ],                                          ...                                          ,                                          [              -vi              ,                                          36              ]]]                                          }                                          },                                          {                                          "type"              :                                          "Feature"              ,                                          "backdrop"              :                                          {                                          "name"              :                                          "Australia"                                          },                                          "geometry"              :                                          {                                          "blazon"              :                                          "Polygon"              ,                                          "coordinates"              :                                          [[[              143              ,                                          -xi              ],                                          [              153              ,                                          -28              ],                                          ...                                          ,                                          [              143              ,                                          -xi              ]]]                                          }                                          },                                          {                                          "type"              :                                          "Feature"              ,                                          "properties"              :                                          {                                          "proper noun"              :                                          "Timbuktu"                                          },                                          "geometry"              :                                          {                                          "type"              :                                          "Indicate"              ,                                          "coordinates"              :                                          [              -iii.0026              ,                                          xvi.7666              ]                                          }                                          }                                          ]                                          }                      

In the to a higher place object there's a FeatureCollection containing an array of 3 features:

  • Africa
  • Australia
  • the city of Timbuktu

Each feature consists of geometry (simple polygons in the example of the countries and a point for Timbuktu) and properties.

Properties can contain any information about the characteristic such every bit name, id, and other data such as population, GDP etc.

D3 takes intendance of almost of the particular when rendering GeoJSON and then yous merely demand a basic understanding of GeoJSON to get started with D3 mapping.

Projections

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

                          part              projection              (              lonLat              )              {              allow              ten              =              ...              // some formula here to summate x              let              y              =              ...              // some formula hither to calculate y              return              [              10              ,              y              ];              }              projection              (              [              -              3.0026              ,              16.7666              ]              )              // returns [474.7594743879618, 220.7367625635119]                      

Project mathematics tin go quite complex only fortunately D3 provides a large number of project functions.

For example you can create an equi-rectangular projection function using:

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

Nosotros'll look at projections in more item later.

Geographic path generators

A geographic path generator is a function that takes a GeoJSON object and converts it into an SVG path cord. (In fact, it'due south just some other type of shape generator.)

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

                          let              projection              =              d3              .              geoEquirectangular              ();              let              geoGenerator              =              d3              .              geoPath              ()              .              project              (              projection              );              allow              geoJson              =              {              "              blazon              "              :              "              Feature              "              ,              "              properties              "              :              {              "              name              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              6              ,              36              ],              [              33              ,              30              ],              ...              ,              [              -              6              ,              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 fix the d attribute on an SVG path element.

Putting it all together

Given some GeoJSON, a project role and a geographic path generator you can create a basic map:

                          allow              geoJson              =              {              "              type              "              :              "              FeatureCollection              "              ,              "              features              "              :              [              {              "              type              "              :              "              Feature              "              ,              "              properties              "              :              {              "              proper name              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              vi              ,              36              ],              [              33              ,              30              ],              ...              ,              [              -              six              ,              36              ]]]              }              },              ...              ]              }              allow              projection              =              d3              .              geoEquirectangular              ();              let              geoGenerator              =              d3              .              geoPath              ()              .              projection              (              projection              );              // Join the FeatureCollection's features array to path elements              let              u              =              d3              .              select              (              '              #content k.map              '              )              .              selectAll              (              '              path              '              )              .              data              (              geojson              .              features              )              .              join              (              '              path              '              )              .              attr              (              '              d              '              ,              geoGenerator              );                      

geoJson.features is an assortment of features. This array is joined to path elements. The d attribute is prepare using the function geoGenerator. This receives a characteristic every bit its first parameter and outputs a path cord.

The last line may look like magic just is the equivalent of:

                          .attr('d', function(d) {     return geoGenerator(d);   });                      

In this case the parameter d is a GeoJSON feature.

To go along things simple the GeoJSON in the above example uses just a few co-ordinates to define the country boundaries.

The above instance 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 nuts we'll look at each concept in more than detail.

GeoJSON

GeoJSON is a JSON-based structure for specifying geographic information. More often than not information technology's converted from shapefile data (a geospatial vector information 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 Earth and if starting out I recommend trying out mapshaper for importing shapefiles and exporting as GeoJSON. It can also filter by backdrop (eastward.m. if yous wanted to filter countries by continent). For a more than in depth await at conversion look at Mike Bostock's Let's Make a Map tutorial.

You tin create maps without understanding the GeoJSON specification in minute item considering tools such as mapshaper and D3 practice such a practiced job of abstracting away the particular. However, if you lot did desire to understand GeoJSON in greater depth I recommend checking out the official specification.

So far nosotros've embedded a GeoJSON object in our instance files. In practice the GeoJSON would be in a carve up file and loaded using an ajax request. We embrace requests in more detail in the requests affiliate only for the remainder of this chapter nosotros'll load a GeoJSON file using:

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

Information technology's worth mentioning TopoJSON which is some other JSON based standard for describing geographic information and tends to result in significantly smaller file sizes. Information technology requires a scrap more work to use, and we don't encompass it in this affiliate. Notwithstanding for further data check out the documentation.

Projections

There are numerous (if not infinite) means of converting (or 'projecting') a bespeak on a sphere (east.g. the world) to a betoken on a apartment surface (eastward.g. a screen) and people take written endless articles (such as this one) on the pros and cons of different projections.

In short in that location is no perfect project as every projection will distort shape, area, distance and/or direction. Choosing a projection is a example of choosing which property you don't want to exist distorted and accepting that in that location'll be distortion in the other backdrop (or choose a projection that strives for a balanced approach). For case, if it's of import that the size of countries are represented accurately then choose a projection that strives to preserve area (probably to the cost of shape, distance and direction).

D3 has a number of core projections that should cover most employ cases:

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

Some projections preserve area (e.g. geoAzimuthalEqualArea & geoConicEqualArea), others altitude (e.g. geoAzimuthalEquidistant & geoConicEquidistant) and others relative angles (eastward.g. geoEquirectangular & geoMercator). For a more than in depth discussion of the pros and cons of each projection try resources such every bit Carlos A. Furuti's Map Projection Pages.

The filigree below shows each core projection on a world map together with a longitude/breadth grid and equal radius circles.

Projection functions

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

Be conscientious to note the order of longitude and latitude in the above array!

You're free to write your ain projection functions but much easier is to ask D3 to brand ane for you. To practise this cull a project method (due east.g. d3.geoAzimuthalEqualArea), call it and it'll return a projection part:

                          permit              projection              =              d3              .              geoAzimuthalEqualArea              ();              projection              (              [              -              three.0026              ,              16.7666              ]              );              // returns [473.67353385539417, 213.6120079887163]                      

The core projections have configuration functions for setting the post-obit parameters:

scale Scale factor of the project
center Projection eye [longitude, breadth]
interpret Pixel [10,y] location of the projection eye
rotate Rotation of the project [lambda, phi, gamma] (or [yaw, pitch, roll])

The precise meaning of each parameter is dependent on the mathematics behind each project but broadly speaking:

  • scale specifies the scale factor of the projection. The college the number the larger the map.
  • center specifies the center of project (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 projection (with a [λ, φ, γ] array) where the parameters stand for to yaw, pitch and ringlet, respectively:

For instance y'all can create and configure a projection function such that Timbuktu is centred in a 960x500 map using:

                          allow              projection              =              d3              .              geoAzimuthalEqualArea              ()              .              scale              (              300              )              .              middle              ([              -              iii.0026              ,              sixteen.7666              ])              .              translate              ([              480              ,              250              ]);                      

To get a feel for how each parameter behaves use the project explorer beneath. The (equal radius) circles and filigree allow you lot to assess the projection'due south distortion of area and angle.

.invert()

You can convert a pixel co-ordinate [x, y] to a longitude/latitude array using the projection'due south .invert() method:

                          let              projection              =              d3              .              geoAzimuthalEqualArea              ();              project              (              [              -              3.0026              ,              16.7666              ]              )              // returns [473.67353385539417, 213.6120079887163]              projection              .              invert              (              [              473.67353385539417              ,              213.6120079887163              ]              )              // returns [-3.0026, 16.766]                      

Fitting

Given a GeoJSON object, a projection'southward .fitExtent() method sets the project'southward scale and interpret such that the geometry fits within a given bounding box:

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

The first statement of .fitExtent is an assortment containing 2 coordinates: the tiptop left signal ([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 below the canvas chemical element has a lite grey background and the bounding box into which we're fitting the geoJSON is shown as a dotted outline. The following lawmaking is used to fit the geometry within the bounding box:

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

If your bounding box's top left corner is at [0, 0] you can omit the top left coordinate and but supply the width and height:

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

Geographic path generators

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

                          geoGenerator              (              geoJson              );              // eastward.thou. returns a SVG path string "M464.01,154.09L491.15,154.88 ... L448.03,183.13Z"                      

Yous create the generator using d3.geoPath() and must configure it'south projection type:

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

You lot can at present utilize the generator to help create an SVG or canvass map. The SVG option is a bit easier to implement, particularly when it comes to user interaction (because upshot handlers and hover states tin can be added).

The canvass approach requires a chip more than work but is typically faster to render (and more than memory efficient).

Rendering SVG

To return an SVG map you:

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

For instance:

                          let              geoJson              =              {              "              type              "              :              "              FeatureCollection              "              ,              "              features              "              :              [              {              "              type              "              :              "              Feature              "              ,              "              backdrop              "              :              {              "              name              "              :              "              Africa              "              },              "              geometry              "              :              {              "              type              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              -              vi              ,              36              ],              [              33              ,              30              ],              ...              ,              [              -              half-dozen              ,              36              ]]]              }              },              {              "              type              "              :              "              Feature              "              ,              "              properties              "              :              {              "              proper name              "              :              "              Commonwealth of australia              "              },              "              geometry              "              :              {              "              blazon              "              :              "              Polygon              "              ,              "              coordinates              "              :              [[[              143              ,              -              11              ],              [              153              ,              -              28              ],              ...              ,              [              143              ,              -              xi              ]]]              }              },              {              "              blazon              "              :              "              Feature              "              ,              "              backdrop              "              :              {              "              name              "              :              "              Timbuktu              "              },              "              geometry              "              :              {              "              type              "              :              "              Signal              "              ,              "              coordinates              "              :              [              -              3.0026              ,              sixteen.7666              ]              }              }              ]              }              let              project              =              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              )              .              bring together              (              '              path              '              )              .              attr              (              '              d              '              ,              geoGenerator              );                      

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

Rendering to canvass

To render to a canvas element y'all pass the canvas DOM chemical element into the generator's context method:

                          permit              context              =              d3              .              select              (              '              #content canvas              '              )              .              node              ()              .              getContext              (              '              2d              '              );              permit              geoGenerator              =              d3              .              geoPath              ()              .              projection              (              projection              )              .              context              (              context              );                      

The .node method returns the showtime DOM element of a pick.

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

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

Lines and arcs

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

Yous can set the radius of the circles using .pointRadius():

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

Path geometry

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

                          permit              feature              =              geojson              .              features              [              0              ];              // Compute the feature'south area (in pixels)              geoGenerator              .              area              (              feature              );              // returns 30324.86518469876              // Compute the feature's centroid (in pixel co-ordinates)              geoGenerator              .              centroid              (              characteristic              );              // returns [266.9510120424504, 127.35819206325564]              // Compute the feature'due south 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              (              feature              );              // returns 775.7895349902461                      

This example shows the expanse and length of a hovered path. It as well draws the path'south centroid and bounding box:

Shapes

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

Lines tin can be added as a LineString feature and will be projected into neat-arcs (i.e. the shortest distance across the surface of the globe).

Hither's an example where a line is added between London and New York:

                          geoGenerator              ({              type              :              '              Feature              '              ,              geometry              :              {              type              :              '              LineString              '              ,              coordinates              :              [[              0.1278              ,              51.5074              ],              [              -              74.0059              ,              40.7128              ]]              }              });                      

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

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

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

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

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

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

Here'due south an case where a line, a circle and graticules are added to a map:

Spherical geometry

There's a scattering of D3 methods that may come in useful from time to fourth dimension. The get-go of these .geoArea(), .geoBounds(), .geoCentroid(), .geoDistance() and geoLength() are similar to the path geometry methods described higher up only operate in spherical space.

Interpolation

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

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

geoContains

If you lot're using a canvas chemical element to render your geometry yous don't have the luxury of being able to add consequence handlers onto SVG path elements. Instead you tin check whether mouse or touch events occur inside the boundary of a characteristic. You tin 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                      

robertsrupot1992.blogspot.com

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

0 Response to "D3 Draw Circle on Map"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel