DEV Community

Cover image for Maps with D3 and Svelte
AnupJoseph for Learners Just that

Posted on

Maps with D3 and Svelte

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
Enter fullscreen mode Exit fullscreen mode

And to run the app itself:

npm run dev
Enter fullscreen mode Exit fullscreen mode

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;
});
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

And pass the dataset to the component like so:

<Marks  {dataset} />
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

And now we have it, a perfectly horrible world map,
World map basic
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>
Enter fullscreen mode Exit fullscreen mode

Simple styled world map

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>
Enter fullscreen mode Exit fullscreen mode
<path  transition:draw={{ duration: 5000, delay: 0 }}  d={path(data)} />
Enter fullscreen mode Exit fullscreen mode

Map draw gif

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";
Enter fullscreen mode Exit fullscreen mode
<path
    transition:draw={{ duration: 5000, delay: 0, easing: quadInOut }}
    d={path(data)}
/>
Enter fullscreen mode Exit fullscreen mode

Image description

Personally I think that's just awesome 🚀. Here's the final code.

Well that's it for today. Hope you have a nice day!

Discussion (0)