The Simpliest D3 map and way too long article about it, even its title. Breakdown of the Brutalism architecture map

Pavel Laptev
ITNEXT
Published in
15 min readFeb 19, 2019

--

I’m a big fan of Brutalist style in architecture and last month I watched a lot of lectures about brutalist architects — their life and work — the likes of Le Corbusier, Ernő Goldfinger, El Lissitzky and many others.

“A house is a machine for living in” — Le Corbusier

The concept of Brutalism is wide and intersects with other modern architecture styles such as Constructivism and NeoBrutalism.

Brutalism came about as a result of a wide range of reasons, amongst which was the global economic crisis, World War II feeling of presenting a new era, etc. It was a reflection of all these reasons, but came to us mostly as government buildings and public housing, for instance, here is the Sainte Marie de La Tourette Monastery in France by Le Corbusier:

Brutalism architecture is the architecture of “the scale”, as almost all brutalist buildings are large-scale. Also, Brutalist architecture is dominant on the environment, as it tends to suppress and appears in contrast to it.

Brutalism had a bright future, but with time became more and more abandoned, as it (brutalism) tends to gain a lot of negative connotations. This is not surprising in any way, but we will talk about it next time. I definitely will write an article about Brutalism history. Today I want to share how I did a brutalism map, with visual descriptions and some functions in managing the data.

Like I mentioned earlier, I’m a fan and I was wondering: «How many brutalist buildings do we even have in the world? How old are they? Which country has more? etc» To answer all of these questions, I decided to make an interactive map where I can see all buildings in time perspective.

Research

First thing for the map i.e. obstacle #1 is data. My map will be useless if it contains a paucity of buildings.

So. my first thought was «Okay, it’s challenging but I’m ready to spend a couple of weeks to collect the data».

Accepting the fact that my next few weeks would be filled by many lines of JSON code, I began to think, «What if my attempts are hopeless? What if this kind of map already exists?». So, I started to Google and yeah, I found a website which contained brutalist buildings map, timeline, and had much more helpful information about the brutalist architecture 😨😱😭

I thought; «So, my pet-project ended before it started, » or how did Churchill say it:

“This is not the end, it is not even the beginning of the end, but it is perhaps the end of the beginning.”

I was frustrated the whole day about my discovery, because I really wanted to make this map first.

But when I woke up the next morning, (a good sleep always helping me), my mind was clearer and I said to myself; «Okay, might be there is an already existing map, but I can still follow through with the project and maybe, I will learn something new from it». And indeed, I learned a lot, mostly from my mistakes 🙄

TLDR

To spare you too much reading, here is the result: you can filter buildings by year using the bottom slider.

Part I. Data

So, my research ended really quickly, but I found the source. My next step was the data from #SOSBrutalism. I started to search for nosy ways, how I could grab it, and it turned out that they had a template for the page.

I copied this JS object into a JSON file. I also asked guys if it was okay, and they said it was.

I copied this JS object into a JSON file. I also asked guys if it was okay, and they said it was. The next problem that needed solving was the JSON size and extra-keys in it. The original JSON was too large — 1.4Mb, so I wrote a NodeJS script to make it smaller and add my own props.

You may notice that I used German instead of English. This was because I thought that it would give a nice touch; a tribute to traditions to keep the original language. Here’s a little lesson of German:

  • Titel → title
  • Architekten → architect
  • Staat → country
  • Stadt → city
  • Jahr → year
  • Gebäude → buildings

Part II. Design?

I imagined that I’d have a lot of dots on the map, and the main feature of the map should be a range slider to filter dots by year. Also, I should be able to watch information about an object on mouse hover.

Designs for my recent projects were made mostly in browsers and not with designing tools. I find this approach much convenient, but sketching is still better done with designing tools.

So, I made my approximate vision in Figma.

You don’t see a design of the map here, so I decided that working with many objects like map markers would be easier in a browser using real data. But I made sketches of main objects like slider and header — they should be simple, bold and brutal.

Part III. JS library to use

So, I had JSON with Brutalist buildings and I had my vision of the map. The next step- choosing a technology. I wanted to be more flexible, that’s why I declined map services like Mapbox, which have a wide range of adjustments but still, they are not fully customed. Anyways, I found an appropriate solution for me — D3 JS library and its add-on d3-geo. It allows you to render geographic information into SVG or CANVAS.

Running ahead, my choice was wrong because of new circumstances I didn’t know back then, I wish I had known then what I know now. My mistake was not in the library, that I chose D3. I chose SVG rendering over CANVAS… we will get back to this issue later.

D3-geo however, provided me all I needed — full map customization, marker customization, custom tooltips, and popups.

Part IV. Project setup

I don’t like to waste my time that’s why I love preprocessors. My favorite is Stylus for CSS and Pug for HTML.

And when I’m doing a simple project I use Prepros app. It’s a simple way to use Stylus or Pug preprocessors with Live Browser Reload. Zero configuration, drag’n’drop interface.

But, Prepros isn’t so good when you have a deal with ES6. So, I decided to use Gulp. My first thought was to use Webpack, but last time I had some problems with its configuration.

In Gulp I used:

Here is my Gulp configuration file: https://github.com/PavelLaptev/brutalist-buildings/blob/master/gulpfile.js

My project structure:

.
├── 📁source
| ├── 📄index.pug
| ├── 📁scripts
| | ├── 📄main.js
| | └── 📁utils
| | | └── ...
| └── 📁styl
| | ├── 📄style.styl
| | ├── 📄variables.styl
| | ├── 📄map.styl
| | └── 📄slider.styl
├── 📁js
| ├── 📄main.bundle.js
| ├── 📄brut-data.json
| └── 📄world-110m2.json
├── 📁favicons
| ├── 🖼favico-16.png
| ├── 🖼favico-32.png
| └── 🖼favico-96.png
├── 📁css
| ├── 📄style.css
| └── 📄normalize.css
├── 📄index.html
├── 📄gulpfile.js
└── 📄package.json

AAll source files like *.pug, *.styl, and scripts written in ES6 are stored in “source” folder. I used chunks for Stylus and JS.

Part V. Map color coding

In my project, I wanted to have color coding which means that I will be able to recognize buildings by year, not only looking at the range slider but by colors as well. I prefer color coding over shading because it’s more distinguishable. There are exceptions of course, but this time, color coding would work better.

For instance, if you need to show height maps and large polygons on the map, shading will work better; but instead, we have years and small dots.

Also, as you can see, we have two places where we need to use the same colors — scale and markers.

To solve this issue, I used Stylus variables. Why not CSS variables? Don’t know, but I think it was just because it was easier for me to write stylus vars.

I divided the scale into small pieces by five years each.

This color scale is not my first attempt, as I have tried many variations. So to make picking easier, I used Lch and Lab color and gradient picker by David Johnstone.

You can add four general colors and this service will create shades between them automatically.

Part VI. Script breakdown

If Pug and Stylus are easy parts — JS is an interesting piece that I want to explain step by step. But first, we need to add a few scripts to <HEAD> section, links to libraries

And here is the main precompiled script:

line 1—4. The initial part of any modern script — import. Here we are importing noUiSlider library.

noUiSlider is a lightweight JavaScript range slider with full touch support but what was more important for me here is supporting of two range handles. Otherwise, I’d have used native input type ”range”. Also, this library has such methods as dragStart, dragEnd, etc. which is very convenient, due to you the fact that you don’t need to write them by yourself.

menuActions is a chunk with scripts for the burger menu. Nothing interesting here. But I want to notice that to write such types of scripts became much simple, it looks almost like in jQuery.

fillDots() and DotsColorByClass() function which I’m importing in are very boring.

fillDots() is a helper function. When I create all markers (dots) I check if a year of the current dot is 1933 I’ll return the class “cd-193–1935” etc.

DotsColorByClass() a function for ease hover effect. 📛 I’m mistaken — there is a moment when I realized that I made a wrong choice — preprocessor variables over CSS variables. Sadly, I can’t return preprocessor vars and if I want to change my palette, instead of just one place, I’ll have to change it in multiple places. But I kept this eyesore to my future self — Look! You did this, are you proud of yourself? Yes… I thought so!

Keep moving.

range is a simple function which returns an array of numbers between two. We need this function to filter markers that are outside the range.

line 6—26. The simplest part of my script — noUiSlider setup. noUiSlider has very clear and understandable syntax, so I won’t explain it. But you can find docs here.

line 29—34. const projection is the part where we set up projection type, map position, scale, and rotation.

line 36—51.

  • const svg. The main container in which we will project our map with all markers.
  • const path. The shape generator where we place const svg
  • const g. Here we also append SVG group tag in order to have more control over the map and keep it in the group. We didn’t append this tag in const svg because later we will work only with this standalone tag.
  • const rectSizeOne. Just a simple object with sizes for our markers. I just grouped width and height. “One” here is a rudiment, in the beginning, I planned to make different sizes for the marker but refuse this idea after.
  • const tooltip. It’s a popup tooltip that appears on hover. This tooltip should be already in HTML and has CSS property “display: none” by default. I, also, made animated placeholder as an image preloader.

line 54—60. This is the first of two main functions where we parse the JSON file with the World map, and place obtained data in our SVG container. There, we are also using another JS library TopoJSON. TopoJSON is an extension of GeoJSON that encodes topology more effectively.

Highlight this line if you are reading this. You won 5 stars! ⭐⭐⭐⭐⭐

line 62. It’s our second main function. Here we parse brut-data.json, the JSON with all data about brutalism buildings, such as:

line 63. This line is charged to count the length of JSON objects — the amount of all buildings- and place it in the header. Here I’m using literals or string templates as a cool feature from ES6, that allows you to make strings like this "" + data.length + "Gebaeude" in a legible way `${data.length} Gebaeude`

line 65—76. This is the beginning of the long chain of functions where we will create our markers and actions for them.

Here, we create a rectangle shape for each marker; set x/y position by latitude and longitude from the JSON brut-data; and set width and height.

line 77—79. Then we need to add a class to each marker, each class has its own fill. By fillDots() we define which class certain marker will have.

line 80–81. on("mouseover") hover function will manage all our interactions with markers. And we start by filling the marker in black.

line 83—130. This is a long piece, but there is nothing special, just manipulations with the tooltip on hover — functions to include the title, year, image, etc.

line 131—137. on("mouseout") Here we hide the tooltip and bring back the initial color of the marker using DotsColorByClass() and D3 transition() functions.

line 139—152. noUiSlider has many capable functions to manage events and here we are using on("slide") listener.

Each time when a user drags handlers, we will listen to this event and return CSS "display” property "block” If a marker remains within the range, or "none” if a marker is out of the selected range on the scale.

There is an interesting part — how we map markers within the selected range.

As you may know in order to use map() method, we need to have an array. There are several ways on how you can create an “instance” array, but I chose the easiest in terms of syntax. I used the spread operator.

You can read about it in detail in this article:

So, if we see that our “start year” key is not equal to any number in the array, (I chose to filter buildings, not by the “end year,” because it seemed to me that filtering buildings by the “start year” is more correct) we return "display: none".

line 156—172. zoom() function is, also, an event listener function. Here we change transform: translate()property, and also change the size of markers on zoom to keep them under the same size, regardless of whether we zoom out or zoom in the map.

Phew… that’s it, the final part of the script.

Part VII. Testing

I always start my work from the foundation like setting gulp, precompilers, plugins, etc, and move towards the result step by step, using live browser reload.

So, everything worked fine before…

Part VIII. Problems on the horizon

…before I had a conversation with Babak Fakhamzadeh. We were talking about data on #SOSBrutaism and he sent me a link with a new data on public.opendatasoft.com. Unfortunately, it’s not available anymore, so I can’t provide any link here, but I had enough time to grab it. You can download it here 👇

And instead of 1,256 buildings that I had currently, I’ve got 3,433. And this was a problem for me because when you have a deal with DOM elements like SVG and don’t have too much data, everything will be fine with the performance. But if you have a heavy JSON, you need to forget about DOM elements and use CANVAS instead. Here is a good explanation:

When you have a huge amount of elements, always use CANVAS — operations under the Hood are much faster than operations with DOM elements.

The HTML <canvas> element is used to draw graphics, on the fly, via JavaScript.
w3schools.com

And D3 has several methods for rendering, including CANVAS. In the beginning, I didn’t take it into account and started my project as if I don’t have more than 1,000 elements in my JSON. So what should I do?

I asked Captain Obvious and he answered:

Thank you Captain! Also, I didn’t think that my data could be supplemented.

Here is a frame rate comparison between two maps — 1,256 and 3,433 objects (2.6 GHz Intel Core i7, 16 GB 2133 MHz, Radeon Pro 450 2048 MB):

To make everything on CANVAS wasn’t a small amendment; I thought it would take too much time to alter the code, but the main reason was that I burned out because of this project. No, I still honor Brutalism, but had to do the project in a different way. I’ll do my next project from scratch and I’ll pay more attention to details.

Part VIX. The result

I think that I said everything I wanted, so, drum roll, please 🥁

Thank you for reading :-) and hope you enjoyed it

--

--