D3 Step-by-step Guide — (Part 3 of 4) Singapore HDB Resale Price on Planning Map — d3 Visualization Dev

Clarence Cai
5 min readMar 22, 2020

This is part 3 of the 4-part guide. In this part, we look at how we can create the d3 map visualization.

This is a practical step-by-step guide to creating a map visualization in d3. In this 4-part guide, we set off to create a visualization to display whether the area where the resale prices of HDB flats have changed throughout the years. Check out Part 1 here.

Here’s a demo of what you will be creating in this tutorial. Here’s where you can find the source code.

Tutorial Parts

Follow Along This Tutorial

You can follow along this tutorial by using this base project: https://github.com/ccjx/hdb-resale-prices-tutorial/blob/master/visualization_base.zip

Extract the zip file into the ‘visualization’ folder. I have set up a basic project based on a template that I have used previously on d3 projects (which I have picked up from a d3 tutorial from someone else). I think it is very crucial to work out a basic boilerplate and use that to quickly prototype your visualizations projects. You can always tailor the styles to any page that you’d like to cater for in the future, but not worrying about the basics, saves you time. Feel free to reuse this as a future boilerplate project for yourself.

This boilerplate comes with:

  • jQuery v3.2.1
  • Bootstrap v3.3.7
  • d3.js v5.15.0
  • d3-tip v0.9.1
  • topojson v3.0.2

Not all of them are used for this, but for the purpose of a prototype visualization, this is not an issue. For production visualizations, removing unused dependencies will reduce the load time for your site. In addition, using a package manager will help to keep dependencies in line and updated as well.

I have also added the processed JSON files from the data processing tutorial.

Basics — Target Main Flow

What I want to achieve, as a start is the following:

  • A map of Singapore with the planning areas laid out
  • We select a single year, let’s start with 1990
  • Each region is highlighted with a color, with its intensity adjusted to how close the average price/sqm is to the highest price of all regions (for that year)

Set Up Main Chart

First, on the top of the file, we set up a width and height as well as append the root svg to the chart div.

Set Up State Variables

Now, we set up state variables to keep track of the current state of the chart. We will show the 1990 data as a start. The purpose of the rest of the variables will be clearer later.

Set Up Chart Elements that Require State Variables

We will now set up an svg group to render everything under. Then, we will print the current year on the screen, as well as set up a legend.

For the legend, we will be needing it as we will be rendering a color based on how high the price/sqm is for each district. We will be using d3.interpolateBlues to generate a blue color as per the value in the range of [0,1]. So we should display what each color means as well so that it is clearer to the viewer.

Data Preparation Step

Before we can render the chart, we should prepare the data first.

In this step, we first select the records that are from the current year.

year = currentYear let selectedPrices = consolidatedPrices.filter(p => (new Date(p.monthDt)).getFullYear() == year)

Then, we merge the records together to create a consolidated one-year record.

Render Main Chart

Now that we have the data prepped and put in mergedPrices, we can render the chart.

First, we update the year that is rendered on the screen (this will be important later). We then calculate the maxPrice of the current year’s dataset so that we can both calculate the colors, as well as render the legend.

There’s also some boilerplate projection set up for the d3 projection.

We then render the actual map. Each path (district) is painted onto the screen, with a black stroke and a fill that is calculated based on the price/sqm as a percentage of the maxPrice. If this district is not in the dataset of prices, we will paint it white (which is 0).

The final result will be this renderChartfunction with all the steps to paint the chart on the screen.

Render Tooltips

When the viewer hovers over each district (or taps on them on mobile), we want to display a tooltip, showing the current district, as well as the mean price/sqm. We can easily achieve this with d3-tip.

First, we set up the d3-tip object, and attach a callback to render the tooltip content on the screen. We then add it to the svg set up.

Once the above is done, we attach the tooltips to the mouseover/mouseout event handlers.

Test

Now we will need to test what we have developed so far.

In order to run the visualization’s index.html, we will need a http server. Due to default browser security settings, we cannot load files directly if we run html files directly. (i.e. file:// protocol).

A quick way to do this is to use npm package http-server.

npm install -g http-server

Then, run the following command in the same directory as the index.html.

http-server . -c-1

The -c-1 option is to ensure that no resources are cached by the browser.

Starting up http-server, serving .
Available on:
http://192.168.1.150:8080
http://192.168.99.1:8080
http://127.0.0.1:8080
Hit CTRL-C to stop the server

Hold Ctrl and click on one of the links and you should see the visualization displayed in your browser. Good job on this so far 🎈.

Adding Progression

Now we will add an auto progression which will move our datasets from 1990 to 2019 a second at a time. We will skip 2020 as it is only 1 month’s worth of data.

The progress function then moves the year ahead or back based on the directionand playpausevariables.

We then add a d3.interval bit in the post-dataload promise callback to progress the year, re-prep the data, and update the chart.

Finalize with Interactivity

Finally, we plug-in the event-handlers of the buttons to allow them to change the state variables. Now we can play/pause, reverse direction as well as skip forward/backward by 1 or 3 years.

All’s Well That Ends Well

And great, refresh the index.htmlpage and enjoy the updated version with progression animation and interactivity.

Next up, we will upload this to Github Pages, and share it with the world.

--

--

Clarence Cai

Someone who loves technology, creates technology, and loves creating technology. Find out more about what I’ve done: https://www.linkedin.com/in/clarencecai/