Quickstart#

Start with a GeoDataFrame of a polygon data set that has a column with attribute values referring to absolute numbers of an occurrence, for instance the population of counties or the voter counts in election districts.

For this example, we prepared a sample data set that you can also download from the GitHub repository. This data set contains polygons showing the nine federal provinces of Austria, with population numbers attached in a column called pop20170101.

import geopandas
df = geopandas.read_file(DATA_DIRECTORY / "Austria_PopulationByNUTS2.geojson")
df
fid nuts2 pop20170101 area_km2 geometry
0 Austria_PopulationByNUTS2.0 AT11 291974 3957.21 MULTIPOLYGON (((609942.82 394253.82, 609947.46...
1 Austria_PopulationByNUTS2.1 AT21 561098 9542.04 MULTIPOLYGON (((448385.84 340577.89, 451424.65...
2 Austria_PopulationByNUTS2.2 AT12 1665815 19190.88 MULTIPOLYGON (((500206.72 521459.79, 502393.35...
3 Austria_PopulationByNUTS2.3 AT31 1465205 11974.26 MULTIPOLYGON (((429364.86 398384.06, 429318.2 ...
4 Austria_PopulationByNUTS2.4 AT32 549372 7132.61 MULTIPOLYGON (((443539.12 352496.95, 443549.12...
5 Austria_PopulationByNUTS2.5 AT22 1237372 16409.74 MULTIPOLYGON (((448385.84 340577.89, 448354.6 ...
6 Austria_PopulationByNUTS2.6 AT33 746179 12614.21 MULTIPOLYGON (((183502.04 410278.82, 181681.71...
7 Austria_PopulationByNUTS2.7 AT34 388711 2580.89 MULTIPOLYGON (((158753.18 347618.77, 153773.88...
8 Austria_PopulationByNUTS2.8 AT13 1867960 414.03 MULTIPOLYGON (((629216.51 473309.2, 622571.51 ...
df.explore(column="pop20170101", cmap="Reds")
Make this Notebook Trusted to load map: File -> Trust Notebook

As you can clearly see, population numbers diverge greatly. Vienna (AT13), the capital, has the highest population with almost 2 million inhabitants, while it has the smallest surface area. By contrast, Carinthia (AT21) and Tyrol (AT33), in the South and Western parts of the country, have small populations spread over a large surface area.

Let’s resize the polygons so that the relative surface area is representative of the relative population of each province, by initialising a new instance of Cartogram, passing a column name:

import cartogram

c = cartogram.Cartogram(df, "pop20170101")
c
fid nuts2 pop20170101 area_km2 geometry
0 Austria_PopulationByNUTS2.0 AT11 291974 3957.21 POLYGON ((593486.809 374150.357, 593493.328 37...
1 Austria_PopulationByNUTS2.1 AT21 561098 9542.04 POLYGON ((441683.591 336402.448, 443996.659 33...
2 Austria_PopulationByNUTS2.2 AT12 1665815 19190.88 POLYGON ((487711.786 511535.541, 489677.12 513...
3 Austria_PopulationByNUTS2.3 AT31 1465205 11974.26 POLYGON ((420435.459 376869.504, 420391.946 37...
4 Austria_PopulationByNUTS2.4 AT32 549372 7132.61 POLYGON ((437602.462 341642.897, 437610.335 34...
5 Austria_PopulationByNUTS2.5 AT22 1237372 16409.74 POLYGON ((441683.591 336402.448, 441659.03 336...
6 Austria_PopulationByNUTS2.6 AT33 746179 12614.21 MULTIPOLYGON (((304767.137 356370.148, 313725....
7 Austria_PopulationByNUTS2.7 AT34 388711 2580.89 POLYGON ((177610.646 345810.215, 170725.38 339...
8 Austria_PopulationByNUTS2.8 AT13 1867960 414.03 POLYGON ((547561.53 496623.625, 564627.071 523...
c.explore()
Make this Notebook Trusted to load map: File -> Trust Notebook

cartogram.Cartogram inherits from geopandas.GeoDataFrame, so all of the latter’s methods and attributes are available in the result dataframe. For instance, to save the output data set to a new file, just use to_file():

c.to_file(DATA_DIRECTORY / "cartogram.geojson")

Fine-tuning the distortions#

The algorithm implemented in python-cartogram is an approximating, iterative approach that was first presented by Dougenik et al. (1985), and is also used by the QGIS plugin cartogram3.

Because of its iterative design, the computation becomes more precise with every repetition. This, in turn, means longer computation times. You can control the accuracy of the output data set using two parameters, overriding their default values: the maximum number of iterations, and the desired average areal error remaining in the output. The two are complimentary: if the remaining average error is lower than the specified threshold before max_iterations is reached, the remaining iterations are skipped.

c = cartogram.Cartogram(
    df,
    "pop20170101",
    max_iterations=99,
    max_average_error=0.05,
)

Average residual area error

The remaining error is stored in an attribute of a cartogram: cartogram.Cartogram.average_error

c.average_error
np.float64(0.031417324873839636)

References#

Dougenik, J. A., Chrisman, N. R., & Niemeyer, D. R. (1985): An algorithm to construct continuous area cartograms. The Professional Geographer, 37(1), 75–81. DOI:10.1111/j.0033-0124.1985.00075.x