Creating Your Own Maps

choroplethr uses object oriented programming to support users creating choropleths with arbitrary maps. If you are interested in this functionality then I recommend you read two articles before proceeding:

The Choropleth base class

A key component of object oriented programming is recognizing common functionality and placing it in a base class. This allows clients to get a lot of common, repetitive behavior “for free”. The base class in version 2.0 of the choroplethr package is called Choropleth and has five main functions. If you were to create a choropleth program yourself, you would probably wind up doing (at least) these five steps:

  1. initialize(map.df, user.df). This is the constructor. map.df is a data.frame representation of a shapefile and needs a column called “region”. user.df is the user's data and needs columns called “region” and “value”. It does basic error checking on the input.

  2. clip(). This function removes elements from user.df which refer to regions not present on the map. For example, you might have data for the 50 states plus Puerto Rico and Washington, DC. But if the map does not render those regions, you should remove them and warn the user before calling discretize().

  3. discretize(). You need to take numeric values, such as population counts per state, and turn them into discrete buckets so that they can be rendered. For example, you might want to assign each state to one of seven unique colors based on their population.

  4. bind(). Finally, you need to bind the discretized values to the map. You might also want to warn the user if their data set does not include all values on your map.

  5. render(). The last step is to render the results. Rendering the final result might be easy, or complex. For example, rendering all US 50 States to scale is fairly straightforward. But rendering Alaska and Hawaii as insets can be tricky.

Example: World Map

Using the base Choropleth functionality, you can easily create a country-level choropleth. Type data(country.map); ?country.map for details about the map.

library(choroplethr)
library(choroplethrMaps)
library(R6)
library(Hmisc)
library(stringr)
library(dplyr)
library(scales)
library(ggplot2)

# create the class, inheriting from the base Choropleth object
CountryChoropleth = R6Class("CountryChoropleth",
  inherit = choroplethr:::Choropleth,
  public = list(

    # initialize with a world map
    initialize = function(user.df)
    {
      data(country.map, package="choroplethrMaps")
      super$initialize(country.map, user.df)
    }
  )
)

# create some sample data and then render it
data(country.regions, package="choroplethrMaps")
df = data.frame(region=country.regions$region, value=sample(1:nrow(country.regions)))
c  = CountryChoropleth$new(df)
c$render()

plot of chunk unnamed-chunk-1

In this case, the default behavior of the base class is exactly what we want. We just needed to initialize it with our own map.

As for rendering it, we just needed to create it with $new and render it with render. For examples of customizing the map, take a look at the function country_choropleth:

country_choropleth
## function (df, title = "", legend = "", num_colors = 7, zoom = NULL) 
## {
##     c = CountryChoropleth$new(df)
##     c$title = title
##     c$legend = legend
##     c$set_num_colors(num_colors)
##     c$set_zoom(zoom)
##     c$render()
## }
## <environment: namespace:choroplethr>

Using the base Choropleth gives you a lot of functionality for free: