Proportional symbol maps are used to represent point data that are attached to a specific geographic location (like a city) or data aggregated to a point from an area (like a state). The area of each symbol on the map (usually a circle) is scaled according to its value at a given geographic location using either absolute scaling or range-grading. The result is a map where larger symbols indicate higher values and smaller ones, lower values. This type of map is flexible in that you can represent raw data values (total population) or data that are normalized (percentage of population).
In this blog, I’ll walk through the basic principles of proportional symbols and some best practices when designing them. Then, I’ll cover the steps to make a map of 2015 urban population by country using the absolute scaling method.
There are two methods used to create proportional symbols: absolute scaling and range-grading. With absolute scaling, the area of each symbol on the map is scaled proportionately to its value in the data. With range-grading, values are broken into ranges (typically 3-5) using a classification method where symbols are sized based on the range they fall into (this is similar to the choropleth technique used for polygon data).
Use absolute scaling when you want your map reader to estimate relative magnitudes between individual values. Use range-grading to show magnitudes using classes that represent a range of values.
You will hear proportional symbol maps referred to in different ways. In the CARTO Editor, we call them Bubble maps. Others refer to absolute scaled symbols as proportional symbol and range-graded as graduated symbols. Another way they are referred to is classed (range-graded) and unclassed (absolute scaling).
There are some important design considerations when making proportional symbol maps:
Avoid a homogeneous looking map. It is important that there is enough variation between symbol sizes representing the range of values. With absolute scaling if there isn’t a lot of variation between the values in your data, there will be little variation in symbol size. With range-grading, make sure there is enough differentiation in symbol size from class to class so they are easily distinguishable from one another
Use the right method for your data. Research shows that map readers have a hard time estimating the area of a symbol on a map and even more difficulty between symbols. Make sure your data are appropriate for absolutely scaled symbols. If not, use range-grading to limit the number of symbols that need to be interpreted
Think about your map reader. Cartographic research also shows that map readers interpret the areas of circles and squares best on proportional symbol maps. Definitely experiment with other symbol types (even text!) but when in doubt, default to these
Legends are important. For absolute scaling, your legend should provide symbols that are sized close to the largest and smallest values in the data. And of course with range-grading, each symbol should be sized as it is on the map with its associated values listed
Symbol legibility. Make sure to order your data so symbols representing smaller values don’t get lost underneath symbols with larger values. In areas where there is a concentration of symbols, the key is to help each one stand out against the other. This can be done using a couple of design tricks like symbol outlines
For more information on proportional symbols, definitely check out Cartography: Thematic Map Design by Borden Dent where a lot of this information comes from!
Before we dive into the data preparation and map making process, let’s take a look at some details about the final map:
As mentioned earlier, with absolute scaling, the area of each symbol is proportionate to its value in the data. In future versions of CARTO.js and the CARTO Editor, you will be able to do this on the fly. Until then, this blog outlines the process to make a proportional symbol map using absolute scaling.
The topics covered are:
The data used for this map come from the World Bank Data Bank.
To get the data ready for the map:
.csv
format.csv
to CARTO where it was georeferenced using country nameST_Centroid
I won’t outline each step in detail, but if you would like to follow along, you can download the prepared dataset here.
The attributes in the dataset are:
country_name
: the name of the country
country_code
: the three letter country code
continent_name
: the continent the country belongs to
pop_2015
: the total count for 2015 urban population
Note: the SQL and CartoCSS in this blog assume that the name of your table is world_population_2015
To determine the proper symbol size for each country, we’ll do the following:
To simplify this to an equation:
symbol size = (maximum symbol size) * (square root / max square root)
Where:
maximum symbol size = the size of the largest symbol on our map
square root = the square root of each value in the data
max square root = the square root of the largest value in the data
Note: For a more detailed discussion on this calculation, see Chapter 9 of Borden Dent’s book!
The maximum symbol size is assigned to the largest value in the data and is the starting point from which all other symbols will be scaled. Choose a maximum symbol size that won’t cause too much clutter or overpower the map.
For the final map (after some trial and error), I chose a maximum symbol size of 75px based on the values in the data, the opening scale, and extent.
Note: You should adjust this number depending on your map’s extent, scale, and the range of values. Using the method outlined below, you can easily adjust this number and recalculate symbol sizes.
We need to add two numeric fields to our table: square_root
where we’ll calculate the square root for each pop_2015
value, and symbol_size
where we’ll calculate the symbol size for each country.
square_root
and hit entersymbol_size
square_root
field with the square root of each value in the pop_2015
field:UPDATE
world_population_2015
SET
square_root = SQRT(pop_2015)
Now that we have decided on our maximum symbol size (75) and have the square_root
field populated, we can use that information to calculate the symbol_size
for all other values in the data.
Let’s take a look at our equation again:
symbol size = (maximum symbol size) * (square root / max square root)
Where:
symbol size = our symbol_size
field
maximum symbol size = 75
square root = our square_root
field
max square root = 27620.2642999664
(the square_root
value for China)
With all of the information that we need, we can construct the equation using SQL to update the symbol_size
field:
UPDATE
world_population_2015
SET
symbol_size = 75.0 * square_root / 27620.2642999664
If we look at the resulting sizes, we can see that the country with the largest population (China) has a symbol size of 75 and all other values are scaled accordingly:
Now that we have the additional attributes in the data, we’ll walk through the steps to size, color, and define the drawing order of the symbols in MAP VIEW.
On the final map, each symbol is colored according to its continent using the continent_name
field.
continent_name
for ColumnNext, we’ll size each point symbol using the values from our [symbol_size]
field.
marker-width:
from 10
to [symbol_size]
so the first block of global styling looks like:#world_population_2015 {
marker-fill-opacity: 0.9;
marker-line-color: #FFF;
marker-line-width: 1;
marker-line-opacity: 0.8;
marker-placement: point;
marker-type: ellipse;
marker-width: [symbol_size];
marker-allow-overlap: true;
}
...
If you aren’t satisfied with the symbol sizes after seeing them on the map, you can change the maximum symbol size value and rerun the equation to calculate new values for the symbol_size
field.
Similar to the drought monitor map, I chose six different colors for each continent, assigned them as variables, and then replaced the default category colors. I also made a slight adjustment to the default marker-line-opacity
from 1
to 0.8
.
@africa: #75445C;
@asia: #AF6458;
@europe: #D5A75B;
@northam: #736F4C;
@oceania: #5b788e;
@southam: #4C4E8F;
#world_population_2015[continent_name="Africa"] {
marker-fill: @africa;
}
As outlined in the best practices section, we need to make sure that smaller symbols are being drawn on top of larger symbols so important information does not get covered up. Looking around the map, we can see that there are cases where this is happening.
To fix this, we will change the default drawing order (from ascending) to descending using a field (symbol_size
) in our data along with the SQL operator ORDER BY
. This will force larger symbol_size
values to draw first and smaller values to draw on top.
SELECT *
FROM
world_population_2015
ORDER BY
symbol_size
DESC
Since the map will be viewed from zooms 2-5, we need to size the symbols appropriately based on viewing scale.
We’ll do this with zoom dependent styling that increases the symbol_size
by a factor of 2 at each zoom level for the marker-width
property.
marker-width
styling to the global properties of the layer#world_population_2015 {
marker-fill-opacity: 0.9;
marker-line-color: #FFF;
marker-line-width: 1;
marker-line-opacity: 0.8;
marker-placement: point;
marker-type: ellipse;
marker-width: [symbol_size];
marker-allow-overlap: true;
//scale symbols based on zoom level
[zoom=3]{marker-width: [symbol_size]*2;}
[zoom=4]{marker-width: [symbol_size]*4;}
[zoom=5]{marker-width: [symbol_size]*8;}
}
At this point, your map should look something like this where symbols are colored by continent, sized by the value they represent in the data, smaller symbols are drawing on top of larger ones, and a symbol’s size scales between zooms 2-5:
If you zoom into China, you’ll notice that at zoom 4, the symbol cuts off along a tile boundary.
We can fix this by increasing the map’s buffer-size
, which adds additional pixels around each tile.
Map {
buffer-size: 256;
}
And the result:
For the final map, I designed a simple basemap in World Robinson. For tips on designing a basemap for thematic overlays in a variety of projections, see this post and this post.
Alternatively, you can use one of the default CARTO basemaps like Dark Matter (the lite version):
As discussed earlier, legends are important for proportional symbol maps. For the final map, I made a legend with three data points, assigned them large, medium, and low values and then scaled them using the same formula that was used for all other symbols. I then positioned the circles and text using CartoCSS through zoom level.
I followed the same method for the map title, description, and data source text.
Proportional symbol maps are some of my favorite! There are a lot of interesting stories that can be told using this method in a variety of creative ways.
For example, the final map from the blog can be taken further to include the World Bank’s population projections for multiple time periods:
or to visualize 50+ years of cholera outbreaks around the world:
or for tiger counts in India:
… and many, many more …
Happy proportional symbol mapping!
Raster is faster but vector is corrector!
Data VisualizationMany comparisons have been made between these two iconic cities, not least in terms of size, population and other factors like quality of life and the best pizza slice, but...
Data VisualizationMeetup is the most popular platform to organize community events. With more than 40 million members and 320,000 Meetup groups, the site facilitates 12,000 meetups per day a...
Data VisualizationPlease fill out the below form and we'll be in touch real soon.