Create a rotating globe using React and D3

Create a rotating globe using React and D3

by Peter Cook / 01 Aug 2018    

Prerequisites: Moderate experience with React.

This tutorial shows how to create a rotating globe using React and D3. We’ll use React to create a component which’ll contain the rotating globe and D3’s powerful geographic functionality to deal with transforming our geographic data into SVG.

See the Pen React / D3 rotating globe by Frontend Charts (@frontendcharts) on CodePen.

1. Create project

There’s several different ways to create a React based project so either use your usual approach and adapt this tutorial to your particular needs, or follow along on Codepen.

The main thing to make sure is that the two React libraries react and react-dom together with D3 and topojson are included. For example if using Codepen, include these files:

https://npmcdn.com/react@15.2.1/dist/react.min.js
https://npmcdn.com/react-dom@15.2.1/dist/react-dom.min.js

https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js
https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.0/topojson.min.js

2. Create Globe component

Our project will consist of a single React component Globe. This will contain an SVG element and a path element (for the countries):

class Globe extends React.Component {
  render() {
    return <svg width={this.props.size} height={this.props.size} >
      <path d="" />
    </svg>
  }
}

The d attribute in the path element will eventually be a string consisting of a series of draw commands (such as move to, draw a line etc.). In this tutorial we don’t need to know the precise details of these commands as D3 will take care of this for us.

Let’s also render this component:

ReactDOM.render(
  <Globe size={400} />,
  document.getElementById('app')
)

(Make sure you have a div element with id app in your HTML!)

3. Load and process geographic data

Two of the most common ways of representing geographic data on the web are GeoJSON and TopoJSON. GeoJSON is a JSON based format for describing geographic features while TopoJSON is also JSON based but it cleverly reduces size by removing redundancy/repetition in the geographic data.

We’ve a world TopoJSON file which we’ll use in this project. Let’s:

  • load it using D3
  • convert it to GeoJSON (we need to convert it to GeoJSON as that’s the format D3 prefers) &
  • pass it into our Globe component:
d3.json('https://codepen.io/frontendcharts/pen/JBprpp.js', (err, json) => {
  let geoJson = topojson.feature(json, json.objects.ne_110m_admin_0_countries)

  ReactDOM.render(
    <Globe geoJson={geoJson} size={400} />,
    document.getElementById('app')
  )
})

4. Render the globe

The GeoJSON file contains an array of features, each of which is described by arrays of longitude and latitude co-ordinates. Each co-ordinate represents a location on the surface of the earth. Converting these to x and y positions on the screen is known as projection and is something D3 can do for us (saving us some mathematical headaches!).

The first step in setting up a projection in D3 is to choose the type of projection we’d like. We’ll use an orthographic projection as this’ll make the earth look like a globe. Let’s create the D3 projection function at the start of our render() function:

render() {
  let projection = d3.geoOrthographic()
    .fitSize([this.props.size, this.props.size], this.props.geoJson)

  ...
}

Note that we use fitSize to automatically scale the projection to size. For more in depth information about D3’s geographic functionality see D3 in Depth.

We’ll now use D3’s geoPath function to create a function geoGenerator that converts our GeoJSON into a path string:

render() {
  ...
  let geoGenerator = d3.geoPath()
    .projection(projection)

  let pathString = geoGenerator(this.props.geoJson)
  ...
}

We can now pass pathString into our SVG path:

render() {
  ...
  return <svg width={this.props.size} height={this.props.size} >
    <path d={pathString} />
  </svg>
}

See the finished code on Codepen.

When we run this code we should now see a globe:

globe

5. Spin the globe

Finally let’s start the globe rotating. D3’s projection has a rotate option which specifies the rotation (in degrees) of the projection.

First we’ll add a rotation property to Globe’s state:

class Globe extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      rotation: 0
    }
  }

  ...
}

and then we’ll set the projection’s rotation:

render() {
  let projection = d3.geoOrthographic()
    .fitSize([this.props.size, this.props.size], this.props.geoJson)
    .rotate([this.state.rotation])

  ...
}

Finally we set up a requestAnimationFrame callback function that’ll increment rotation:

render() {
  ...
  window.requestAnimationFrame(() => {
    this.setState({
      rotation: this.state.rotation + 0.2
    })
  })
  ...
}

Now when we run the app the globe will slowly spin round!

6. Summary

This tutorial has shown how we can use React and D3 to create a rotating globe. React allows us to build a reusable component that handles the creation and updating of our SVG while D3 provides the geographic cleverness.

Have a play around with the code on Codepen - see if you can change things like the size of the globe abd the speed of rotation. If you’re feeling more adventurous you could explore some of the other projections that D3 provides. There’s quite a few to choose from!

Code

See the Pen React / D3 rotating globe by Frontend Charts (@frontendcharts) on CodePen.