Introduction

This package contains a C++ implementation of the RecMap algorithm [1] to draw maps according to given statistical values. These so-called cartograms or value-by-area-maps may be used to visualize any geospatial-related data, e. g. political, economical or public health data. The input consists of a map represented by overlapping rectangles. This map is defined by the following parameters for each map region:

The (x, y) coordinates represent the center of the minimal bounding boxes (MBB), The coordinates of the MBB are derived by adding or subtracting the (dx, dy) tuple accordingly. The tuple (dx, dy) defines also the ratio of the map region. The statistical values define the desired area of each map region.

The output is a rectangular cartogram where the map regions are:

The construction heuristic places the rectangles in a way that important spatial constraints, in particular

are tried to be preserved.

The ratios are preserved and the area of each region corresponds to the as input given statistical value z.

The power of a cartogram based visualization is demonstrated below.

Rectangular Cartogram of the U.S. election 2004; The area corresponds to the number of electors (color indicates the party red: democrats / blue: republican; the color intensity ~ outcome of the vote.). The graphic was computed by using the original implementation of the construction heuristic RecMap MP2 introduced in [@recmap].

Rectangular Cartogram of the U.S. election 2004; The area corresponds to the number of electors (color indicates the party red: democrats / blue: republican; the color intensity ~ outcome of the vote.). The graphic was computed by using the original implementation of the construction heuristic RecMap MP2 introduced in [1].

The Algorithm

Input using the U.S. state Facts and Figures Dataset

usa <- data.frame(x=state.center$x, 
    y = state.center$y, 
    # make the rectangles overlapping by correcting lines of longitude distance
    dx = sqrt(state.area) / 2 / (0.8 * 60 * cos(state.center$y*pi/180)), 
    dy = sqrt(state.area) / 2 / (0.8 * 60) , 
    z = sqrt(state.area),
    name = state.name)

Determine the Desired Areas

The desired area \(\tilde{A_j}\) of a map region \(r_j\) is defined as \[\tilde{A_j}= z_j \cdot \frac {\sum_{i=1}^{k}A(r_j)} {\sum_{j=1}^{k} z_{j}}\] (CTRL-C CTRL-V from [2]).

Compute Pseudo Dual Graph (PD)

The rectangles have to overlap to compute the dual graph. This enables to generate valid input having only the (x, y) coordinates of the map region.

op<-par(mfrow=c(1,1), mar=c(0,0,0,0))
library(recmap)
plot_recmap(M <- usa[!usa$name %in% c("Hawaii", "Alaska"), ],  
            col.text = 'black', lwd=2)

Construction heuristic

  1. Compute the dual graph

  2. Determine the core region

  3. Place region by region along DFS skeleton of pseudo dual starting with the core region

  4. If a rectangle can not be placed find a feasible solution (no overlapping regions) by placing it next to a neighbor

Computational geometry help functions

A local placement function can place an rectangle b (rainbow colored rectangles) around a given rectangle a of any angle \(\alpha\) between \([-\pi,\pi]\). There are special cases for quad I, II, III, and IV (see also the graphics on std::atan2) indicated by different colors in the graphic below.

`a.x = 2, a.y = -5, a.dx = 20, a.dy = 5, b.dx = 1.5, b.dy = 5`

a.x = 2, a.y = -5, a.dx = 20, a.dy = 5, b.dx = 1.5, b.dy = 5

Metaheuristic

The index order of the input map has an impact to the resulting cartogram. This is caused due do the computation of the dual graph. In [1] a genetic algorithm was applied as metaheuristic. Due do the limited compute resources on the CRAN package build process no metaheuristic will be executed to build this vignette file.

Study the examples of the reference manual ?recmap on how the GA package can be used.

Objective Functions

The topology error is an indicator of the deviation of the neighborhood relationships. The error is computed by counting the differences between dual graphs or adjacency graphs of map and cartogram

The relative positions error measures the angle difference between all region centers.

Output

The output is a data.frame object.

head(Cartogram <- recmap(Map <- usa[!usa$name %in% c("Hawaii", "Alaska"), ]))
##            x        y       dx       dy         z        name dfs.num
## 1  -86.12445 24.40204 3.201992 2.697824 227.17614     Alabama      31
## 2 -123.13242 29.61981 3.939586 3.257613 337.50407     Arizona      38
## 3  -85.30009 39.65352 3.265364 2.683509 230.44305    Arkansas      33
## 4 -131.42392 31.53933 4.341915 3.488740 398.36290  California      37
## 5 -122.00152 23.21422 3.965671 3.095895 322.87304    Colorado      47
## 6  -53.13517 41.31657 1.896954 1.418697  70.77429 Connecticut      19
##   topology.error relpos.error relposnh.error
## 1              4    0.2287945      0.6128110
## 2              4    0.2110718      0.7496132
## 3              6    0.7596130      2.8277677
## 4              3    0.1369841      0.1614465
## 5              8    0.6025215      1.3705189
## 6              3    0.1954060      0.4808290

Application

Rectangular Map Approximation

smp<- c(29, 22, 30, 3, 17, 8, 9, 41, 18, 15, 38, 35, 21, 23, 19, 6, 31, 32, 20, 
        28, 48, 4, 13, 14, 42, 37, 5, 16, 36 , 43, 25, 33, 12, 7, 39, 44, 2, 47,
        45, 46, 24, 10, 1,11 ,40 ,26 ,27 ,34)
plot_recmap(Cartogram.Population <- recmap(M[smp, ]), 
            col.text = 'black', lwd=2)
## Warning in recmap_(df): Arizona could not be placed on the first attempt;
Rectangular Map Approximation.

Rectangular Map Approximation.

state.x77[, 'Population']

op<-par(mfrow=c(1,1), mar=c(0,0,0,0))
usa$z <- state.x77[, 'Population']
M <- usa[!usa$name %in% c("Hawaii", "Alaska"), ]
plot_recmap(Cartogram.Population <- recmap(M[order(M$x),]), 
            col.text = 'black', lwd=2)
Area ~ population estimate as of July 1, 1975;

Area ~ population estimate as of July 1, 1975;

op<-par(mfrow=c(1,1), mar=c(0,0,0,0))
# index order

smp <- c(20,47,4,40,9,6,32,33,3,10,34,22,2,28,15,12,39,7,42,45,19,13,43,30,24,
         25,11,17,37,41,26,29,21,35,8,36,14,16,31,48,46,38,23,18,1,5,44,27)

plot_recmap(Cartogram.Population <- recmap(M[smp,]), col.text = 'black', lwd=2)
Area ~ population estimate as of July 1, 1975; a better index order has been chosen to minimize the relative position error.

Area ~ population estimate as of July 1, 1975; a better index order has been chosen to minimize the relative position error.

state.x77[, 'Income']

op<-par(mfrow=c(1,1), mar=c(0,0,0,0))
usa$z <- state.x77[, 'Income']
M <- usa[!usa$name %in% c("Hawaii", "Alaska"), ]
plot_recmap(Cartogram.Income <- recmap(M[order(M$x),]), col.text = 'black', lwd=2)
Area ~ capita income (1974);

Area ~ capita income (1974);

state.x77[, 'Frost']

op<-par(mfrow=c(1,1), mar=c(0,0,0,0))
usa$z <- state.x77[, 'Frost'] 
M <- usa[!usa$name %in% c("Hawaii", "Alaska"), ]
plot_recmap(Cartogram.Income <- recmap(M[order(M$x),]), 
            col.text = 'black', lwd=2)
Area ~ mean number of days with minimum temperature below freezing (1931–1960) in capital or large city;

Area ~ mean number of days with minimum temperature below freezing (1931–1960) in capital or large city;

More examples using state.x77 data and shiny application are available through https://recmap.shinyapps.io/state_x77/.

Synthetic checkerboard

Checker boards provide examples of sets of map regions which do not have ideal cartogram solutions according to Definition 1 [2].

op<-par(mar=c(0,0,0,0), mfrow=c(1, 3), bg='white')

plot_recmap(checkerboard8x8 <- recmap:::.checkerboard(8),
            col=c('white','white','white','black')[checkerboard8x8$z])

# found by a greedy randomized search
index.greedy <- c(8, 56, 18, 5, 13, 57, 3, 37, 62, 58, 7, 16, 40, 59, 17, 34,
                  29, 41, 46, 27, 54, 43, 2, 21, 38, 52, 31, 20, 28, 48, 1, 22,
                  55, 11, 25, 19, 50, 10, 24, 53, 47, 30, 45, 44, 32, 35, 51,
                  15, 64, 12, 14, 39, 26, 6, 42, 33, 4, 36, 63, 49, 60, 61, 9,
                  23)

plot_recmap(Cartogram.checkerboard8x8.greedy <- recmap(checkerboard8x8[index.greedy,]),
            col=c('white','white','white','black')[Cartogram.checkerboard8x8.greedy$z])

# found by a genetic algorithm
index.ga <- c(52, 10, 27, 63, 7, 20, 32, 18, 47, 28, 6, 55, 11, 61, 38, 50, 5,
              21, 36, 34, 2, 22, 3, 1, 29, 57, 43, 4, 51, 58, 31, 49, 44, 25,
              59, 33, 17, 40, 8, 41, 26, 37, 19, 56, 45, 35, 62, 53, 24, 64,
              30, 15, 39, 12, 60, 48, 16, 23, 46, 42, 13, 54, 14, 9)

plot_recmap(Cartogram.checkerboard8x8.ga <- recmap(checkerboard8x8[index.ga,]),
            col=c('white','white','white','black')[Cartogram.checkerboard8x8.ga$z])
checkerboard fun - input, area of black regions have to be four times as big as white regions (left); solution found by a greedy random algorithm (middle); solution found by genetic algorithm (right)

checkerboard fun - input, area of black regions have to be four times as big as white regions (left); solution found by a greedy random algorithm (middle); solution found by genetic algorithm (right)

History

The work on RecMap was initiated by understanding the limits of contiguous cartogram drawing [2] and after studying the visualizations drawn by Erwin Raisz [3]. The purpose of the first implementation [1] was a feasibility check on how computer-generated rectangular cartograms with zero area error could look like. The recmap R package on CRAN provides a rectangular cartogram algorithm to be used by any R user. Now, it is easy to generate input (e.g, no complex polygon mesh), the code is maintainable(<500 lines of C++ code), and the algorithm is made robust to the price of not having all features implemented (simplified local placement; no empty space error; no MP1 version yet). Recent research publication on rectangular cartograms drawing include [4], [5], [6], [7]. However, according to to recent publication [8] recmap remains the only rectangular cartogram algorithm ‘maintain zero cartographic error’.

Session Info

sessionInfo()
## R Under development (unstable) (2016-04-28 r70561)
## Platform: x86_64-apple-darwin13.4.0 (64-bit)
## Running under: OS X 10.11.5 (El Capitan)
## 
## locale:
## [1] C/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
## [1] recmap_0.4.0 Rcpp_0.12.4 
## 
## loaded via a namespace (and not attached):
##  [1] magrittr_1.5    formatR_1.3     htmltools_0.3.5 tools_3.4.0    
##  [5] yaml_2.1.13     stringi_1.0-1   rmarkdown_0.9.5 knitr_1.12.3   
##  [9] stringr_1.0.0   digest_0.6.9    evaluate_0.9

References

[1] R. Heilmann, D. A. Keim, C. Panse, and M. Sips, “RecMap: Rectangular Map Approximations,” in 10th IEEE Symposium on Information Visualization (InfoVis 2004), 10-12 October 2004, Austin, TX, USA, 2004, pp. 33–40.

[2] D. A. Keim, S. C. North, and C. Panse, “CartoDraw: A Fast Algorithm for Generating Contiguous Cartograms,” IEEE Trans. Vis. Comput. Graph., vol. 10, no. 1, pp. 95–110, 2004.

[3] E. Raisz, “The Rectangular Statistical Cartogram,” Geographical Review., vol. 24, no. 2, pp. 292–296, 1934.

[4] M. J. van Kreveld and B. Speckmann, “On rectangular cartograms,” in Algorithms - ESA 2004, 12th annual european symposium, bergen, norway, september 14-17, 2004, proceedings, 2004, pp. 724–735.

[5] M. J. van Kreveld and B. Speckmann, “On rectangular cartograms,” Comput. Geom., vol. 37, no. 3, pp. 175–187, 2007.

[6] K. Buchin, B. Speckmann, and S. Verdonschot, “Evolution strategies for optimizing rectangular cartograms,” in Geographic information science - 7th international conference, gIScience 2012, columbus, oH, uSA, september 18-21, 2012. proceedings, 2012, pp. 29–42.

[7] K. Buchin, D. Eppstein, M. Löffler, M. Nöllenburg, and R. Silveira, “Adjacency-preserving spatial treemaps,” Computational Geometry, vol. 7, no. 1, pp. 100–122, 2016.

[8] S. Nusrat and S. Kobourov, “The State of the Art in Cartograms,” in EuroVis 2016, 18th EG/VGTC Conference on Visualization, 6-10 June 2016, Groningen, the Netherlands, 2016.