An Introduction to the D3 Module d3-geo for Svelte Charts
An introduction to d3-geo
Get the project source code below, and follow along with the lesson material.
Download Project Source CodeTo set up the project on your local machine, please follow the directions provided in the README.md
file. If you run into any issues with running the project source code, then feel free to reach out to the author in the course's Discord channel.
This lesson preview is part of the Better Data Visualizations with Svelte course and can be unlocked immediately with a \newline Pro subscription or a single-time purchase. Already have access to this course? Log in here.
Get unlimited access to Better Data Visualizations with Svelte, plus 70+ \newline books, guides and courses with the \newline Pro subscription.

[00:00 - 00:09] To start off this module, we're going to be plotting some countries. Obviously, we can't make a map, we can't make a rotating globe without countries rendered on that globe.
[00:10 - 00:19] And so the first lesson for this module is plotting countries. Specifically, we're going to be using a popular D3 module called D3GO.
[00:20 - 00:48] I'll go ahead and pull up some documentation that you can look at right here, which as you can see, basically handles map projections, spherical shapes, and spherical trigonometry. Lots of big words, but essentially, this handles a lot of the complex parts of mapping that we don't want to spin up by hand, that we can instead rely on this package to draw a complex, country-based, or geographic shapes for us.
[00:49 - 00:56] So this is D3GO at a glance. I would definitely recommend taking a look at some of the documentation on GitHub, just look up D3GO.
[00:57 - 01:10] But let's go back into our code to kind of describe exactly what it is that we want to do. Well, first of all, we probably want to delete everything that's currently here , because this is serving no purpose for now.
[01:11 - 01:21] Let's save, get a blank page, and describe the steps that we want to accomplish in order to plot countries. We're going to be carrying out four discrete steps.
[01:22 - 01:42] These are documented in the text of the lesson, but they are first creating a projection function, second creating a path generator, third actually rendering the country paths, and then fourth adding an actual globe that all these paths will live on. Now I'm going to go into greater depth for each of these steps once we get to them.
[01:43 - 01:55] So I'm not going to spend too much time on this high-level overview explaining each of those four steps. But before we even get started with either of those steps, we do need to go ahead and import some data.
[01:56 - 02:02] And specifically, we're going to get data from two places. First, you're going to download from the internet, which I'll provide a link for.
[02:03 - 02:09] And second, we will download using npm. So first, we're going to want to install world 110m.json.
[02:10 - 02:17] So this is a GeoJSON file, which contains all of the coordinates of the world's countries. So you can go ahead and click on it here.
[02:18 - 02:25] And then this should open up a raw web page. And you can just hit command S and save this as whatever you want to call it.
[02:26 - 02:38] I believe I want to name mine world 110m.json. And then, maybe if I go to the right link, we'll find exactly.
[02:39 - 02:51] And then so what we want to do now that we have this installed is put it within our source /data folder. So go ahead and find that and drag it in to your data folder and hit save.
[02:52 - 03:12] And then once you have that installed, you're going to also want to import a second piece of data, which is from topo.json. So basically, this library that we're going to install takes raw data in a Geo JSON file and turns it into a format that D3.io can actually ingest.
[03:13 - 03:26] So this isn't data so much as it is a package. And so within our terminal, right, we can actually open up a side by side terminal right here and write npm install topo.json-client.
[03:27 - 03:29] Hit enter. You'll see that it did in fact succeed.
[03:30 - 03:36] So then we can close that terminal. And then we want to import these to this data and this library.
[03:37 - 03:46] So we'll open up a script tag. Remember that this is where our logic lives and we'll write those two commands and copy them directly from here if you'd like, or you can follow along with the video.
[03:47 - 03:53] We can go ahead and save this and refresh. And at first, nothing appears, obviously because nothing is being rendered.
[03:54 - 04:12] We can go ahead and verify this is in fact working by console.logging our world variable and seeing what it looks like. We see that it's an object with a variety of different values inside including arcs, a B box or bounding box, objects and a transform method and then a type.
[04:13 - 04:35] And so those are some of the things that are included in the raw data that we derive from world 110m. But the second part that we're going to look at today is topo.json because this is essentially a toolkit of sorts that can take these values within our world and turn them into D3 readable objects.
[04:36 - 04:46] So again, this might look a little boilerplatey. We're basically just doing what is necessary in all geographic projects that deal with D3.
[04:47 - 05:02] So we're going to create two new variables, one for our countries and one for our borders. And countries is going to be equal to this code right here where we basically write topo.json.feature and then we describe what it is that we want to access.
[05:03 - 05:19] So we want to within our world look at world.objects.countries and then outside of this, we have to add features as well. And then once you have this written, you can save and let's verify that this worked by console.logging countries.
[05:20 - 05:23] Save here. Now we see 177 countries.
[05:24 - 05:31] Each of those countries cleanly represent a single item. We have an ID, some properties and a type and coordinates, most importantly.
[05:32 - 05:38] So we know we're probably doing something right. We also want to do the same thing for our borders.
[05:39 - 06:03] And rather than creating topo.json features, we want this to be a topo.json mesh. So we're going to write let borders equal topo.json.mesh and then almost an identical bit of code where we write world, comma, and then we do world objects.countries and then this accessor which basically distinguishes one country from another.
[06:04 - 06:15] So a, comma, b, and then an arrow function where a does not equal b. Now I want to make a note really quick because this looks like a lot of complex code.
[06:16 - 06:27] You're probably like, what the heck is going on? The reality is the dirty truth that most people would not tell you is that most of this, a lot of people don't understand.
[06:28 - 06:42] Even if they're working with geographic visualizations, they may, they might not know precisely what all of this code is doing. What they do know is that for pretty much every D3GO project where you're rendering, you know, these shapes and these countries, you're going to have to use it.
[06:43 - 06:48] And so, you know, this is, you find the example online, you'd copy it, you'd paste it. This would create your countries and your borders.
[06:49 - 07:05] The important, the important thing is we know this works and we know what the output of each of these two objects is, as you can check by, you know, console.logging them and inspecting internally. But what we know is that borders is a multi-line string with coordinates that basically draw the borders of all countries in one.
[07:06 - 07:22] And then countries is an array of 177 countries, each with, you know, geometry coordinates and ID, some other properties, and a type. And so let's break down what we have here, because if we understand the structure of, you know, this internal data, it will help us actually plot countries.
[07:23 - 07:29] There are four things I want to draw attention to. Type, which in this case for our countries is always going to be features.
[07:30 - 07:47] ID, which is going to be the country's ISO 3 digit code. Properties, which you can ignore because in our file it's empty, and geometry, which crucially contains the coordinates of that country's shape or its borders in an array.
[07:48 - 07:59] So you'll notice that coordinates has a nested array within, and that array has 69 array elements, each with a length of, it looks like two. So that is the output.
[08:00 - 08:09] And this is what your country's object should look like in most geographic projects. And in this case, we're going to be using our country's IDs to link to our data .
[08:10 - 08:21] As you'll see later, whenever we import data, these three digit ISO codes are going to be what we can match across data sets. So it's very important that we have the ID and the geometry coordinates all in one.
[08:22 - 08:38] So for fun, you know, we could go ahead and visualize, if you want to call this a visualization, our countries as they currently exist. We could simply render the country geometry coordinates and then end our each tag and see what this produces.
[08:39 - 08:45] It produces something absolutely beautiful like this with all the coordinates listed. So maybe this isn't very helpful.
[08:46 - 09:01] But right now, this is actually all we could do because if all we have are these literal coordinates, there's no way for us to actually kind of project these onto a map onto a canvas onto anything. All we have is raw coordinates.
[09:02 - 09:12] And so what we need is, you know, the first step in this lesson is going to be a projection function. And so what is a projection function?
[09:13 - 09:23] Basically projections can be thought of how a map is presented. This could include, you know, its shape, its level of granularity and the angle that we are viewing that map at.
[09:24 - 09:27] So there are a lot of different types of projections. I'll go ahead and show you some over here.
[09:28 - 09:32] Some really fun ones. So if you want to make your map look like this, feel more than free, but you probably don't.
[09:33 - 09:49] Triangular heart shaped, star shaped, you could transition between globe projections like so, but we're going to be using a very simple one, which is the orthographic projection. This is probably stock standard example of what a globe looks like, right?
[09:50 - 10:00] So the first thing that we're going to do is take our countries and our borders and create a projection to map them onto. And so in order to do this, we want to go ahead and delete this console.
[10:01 - 10:13] We want to import this new method called geo orthographic because that is the name of the projection that we want to use. We want to import it from D3 geo.
[10:14 - 10:21] So you can go ahead and save that, make sure there are no errors. And if there are not, then we can begin by creating our actual projection.
[10:22 - 10:42] Now our projection is going to have a few arguments and you're going to notice that it kind of resembles what we're used to with D3 scales and that it has a single opener and then it has multiple arguments, kind of methods appended in like a string like fashion, like one after another after another. So it's very similar to the syntax that we're used to.
[10:43 - 10:52] So projection is going to be an orthographic projection and it's going to have the following properties scale. Wow, that is not what I wanted.
[10:53 - 10:57] So which will for now leave blank. It's not letting me leave a blank.
[10:58 - 11:21] Rotate, which will also leave blank for now and translate. So on a high level, I think all three of these are pretty intuitive, but scale determines how big a projection is, rotate determines how it is rotated on an x, y and z axis and translate is kind of where the projection is centered in the overall view of things.
[11:22 - 11:32] So for now we're going to pass some naive values that we can actually get this projection up and running. So let's begin by instantiating a width and a height of 400.
[11:33 - 11:52] And then we can use these to create our projection to start off. So for now, let's say, and this kind of makes sense if you're thinking of a single globe, that the width of the scale can be half of the width, that the rotation for now can just be zero and that the translation should basically be centered in the middle.
[11:53 - 12:06] So if the scale of this is half of the width, meaning it's basically half of the canvas size, we want to move it halfway down and halfway to the right within the overall viewport. So we want these values for our projection.
[12:07 - 12:16] And so now that we have this projection, we can actually create a path generator that will draw our shapes. So you might be asking, what does this do on its own?
[12:17 - 12:24] It doesn't do anything. We need to pass this projection into a brand new path generator, which is the second step.
[12:25 - 12:34] So we're going to add a new import up here, which is geo path. And again, anything that you want to learn more about, I would definitely recommend doing some research.
[12:35 - 12:54] You could look up geo path and then obviously append D three, find some documentation and see what this does. You know, I would just look it up and see exactly what geo path does is similar to the shape generators and D three shape, given a geo JSON geometry or feature object, it generates an SVG path data string or renders a path to canvas.
[12:55 - 13:06] So essentially, what we know is that given a geometry or feature object, this will create an SVG path data string. That's exactly what we want to do if we want to render countries.
[13:07 - 13:30] So with that being established, let's go ahead and use this new geo path and just create one line of code, which will create a path based on geo path of projection. So this is the syntax for basically creating a new path function that given a series of coordinates will produce an actual SVG shape.
[13:31 - 13:41] And it's important that we pass on this projection as the argument. So geo path knows what the overall dimensions, basically how the transform should be applied to the raw coordinates.
[13:42 - 14:06] And so if we, for example, were to console dot log our borders, but pass them into this path function, what would we notice? What we would see is that this no longer looks like the borders object that we had seen earlier with the, you know, this type of multi line string and some nested objects within, this is a singular long incredibly long string path.
[14:07 - 14:24] And what this really is, in case you couldn't recognize the syntax, because I know it looks like a bunch of gibberish, it's an SVG path. So remember that we've dealt with SVG paths before, but SVG paths take in this D attribute, which basically can be a series of line commands, right?
[14:25 - 14:42] Move from X to Y, move from X to X, move from Y to Y. And so the simplest path could be a path with a D of M 10 10. And so if you want to study how paths work internally, you can, but the fun thing is D three handles this for us.
[14:43 - 14:57] And as you can see here, this long SVG path kind of represents exactly what we 're trying to get at, even if you can't tell yet. So now that we have these paths, we can go ahead and render our path on the page.
[14:58 - 15:14] And so remember that all of this is going to live within an SVG element, just like our other charts, we want to start with an SVG. So we're going to apply a width of width and a height of height, just like every other time, we're going to open and close this SVG element.
[15:15 - 15:19] And what do we want inside? Let's just start off by adding our borders.
[15:20 - 15:32] So we do path in the D attribute, just as we logged above is simply path of borders. Let's go ahead and save this now and see what we see.
[15:33 - 15:53] So we do have a globe, but there's some funky zebra like fill patterns going on , because D three or because SVG is trying to fill this lengthy path attribute in ways that we don't want it to. So the simplest way to fix this, the proper way to render a series of borders is to apply a fill of none.
[15:54 - 16:04] And then nothing will appear, so we need a stroke of black. And now you can actually see the borders appearing for every single country on our globe.
[16:05 - 16:28] Now just to illustrate kind of what's happening behind the scenes and the relationship between projections and between path generators, what would happen if we added a rotation of 90 degrees to our projection, all of a sudden we're at the bottom of the earth. Put it on the x-axis instead, we would see what looks like to be north and south America, and if we did so on the z-axis, I don't even know.
[16:29 - 16:37] But the point is we could play around with rotation here. We could change the scale, for example, to make this exceed the overall boundaries of the chart.
[16:38 - 16:48] We obviously don't want to do that, but these are some of the arguments that we need in order to render these country paths. If we had nothing at all, we would see it to be out of place and not really sure where it's rendering.
[16:49 - 17:05] And so obviously we do want to apply the correct arguments to our orthographic projection. The other cool thing while we're still here is going back to visiting our projection functions once again, you could definitely, if you were interested, find a list of projection functions that are fun to play around with.
[17:06 - 17:31] For example, you could just Google D3 Geo projection functions, find one that p iques your interest. So remember that we are using orthographic, but if you wanted to do stere ographic, all you would have to do is go into your code editor and replace this import with this new one, stereographic, and it would now assume that new shape, right?
[17:32 - 17:44] So you could really play around with some funky ones if you wanted, but I think this is totally sufficient for now. So let's go back to our old one, Geo orthographic, and let's continue with our chart.
[17:45 - 17:58] So we have a series of countries, well, we only have their borders, but we can see the outlines of the countries themselves. Now let's actually iterate through our country's object to render the actual countries themselves.
[17:59 - 18:12] So I'm going to remove this console log of borders, and I'm going to re console our countries to remind you what it is that the country's array is. So there are 177 items, each of them with these series of properties.
[18:13 - 18:23] And the cool thing is if we pass any of these individual countries into our newly created path generation function, it will in fact return an SVG string. So let's go ahead and see what would happen.
[18:24 - 18:41] If I passed the first item in our country's array into our path function. And then we need to prefix that there you go, because this was declared with the dollar label, we also need this to be declared with the dollar label as well.
[18:42 - 19:08] Now we see an another string path that resembles very similar to the last one, an SVG path element string. And so we know that if we, for example, for all 177 countries in our data set, pass that country into a path function, it would return the corresponding SVG path, which is exactly what we're going to do with an each block, as you might remember, from previous lessons.
[19:09 - 19:20] We want our each block to be rendered before our borders. The borders kind of cleanly cut off the countries and there's no, you know, tension for trying to draw at the edges of the countries.
[19:21 - 19:29] So I'm going to remind us this is where we're rendering our countries. And I'm going to open in each block and call each country in our array country.
[19:30 - 19:41] And then I'm going to open a new path with a D element again, or attribute that 's equal to path of country. Remember that this is going to look like this guy right here.
[19:42 - 19:51] For now I'm just going to apply a kind of ugly fill of light green and a stroke of none. I'm going to close this path and end our each block and hit save.
[19:52 - 19:59] Now you see each of our countries actually appears. If you wanted to make this a little bit cleaner looking, you could change the stroke of borders from black to white.
[20:00 - 20:08] And now we have a pretty good looking globe. Let's remind ourselves that this is the border with a comment as well.
[20:09 - 20:13] So we have a globe. The only difference or the only problem is we don't actually have a literal circular globe behind it.
[20:14 - 20:20] So if we wanted this to be a different color from the background, we don't have that handle yet. So thankfully this is quite easy to do.
[20:21 - 20:28] We're going to add one final bit of SVG code. And that is going to be a single circle that has what properties, right?
[20:29 - 20:33] Well, remember that this is just a globe. This is just a single simple circle.
[20:34 - 20:44] So for this projection, it's actually very, very easy. We wanted to have a CX, meaning its exposition that's halfway through the screen and a CY that is also halfway through the screen.
[20:45 - 20:48] Okay. So this is centered at the 50%, 50%.
[20:49 - 20:57] We want its radius to be width divided by two or height divided by two because they're equivalent. And then let's give it a fill of light blue and hit save.
[20:58 - 21:01] Now we see a globe does in fact appear. Does it look very pretty?
[21:02 - 21:07] No. But has it given us the starting point we need to start off with geographic visualization?
[21:08 - 21:13] Yes. So let's recap what we've done with kind of the four steps that we described earlier, right?
[21:14 - 21:34] The first thing that we did was we imported data from world 110m.json. And then we used topo.json client, which is this NPM package to kind of turn that raw data into JavaScript accessible objects or variables that we called countries and borders.
[21:35 - 21:44] Then we knew that we wanted to render those on the canvas, but we weren't sure how. The answer were D three projection functions and D three path generators.
[21:45 - 22:05] So we created a projection function with, which used geo orthographic with the following parameters and then a path generator function, that's simply one line of code. And with this final path generator function we create online 22, we were able to render countries in each block and borders in a path here as well.
[22:06 - 22:12] Finally, we added a globe behind. So this actually looks like a globe visualization.
[22:13 - 22:29] So it's actually quite simple to get up and running, but it is a bit more boilerplate than some of the previous charts that we've worked on in this course so far. So in the next lesson, we're going to take this and tweak it just a little bit so that this globe is responsive on a variety of screen sizes.
[22:30 - 22:31] So thanks for tuning in, and I'll see you there.