A world map chart is a great way to show items globally. Integrating D3 with React and TS can create readable code that uses the best of all the tools. In this chapter, I will show you how to create a rotating map and assign dots based on coordinates.
Specifically, in this chapter, I will show you how to work with a world map using React, D3, and TS as type checkers. I will break down the process into steps. In each step, I will be adding more functionality until we have the rotating world map with dots that represent coordinates.
World map atlas: WorldMapAtlas.tsx
Round world map: RoundWorldMap.tsx
Rotating round world map: RotatingRoundWorldMap.tsx
Rotating round world map with coordinates: RotatingRoundWorldMapWithCoordinates.tsx
Refactoring: WorldMap.tsx
The project can be downloaded from here:
https://github.com/Apress/integrating-d3.js-with-react/tree/main/ch06/world-map-chart
Setup
Install Additional Needed Libraries and Types
d3-geo: We will use d3-geo for geographic projections (drawing the map). See https://github.com/d3/d3-geo.
topojson-client: This is a client to manipulate TopoJSON. TopoJSON is the library that provides the map of the world, which I can use to draw the map. See https://github.com/topojson/topojson-client.
geojson: This is a format for encoding geographic data. See https://geojson.org/. TopoJSON files are type “Topology” and follow the TopoJSON specification. GeoJSON will then be used to format the encoding of the geographic data structures. See https://geojson.org/.
react-uuid: Create a random UUID that we will be using for the list key needed when we map the React component. See https://github.com/uuidjs/uuid.
Lastly, download the data of the world atlas. The data is provided from TopoJSON that has prebuilt countries data (https://github.com/topojson/world-atlas). Here is the actual JSON I will be using:
https://d3js.org/world-110m.v1.json
Place the file in the public folder for easy access: /public/data/world-110m.json.
World Map Atlas
The first map I will be creating is just a flat world atlas type map that will show the world.
WorldMapAtlas.tsx
Let’s review.
WorldMapAtlas is set as function component. This is a matter of preference, and I could have used a class component.
In terms of the type, I had to figure the type by drilling into the actual geojson library.
Notice that I am using key={`path-${uuid()}`}.
Keys help React identify which items have changed, are added, or are removed. Keys should be given to the elements inside the array to give the elements a stable identity.
Keys and refs are added as an attribute to a React.createElement() call. They help React optimize the rendering by recycling all the existing elements in the DOM.
App.tsx
Next, let’s add our WorldMapAtlas component to App.tsx as a child.
App.scss
As I mentioned, for the projections, I used geoEqualEarth; however, I can change the projection easily to other projections. For instance, if I want to change to geoStereographic, my map will change. See Figure 6-2 .
Round World Map
Now we know how to draw a world map atlas. Next we will see how to create a round world map.
To change the map to be round, all I have to do is use the geoOrthographic projection.
To make the round map look better, I am also going to draw a round light gray background using a SVG circle element.
RoundWorldMap.tsx
Rotating Round World Map Chart
Now that we have a round world map atlas, wouldn’t it be neat to add animation and interactions? We can rotate the atlas and add a button to start the animation.
AnimationFrame.tsx
To add animations, we can call JavaScript window requestAnimationFrame API (https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
The requestAnimationFrame method tells the browser that I want to perform an animation, and the browser will call my callback function so that I can update my animation before the next redraw.
Let’s review the code.
Next, I will keep track of the frame using performance.now() (https://developer.mozilla.org/en-US/docs/Web/API/Performance/now). That feature brings a timestamp in the one-millisecond resolution that I can use to figure out the time delta in case I need it.
Time deltas are the differences in times.
I can then use the useEffect hook to tie the animate method.
RotatingRoundWorldMap.tsx
Now that we have the AnimationFrame hook ready to be used for our animation, I can add my rotating animation. Additionally, I will be adding a user gesture in the form of an icon button from Material-UI to start the animation.
One more change we need to make is to place the loading of the map data inside of a statement that checks if the data was loaded.
The reason is that since using the animation hook, the useEffect will get called all the time.
Rotating Round World Map Chart with Coordinates
In this section, I will show you how to add coordinate dots to our map.
RotatingRoundWorldMapWithCoordinates.tsx
Copy the RotatingRoundWorldMap.tsx file from the previous example and name it RotatingRoundWorldMapWIthCoordinates.tsx .
Yes, I know it’s a long name, but using Shakespeare’s method names, it’s easy to tell what this component is doing.
Notice that just like the previous step, every time that I am using the map in React I am adding a unique key to each item.
Refactoring
Props: Extract the props attributes so the parent component can adjust the attributes and data.
Coordinates: Set the coordinate data as a second data feed in the form of CSV.
Loader: Load the data from the parent component and pass the data to the map chart using asynchronous tasks.
Types: Add the TypeScript types to make the code more readable, avoid errors, and help with testing.
coordinates.csv
Place the file here for easy access: world-map-chart/public/data/coordinates.csv.
types.tsx
WorldMap.tsx
For our refactoring effort, copy the RotatingRoundWorldMapWIthCoordinates.tsx file from the previous example and name it WorldMap.tsx .
Most of the code stays the same.
For the props interface, I will be adding the data feeds and the alignment attributes, so I need to add the props interface and change the function signature.
As you can see, our code in WorldMap.tsx is now cleaner and more readable compared to the RotatingRoundWorldMapWIthCoordinates.tsx file from the previous example.
App.tsx
For the parent component, I will be extracting the data, placing it inside the effect hook, and using the D3 to load both the JSON and the CSV.
Since I want to load both data sets before I draw the map, a good approach is to use the asynchronous calls to both data sets before passing the data to the chart component.
We can use D3’s queue(). See https://github.com/d3/d3-queue. D3’s queue() to do asynchronous tasks. Add these D3 modules and TS types.
Let’s review the code.
The useEffect hook will be doing the heavy lifting. The if statement ensures I am not loading my data multiple times.
Nothing really changes, from the user view perspective, once you check port 3000: http://localhost:3000. However, my code is more organized and easier to read as well as ready to implement state management such as Recoil or Redux since the data is extracted from the actual component and can be shared with multiple components.
Summary
In this chapter, we created a world map with the help of D3, TopoJSON, and React. The ability to draw maps as a background and add dots, animations, and interaction can help create a compelling chart that can be used for many things to tell your story.
World map atlas: WorldMapAtlas.tsx
Round world map: RoundWorldMap.tsx
Rotating round world map : RotatingRoundWorldMap.tsx
Rotating round world map with coordinates: RotatingRoundWorldMapWithCoordinates.tsx
Refactoring: WorldMap.tsx
As you can see from this chapter, integrating the world map atlas using D3 with the help of topojson and geojson is straightforward.
Having React involved makes adding animations and interaction intuitive. TS helps ensure we understand what we are doing and that we avoid potential errors, and after doing some refactoring, you can see that our component is not just reusable but ready for state management.
In the next chapter, I will show you how to use the map we created here and implement Recoil state management and a list to create a widget that displays a résumé in an interactive way.