Data-Driven Documents (D3) is a vast and powerful JavaScript library. It's pretty mindblowing that it lets you draw a map of the world, with all the countries in it in just 16 lines of code here.
import { select, json, geoPath, geoNaturalEarth1 } from 'd3';
import { feature } from 'topojson';
const svg = select('svg');
const projection = geoNaturalEarth1();
const pathGenerator = geoPath().projection(projection);
svg.append('path')
.attr('class', 'sphere')
.attr('d', pathGenerator({type: 'Sphere'}));
json('https://unpkg.com/world-atlas@1.1.4/world/110m.json')
.then(data => {
const countries = feature(data, data.objects.countries);
svg.selectAll('path').data(countries.features)
.enter().append('path')
.attr('class', 'country')
.attr('d', pathGenerator);
});
Granted, there's an index.html file with another 12 lines of code, and a CSS file with another 10, but still. What's exactly going on here? How do these lines finesse an entire world map?!
FYI, this code comes from a free data visualization course posted by VizHub.com founder Curran Kelleher on YouTube.
Before we jump into the code, read up on this D3 concept called the data join, how to write CSS, and load data. For the highest fidelity, watch this 17-minute video and come back to get a line-by-line breakdown here.
Line 1
import { select, json, geoPath, geoNaturalEarth1 } from 'd3';
We're importing four D3 modules/functions using ES6 module syntax here. They'll all be used in the code to follow.
select This d3 module selects the first DOM element that matches the specified selector string.
json A function from the d3-fetch module that fetches the JSON file at the specified input URL.
geoPath Geographic path generator. Given a GeoJSON geometry or feature object, this module generates an SVG path data string.
geoNaturalEarth1 A geographic map projection style.
Note: The D3 library was loaded in the index.html file using a <script>
tag on line 6.
Line 2
import { feature } from 'topojson';
TopoJSON is an extension of GeoJSON (a format designed for representing geographical features using JavaScript Object Notation(JSON)) that encodes topology. As per the docs, it's "substantially more compact than GeoJSON, frequently offering a reduction of 80% or more even without simplification".
Topojson.feature is the function that we need to convert our TopoJSON data into GeoJSON. So that we can feed the GeoJSON object into the path generator, which converts it into an SVG path string.
According to the docs, "feature
returns the GeoJSON Feature or FeatureCollection for the specified object in the given topology. Feature objects in GeoJSON contain a Geometry object with one of the above geometry types and additional members. A FeatureCollection object contains an array of Feature objects."
In line 15, we invoke feature
, passing in as the first argument the data, and countries as the second argument.
Line 3
const svg = select('svg');
The const
declaration creates a read-only reference to a value. The D3 select function selects the SVG element. We call the D3 select function on the SVG element, which was declared in the index.html file, and assign it to a named constant named svg
. We do this so that we can apply data-driven transformations to it. We'll be using this SVG selection to draw a map with the TopoJSON data fetched from World Atlas via unpkg.com.
Line 4,5
const projection = geoNaturalEarth1();
const pathGenerator = geoPath().projection(projection);
We make use of the geoNaturalEarth1
projection style that we imported in line 1. We assign it to a named constant, which is passed on to the geoPath.projection function on line 5. geoPath, as the name suggests, is a geographic path generator function, that returns an SVG path data string.
Line 6-8
svg.append('path')
.attr('class', 'sphere')
.attr('d', pathGenerator({type: 'Sphere'}));
These three lines draw a sphere around the world map. We can generate a working viz without these three lines. We draw a new path by appending the SVG (line 6), and on this path, we set the d attribute to be our pathGenerator, and pass in an object where the type is 'Sphere' (line 8).
Line 9-15
json('https://unpkg.com/world-atlas@1.1.4/world/110m.json')
.then(data => {
const countries = feature(data, data.objects.countries);
svg.selectAll('path').data(countries.features)
.enter().append('path')
.attr('class', 'country')
.attr('d', pathGenerator);
});
Here's where it call comes together. First, we fetch the shape data of the countries from the World Atlas TopoJSON file and then pass a function as input, the data.
We pass to named constant countries
an invocation of topojson.feature
, passing in as the first argument the data
, and the second argument is the countries(data.object.countries
).
On a data join on the SVG, we append a new path
- a generic SVG element for defining a shape, for each country in countries.features
using the selection.enter() update pattern.
Each country has its class set as 'country' (for CSS styling purposes).
The outline of the country is defined by the d attribute, a series of path commands which defines the path to be drawn. For each datum, which represents a country, the d
attribute is assigned SVG path data strings by pathGenerator
.
If you're unfamiliar with the SVG format, inspect the rendered map over in Vizhub with your browser. This is what the path and d
attribute for the USA looks like.
<path class="country" d="M72.70434895236906,191.09611811966766L72.19310739632198,191.61483633277732L71.64838185889676,191.1747140441221L71.9342578027443,190.3101145914064L71.78147092492122,189.17812827353703L71.9988005865149,188.8374516883223L72.57906211054973,188.32902946936042L72.55531296528062,187.72097558775337L72.77988334274085,187.43265755021267L72.96257404298359,187.4903220118901L73.83519014347632,188.014524788678L74.22851507754035,188.28185458065875L74.54780247305035,188.6959354583083L74.94703299243025,189.78082689971478L74.838651545407,189.95376648959228L73.65994311624263,190.6140454999502ZM72.51986767838088,186.26354988216983L71.5771097720455,186.4889967744632L71.30839953229616,185.83361000662592L71.07473601842412,185.58192702144328L71.09840052818487,185.38791592798725L71.42230574153984,185.1257312427267L72.27358068908904,185.419377508779L72.83412357496468,185.89128622895083ZM71.1839587492887,184.61706835218106L71.01012246529518,184.94744072307918L69.6350458923153,184.85829396852012L69.93482661660681,184.4807202236086ZM68.96957540117052,184.16606185962536L68.77987869121898,184.3443697779916L68.6130264483291,184.30241532918927L67.73280700558615,184.19228386004613L67.59754047727017,183.49474967492654L67.52945101056201,183.3688724552654L68.34391169603254,182.93877730560163L68.50379665046916,183.1380925048769ZM65.13516021749712,182.12048538371442L64.73881247325727,182.4247315858694L64.02819795468463,181.8634411250656L64.22456820012508,181.6378651596216L64.71564898139383,181.33359030526873L65.29692949144965,181.3965446251982ZM319.0089593195414,110.56334538575857L319.1524924800909,111.56187806339628L316.1915095057085,113.03577001454482L313.4247578117457,114.0883630096582L310.632120488919,114.99214621709561L308.8575328692207,116.8070804891133L308.26115901435094,117.49553106020318L307.8004472712768,119.11725278656607L308.17935158211367,120.74103174258951L309.1682756818513,120.81889828290224L309.2058640304733,119.69805523334773L309.7562680016212,120.37771369776766L309.33664639160975,121.25503269079812L307.5565134009854,121.75363985088643L306.402580033509,121.69649879723059L304.4607364538508,122.23163678517486L303.3569614732547,122.38234275124336L301.89203323082893,122.53306438951682L299.6226306248253,123.42211619210704L303.3676150450837,122.84495163128744L303.93811208750384,123.42731688287599L300.2689552856847,124.34811776279243L298.706121950337,124.35332158253861L298.87574227857976,123.97869088983958L297.90907898205614,124.83214635296125L298.5960795892988,124.97269833002841L297.49144744746866,127.18144932765554L295.0835631608836,129.5547510883519L295.1082060035913,128.76157834249574L294.6069323666262,128.6050697851661L293.9995358468151,127.83315248275687L294.07755213146646,129.4921204403852L294.5515296749405,130.0402055636676L294.3028912219482,131.2047228044348L293.2100089202114,132.40645923784746L291.19972642970504,134.8745837828708L291.0066508445487,134.74902633615972L292.29367489973697,132.64688415599937L291.32269174238223,131.47113788629645L291.6878784798072,128.90766461786643L290.8720483204633,130.23859704585936L290.8984704163262,132.19741478052697L289.3901124629163,131.71146197455158L290.841165715893,132.70960784996146L290.2231937624137,135.64898780885176L290.8790813175136,135.8635583526534L290.8828100012049,136.93142405723964L290.50741117814533,140.02722286319687L288.39346896050415,142.328602144662L285.58194196128545,143.24637758195814L283.52344305873555,145.0614978035052L282.21870462661707,145.260887265559L280.6831611046009,146.39964848536954L280.09540230383163,147.44414907840326L276.8753739825713,149.45488890399557L275.1204755954675,150.9304679760362L273.5246082581622,152.77395370675868L272.6629443013227,154.97494750500474L272.6736411410453,157.1341855650233L272.986178392355,159.7874601454639L273.70213003190247,161.9836723673013L273.4565892380128,163.32869127945293L274.0185139925187,166.92737931844965L273.5671685209923,169.01799869393244L273.2476573973862,170.22601521957412L272.28580123879294,172.12185320251757L271.44776458821985,172.5156883585592L270.23646847839655,172.13760686300225L270.059207235832,170.77746551791427L269.2076186675147,170.06320171123645L268.3105935676967,167.39490217803822L267.55681967896294,165.02040246042503L267.3997854520318,163.80679296257554L268.327117782128,161.74198577834338L267.95760942003926,160.03440327716004L266.49624022089006,157.43891024125355L265.6045258239427,156.96606286990345L262.7260860981812,158.37411530882912L262.3091053017,158.21649506661296L261.37896823959875,156.77167220903928L259.95167889467916,155.99937768604167L256.9849398667667,156.3934024880881L254.79728804454558,156.04666030846977L252.8045160529786,156.26206015671224L251.64925637689038,156.745403304052L251.92259044275332,157.5702579334154L251.5966213613643,158.83121657173672L252.0044154980058,159.44594359780174L251.42854489567014,159.8505094315532L250.58078108212723,159.39340266856868L249.49463085621352,159.98186216620238L247.65621184081687,159.88728819028023L246.12018825937702,158.2480190748762L243.80854074560006,158.63156271445808L242.1181297630729,157.91176410412905L240.4773732113685,158.13243114683348L238.1574322170164,158.857486861163L235.29658935652895,161.15878265866286L232.44577751620062,162.49856678859402L230.70732371530397,163.98016882362592L229.81327237729053,165.37764488650043L229.3187728407346,167.51572069758652L229.13062700907125,169.00749375615624L229.392363506042,170.06320171123645L228.36962170786558,170.15773882595911L226.68372030037628,169.47495620015835L224.87685271027445,168.5085007902555L224.47172317189043,167.05345408411137L224.39487278574416,164.8785547703124L223.28848351758398,163.10802686394823L222.8342448517788,161.29013500559375L222.072560270071,159.1674769739673L220.5816272297493,157.92752599826878L218.48157046479213,157.98531966053622L216.25314885620816,160.43896998554698L214.37015262652073,159.50899274367902L213.28546419140918,158.5737682968739L213.0977735973915,156.8662404725941L212.6878927101053,155.24287016939448L211.55019781162792,153.87703481794696L210.52352781084863,152.90001529547604L209.90367403902906,151.79702333667063L205.5124576395351,151.79702333667063L205.14393082654607,153.07860476786863L203.12363610360416,153.07860476786863L198.07131914228285,153.09961546240922L192.93634577050125,150.91471314601188L189.5864474687644,149.40763273595695L190.0159555115144,148.7985795279825L186.69598196262223,149.13460277971294L183.7414272311632,149.37612878370527L183.83859029118622,147.790605227325L182.79742311491378,146.0060407382598L181.74430657984175,145.63345109176322L181.77498388758528,144.74668819169653L180.4161235344592,144.5892908015848L179.80056399139664,143.75517013918204L177.57526559506903,143.44569154282732L177.10995812167477,142.94742348991133L177.4225188482436,141.24851114415236L176.14409189808748,138.13585956968853L175.7400600209748,133.8389209698626L176.11534820482092,133.12258118703346L175.42887184410017,132.10335111071976L174.5821607900918,129.51821630179444L175.3136733013332,127.00422725882842L174.73687010921776,125.32152830579228L176.4064909293134,122.77737044184273L177.51891642343986,120.14420395949742L177.83804012014105,117.7958811427695L180.16863811746225,114.91464986425765L181.8217501263186,112.14911637274523L183.49516634671738,109.39113946631662L185.17578007636638,105.33107597484761L185.81143993745252,102.74743056476646L185.89559722210277,101.35195678164692L186.49913717148814,100.76704078758354L189.35418803710013,101.7845828949572L189.09337816580057,104.63072784951765L190.10317896400568,103.83398916508116L191.04175946569774,101.3672216619047L191.59760014087425,98.90355292392155L198.2579489265076,98.90355292392155L205.23102982517207,98.90355292392155L207.53848568618469,98.90355292392155L214.6975154088135,98.90355292392155L221.61988299185128,98.90355292392155L228.66903386395572,98.90355292392155L235.709732516789,98.90355292392155L243.68862750871537,98.90355292392155L251.7266880355395,98.90355292392155L256.58671411642683,98.90355292392155L257.09277955336074,97.75340307236482L257.8920997782841,97.73821577256308L257.59796312147296,99.38545926036858L258.11032973110525,99.89308444415028L259.6764746368716,100.07591797281555L261.8847111273673,100.55353553528164L263.786916309045,101.48426283059754L265.85559092571134,101.09250194873081L268.447905422859,101.87622994262077L269.23342017681017,101.84567968462895L271.6734965769789,100.99586561507292L273.467556005666,102.08502320535342L275.33906146718897,103.2522625675559L276.8749007350543,104.2577963061639L278.3670731354364,105.22368446194272L278.31171406367207,106.01666377249879L278.76203837948225,106.31358479362547L278.5083472055326,106.61573186010216L279.11856655444535,106.70793362043844L279.6926049789017,106.39551237967854L279.5584196004186,107.1127138482195L279.87132865161084,107.58946932717222L280.5240388338865,107.58946932717222L280.7456842554946,107.96387808916631L280.25763615379225,108.5026874832273L282.26823688607044,109.92564920852877L281.85881893490546,112.68514083935668L281.4853025464872,115.33836260645089L280.20647079888613,117.14349165164299L278.5337928950744,118.82694999910618L277.66597466667895,119.9003597849051L277.5155071437733,120.2168470566809L277.65154420099475,120.657979013069L278.32893118070353,121.14079366411067L278.943527702933,121.14079366411067L282.27401520951014,119.50096807077415L284.9238774634474,119.01874978385348L288.55422654346944,117.5007088793055L288.6914770342121,117.19007916437153L288.7575135588006,116.25350553310966L288.5578301404928,115.65366762512338L289.79312253942334,115.17299210447484L292.1725240070573,115.15748987587236L294.3890519750213,115.16265726143874L295.5209305810662,113.96963607178571L295.88765669532165,113.73222173847068L299.0976655115583,111.54127949077946L300.34834671847045,110.98013415585436L303.99993674166933,110.95954778539024L308.4227217125037,110.95954778539024L308.88277954880243,110.20844555868052L309.695403102259,110.05418252679797L310.84857166348,109.58640564224456L312.1030681516743,108.20501936856394L313.5189409617558,105.8477726823971L316.01162970181275,103.56858678444001L316.5603580512917,104.35995213938398L318.3075680229532,103.84930356100872L319.1016819868474,104.72271006032273L317.9237908303863,108.84666759903934ZM139.06517906588755,75.08101207375367L135.91624220336655,76.17412237344533L135.40171447515758,75.435372118875L136.2081716055734,74.09225075822732L139.09839268388782,73.07674147286463L140.65430192917222,72.6422533288212L141.96335417718467,72.83530488111046L142.1419698670756,73.71962133521606ZM118.54966001338914,67.14408571051428L116.77830637476046,67.58237238521244L115.85961304745729,67.05839017591285L115.2960589001172,66.29271179150047L117.91757047507974,65.81313516030693L119.38834424507309,66.06946981751267ZM116.40250292164427,56.40232356988673L117.13789643638006,56.925540891214325L118.76395010362302,56.643000017705276L119.70373217857428,57.36601181968706L121.42353499964253,57.732713056460256L120.93188649775982,58.030063110698705L118.6977087571538,58.61153474357383L117.71461492072382,58.01611932237435L117.43668946239535,57.5191450536845L115.39235888724824,57.67698737176565L115.14879275041409,57.43560969065274ZM196.5034115301035,40.582298239550795L186.98084648058148,50.393943717010615L173.13512711487215,66.03148377247493L175.20712339776463,66.11220849745203L176.6905908872879,66.89181271338265L177.21305268007552,68.12613168291315L177.68088182806014,69.98229787052836L181.06639620390501,68.40306275591962L183.99153449998835,67.4870518377659L183.9982242187046,68.9479271929599L184.57633253467338,70.10698086383778L185.6214400573101,71.38473515635928L185.4636226032548,73.42948205187639L185.35014592341014,76.69976259156275L187.8668850972013,78.53450507406438L186.65524440993528,80.36176762865094L184.44034882303083,81.7626449033693L183.94973684077485,80.66617550691862L182.60382553799138,79.74372715892113L183.7629352800078,77.22601271508529L182.57924943582083,74.90151304718327L183.401278948587,72.20338548238686L181.4149252024883,72.0202557164526L177.9604883616828,71.94798938309279L176.00696844900602,71.12983331050464L173.8176419040139,68.19295975169084L172.16109302724323,67.65864513731367L169.1784797360466,66.65396588966556L165.95098247880247,66.8965711021639L162.77893695934472,65.6043935071032L161.2450291684102,64.41101952380143L158.3098781226039,65.00251328723294L157.05033425311518,66.95367620678547L155.68260187588248,67.13456307510327L152.64524844980076,67.72062742023664L149.88238216910912,68.67062420897216L146.92184575484458,69.27328122644616L148.08318490499488,67.61097296836601L151.53597054831425,64.85573869741717L154.616061235696,63.99515011426348L154.65859553690672,63.296747386034895L150.48593651994685,64.85100495629317L147.30802963875107,66.7205486422194L142.36269650035234,68.72320164073037L142.74745511333958,70.0973885302277L138.87896619598348,72.13590744135297L135.4511372564766,73.33281213547187L132.47030919014526,74.20361742367683L130.82006419257186,75.46936669770426L126.04952249110767,76.95306635591379L124.18377887100286,78.30964990948218L120.50069607784462,79.5427914816596L119.14058594637862,79.32235035428255L116.34674863186723,80.13113527478453L113.20984971839846,81.11823134939374L110.50033754740753,82.08763218188471L105.89429143968562,82.92076924541925L105.96163383476033,82.43254930926449L109.60988716188774,81.07398989439247L112.58524964000162,80.18019669253752L116.36101467747415,78.59318078978862L119.43119219943014,78.27055575418115L121.56701278308532,77.08464510609184L126.1639177105792,75.36739074924284L127.16761643854193,74.7899693087146L129.69512108071262,73.7825104210375L131.9980532697316,71.62053654932404L134.62494542232753,69.94394143571134L131.3117900999137,70.80301618344416L131.04234950984642,70.3132697708815L128.91435971937625,71.34624950004107L128.79344051086701,69.90558856191328L127.26615217923779,70.923139875706L127.7396480622375,69.51267717492297L124.49109707461275,70.64450579474547L123.14268477449752,70.64450579474547L124.52750079659131,68.95749273152802L125.8946539490762,67.92571384811319L125.44350383632684,66.9203638998379L122.09017508377667,67.45846009406662L121.52073573993965,66.14070352615022L120.6932141433428,65.46687455632983L122.25007268521915,63.886530998227954L121.77710430584278,62.698459882685285L124.2329445313054,61.101398720056295L127.55278280203402,59.557838140534415L129.76540068476936,58.146282385451286L131.6949352510763,57.946408498828134L132.71011088781933,58.383486117557624L135.77984750642065,57.059942592944935L137.07345300925635,57.296427528764866L139.5571727465421,56.448594762399495L140.441924217291,55.20140807620186L139.77131711617494,54.712988416752296L142.4139478039279,53.66476335363146L141.09532540329485,53.69689805455263L138.2475156890834,54.285036726317884L136.9719666408746,54.888003128565L135.94520867352634,54.285036726317884L132.64873676270088,54.59329252899818L130.26467439716902,53.94030175432772L130.56201148736733,52.84864220194146L129.641923977597,51.281561474659696L133.80757877977237,50.15765097670152L139.8806479134813,48.847539356526255L141.5780248689436,48.847539356526255L139.82863706914833,50.189449645223334L144.32228639508486,50.084979962600954L144.44135356883305,48.427081353491815L143.02017084579643,47.412149429962426L143.04907423248716,46.08196455935632L142.3604396504461,44.95405693659549L140.52269827874358,44.11981648561786L143.25072919427885,42.74650082518872L146.94385380119792,42.66227075305119L150.86965234728524,41.46802421612165L152.81464099916923,40.20421014412261L156.30175494816558,38.97256168967755L158.59837611990935,38.67538155502169L163.70253104469816,37.533594998045544L165.3342250390482,37.707578528237406L169.96693965943695,36.34078781636265L172.35316770725905,36.877910797570536L172.48177582895966,38.03412465772635L173.9216793960618,37.53794310186365L177.11016276128237,37.6901747008304L176.33796392215703,38.28258333090133L178.90693294241794,38.72343158720449L181.22164566749944,38.46581397474742L184.5561545966919,39.27883882116109L188.14643261581364,39.52411930983973L189.33818723729098,39.861753296796024L192.44409067599452,39.44087344442801L194.68756361227656,40.21739032498769Z"></path>
Interactive code annotations here
That's Cool. Why Bother?
Why make a world map from scratch? Why not use a static image, or a pre-made SVG that could be inserted into an HTML page?
This example is just a barebones version that you would not see outside of a classroom. A D3 dataviz usually weaves in data from other datasets, like this choropleth world map that color-codes countries by their state of economic development. It implements tooltips and zoom effects, enabling rich interactivity, deep analysis and discovery. You can bring in any dataset, from population to earthquake data, and represent it in any shape, add a time slider, interactive legends, hover effects, tooltips, etc.
The D3 library has hundreds of modules, enabling interactivity and customization down to the nuts and bolts of your viz. You can create your own visual representations, and you are only limited by your imagination. You don't get to do that if you were using an app like Google Data Studio, Datawrapper, or Flourish. For my next post, I'll walk you through this refactored version titled Cheap Tricks for Interaction that implements tooltips and zoom on the world map. I'll also walk you through how to bolt on a new dataset into the viz.
Top comments (5)
Many thousands more lines of code if you include all the imports
Sure, but try working that into a catchy title!
Well, its not all about writing catchy titles. It should also preferably be true. 😅
I think it was Carl Sagan who said, If you wish to make an apple pie from scratch, you must first invent the universe.
Reworked the title to make it more truthy.
Oh saved!