Traveling The World in D3 - Part 3: Legend and Filtering

ce635-screen-shot-2017-06-26-at-17.39.jpg

The Knoyd team is currently spread out all over the world (Chile, Colombia and Austria) and I am living on the road for quite a while now. I had an idea to code up an interactive map for our travel blog and thought it would be nice to share with you how to do it yourself.

This is a multi-part tutorial. To get started, you can get all the code from GitHub and also see the final product here

You can also check out Part 1: Making a Map and Part 2: Points and Links.

Adding a legend

In the last part of the tutorial, we managed to interactively draw location points on the zoomed in country and added a tooltip linking directly to our content. One thing we are missing is an overview of what content is available for which countries without having to manually click through all the points available.

Firstly lets add a legend to the top level view of the map, listing all the available categories of our content. 

var

legend_cont

=

d3

.

select

(

"body"

).

append

(

"div"

).

attr

(

"class"

,

"legendContainer"

);

We also need to add some CSS to make it look nicer.

div

.legendContainer

{

color

:

#000000

;

background-color

:

#fff

;

border-width

:

0.1px

;

border-style

:

solid

;

border-color

:

#000000

;

padding

:

.5em

;

border-radius

:

5px

;

opacity

:

0.9

;

position

:

absolute

;

font-family

:

"Lato"

,

sans-serif

;

display

:

table

;

margin

:

auto

;

position

:

absolute

;

top

:

0

;

bottom

:

0

;}

Now we have a little frame but it will not help us much without some data in it. We will get a variable containing all the categories and add it as elements of unordered list. Lets start by just listing the names of the categories.

//create unordered list

var

legend

=

d3

.

select

(

".legendContainer"

).

append

(

"ul"

).

attr

(

"class"

,

"legend"

);

//add list item for every category

var

legend_items

=

legend

.

selectAll

(

"li"

).

data

(

post_cats

).

enter

().

append

(

"li"

).

html

(

function

(

d

,

i

){

return

d

.

name

});

So far it looks pretty ugly

So far it looks pretty ugly

We can apply some CSS to make it look much nicer:

ul

.legend

{

list-style-type

:

none

;

list-style

:

none

;

padding

:

3px

;

vertical-align

:

middle

;

display

:

block

;

line-height

:

50px

;

font-family

:

"Lato"

,

sans-serif

;

cursor

:

pointer

;}

Even cooler would be if we could display an icon instead of just simply the name. Well, we can. First define a little helper function to help us assemble the necessary HTML for each of the icons.

// get legend items

function

getLegend

(

d

){

var

temp

=

"<img class='legend_icon' title='ICON_TITLE' \

src='ICON_LINK' alt='' width='50' height='50'> \

ICON_KIND"

;

temp

=

temp

.

replace

(

"ICON_TITLE"

,

d

.

name

);

temp

=

temp

.

replace

(

"ICON_LINK"

,

d

.

url

);

temp

=

temp

.

replace

(

"ICON_KIND"

,

d

.

name

);

return

(

temp

);};

Then we just have to apply this function in our HTML call while creating the legend list items.

var

legend_items

=

legend

.

selectAll

(

"li"

)

...

//interesting part

.

html

(

function

(

d

,

i

){

return

getLegend

(

d

);});

With the last little bit of CSS, we make the icons aligned with the name of the category.

ul

.legend

img

{

vertical-align

:

middle

;}

Static legend on a map

Static legend on a map

We now have a decent looking legend on our map. It is, however a little boring if it's just sitting there. In the next section, we are going to make it do something more interesting.

Filtering the content

A nice straight forward idea to make the legend more interactive is to use it to filter our blog content on interaction. Once we click on one of the items in the legend, we would like to color differently only the countries that contain blog posts in this category. Lets get to it.

First we make a little helper function to help us color individual country, very similar to the one we used in Part 1 of our tutorial.

// color country according to legend

function

colorCountryLegend

(

country

,

active_countries

) {

if

(

active_countries

.

includes

(

country

.

id

)) {

return

'#f56260'

;

//color for selected item

}

else

if

(

visited_countries

.

includes

(

country

.

id

)) {

return

'#c8b98d'

;

//same as in Part 1

}

else

{

return

'#e7d8ad'

;

//same as in Part 1

}};

Next we need a helper function to help us figure out, if a particular country has a piece of content in the kind category that we have clicked on.

function

hasContent

(

s

,

kind

){

var

post_keys

=

Object

.

keys

(

s

.

posts

);

return

post_keys

.

includes

(

kind

)};

Finally we put it all together in one function running the entire process on each click. It selects the countries at hand and colors them using the functions above. (For the data structures used, check the files in the repository)

// color countries for particular legend item

function

colorCountriesCategory

(

d

){

//filter relevant countries using hasContent function

var

these_countries

=

trip_data

.

filter

(

function

(

s

){

return

hasContent

(

s

,

d

.

name

);});

var

active_countries

=

these_countries

.

map

(

function

(

a

) {

return

a

.

country

;});

var

unique

=

active_countries

.

filter

(

function

(

item

,

i

,

ar

){

return

ar

.

indexOf

(

item

)

===

i

; });

//color countries

g

.

selectAll

(

'path'

).

attr

(

'fill'

,

function

(

t

){

return

colorCountryLegend

(

t

,

unique

);});};

All we need to do now is triggering the function by clicking on an icon. We can do this simply by adding the action to theli elements.

var

legend_items

=

legend

.

selectAll

(

"li"

)

...

//here

.

on

(

"click"

,

colorCountriesCategory

).

html

(

function

(

d

,

i

){

return

getLegend

(

d

);});

That's it. There are some little things to take care of with discoloring the countries once you decide to zoom in etc., but these are details left as an exercise for you.

Colored by the legend

Colored by the legend

Conclusion

In this short tutorial series, we have learned basics of plotting geographical data using the D3.js library. There are plenty of really cool things you can do with D3 and we have only scratched the surface. If you would like to see something else, please write a comment.

In case you are interested in getting deeper knowledge in Data Science, feel free to check our Data Science Bootcamp BaseCamp and sign up :-)

Previous
Previous

Semantic Similarity Across Facebook Posts

Next
Next

Traveling The World In D3 - Part 2: Points & Links