This blog is fourth in a series of (unofficial) course notes for the Data Visualization with React and D3 series by Curran Kelleher. Read the introductory blog post here.
The next chart in the series is a simple map of the world. In this chart Curran actually uses a Topojson file, then converts it into Geojson and then plots it. I am not planning to do that as it seems complicated and also for some reason the topojson library does not play nice with Node on my computer and I was too lazy to actually debug it š¬. So instead I are going to use a simple geojson I found on D3 Graph Gallery.
I am going to start from a fresh Svelte project instead of carrying on from the last. To do and install d3 along with its:
npx degit sveltejs/template world-map
cd word-map
npm install
npm install d3
And to run the app itself:
npm run dev
Let clean everthing in the App.svelte. The dataset for the map is available here. Let's use the d3 json method to load in the data and use it.
import { json } from "d3";
let dataset = [];
json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson"
).then((data) => {
dataset = data.features;
});
I am only using the features array in the geojson and this contains everything we need to draw the map. Let's start by making an SVG in the markup and adding a width and height property to it.
<script>
const width = 1200,
height = 600;
</script>
<main>
<svg {width} {height}>
</svg>
</main>
If you console.log
the dataset at this point (and well go deeper and deeper into the json tree) you'll notice that the geojson contains the boundary information using a construct called geometry
. The only problem with this is that, this construct is not a svg path and is instead of course a co-ordinate you can plot. To solve this problem D3 provides a powerful geographic path generator, d3.geoPath which can take in a given GeoJSON feature or geometry to generate an SVG path.
D3 also provides a lot of geographical projections as well to transform how the map looks like. I am going to split out this logic to drawing the paths themeselves into a different component called Marks
. So let import the new component into App.svelte
like so:
import Marks from "./Marks.svelte"
And pass the dataset to the component like so:
<Marks {dataset} />
Now of course this component does not exist, so lets go about making it real. We'll also import the projections and path generator,
<script>
import { geoPath, geoNaturalEarth1 } from "d3";
export let dataset = [];
const projection = geoNaturalEarth1();
const path = geoPath(projection);
</script>
Afterwards its really easy. We just need to iterate over the dataset and pass the data to the path
function:
{#each dataset as data}
<path d={path(data)} />
{/each}
And now we have it, a perfectly horrible world map,
I actually kind of like the stark, edgy nature of the map. I was going to style this, but this is actually rather nice.
So of course the next part will be dedicated to showing off a cool svelte feature I just found. Let first remove all the fill
colour from the map and add a stroke
.
<style>
path {
fill: none;
stroke:darkgreen;
}
</style>
Svelte has great support for styling. One thing I like in particular are the transition directives. These are tools for using motion more effectively and gracefully in your app and generally making it look smoother.
Lets import the draw function from svelte/transitons
and add it to the path
tag.
<script>
import { draw } from "svelte/transition";
</script>
<path transition:draw={{ duration: 5000, delay: 0 }} d={path(data)} />
These transition directives actually pack in a lot of flexibility. We can pass in custom easing function to control the speed of the transition and we can even pass in custom CSS and JS to control the transtitions.
import { quadInOut } from "svelte/easing";
<path
transition:draw={{ duration: 5000, delay: 0, easing: quadInOut }}
d={path(data)}
/>
Personally I think that's just awesome š. Here's the final code.
Well that's it for today. Hope you have a nice day!
Top comments (0)