<!--
%\VignetteEngine{knitr}
%\VignetteIndexEntry{f) Creating Your Own Maps}
-->

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 take my (now free) full-length video course [Shapefiles for R Programmers](https://ari-lamsteins-courses.thinkific.com/courses/shapefiles-for-r-programmers).

The example below assumes you have already read a shapefile into R and wish to connect it to Choroplethr. Note that this example assumes you have some familiarity with [R6](https://r6.r-lib.org/index.html), the Object-Oriented systme that choroplethr uses.

## 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.

```{r warning=FALSE, message=FALSE}
library(choroplethr)
library(choroplethrMaps)
library(R6)

# 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()
```

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`:

```{r}
country_choropleth
```

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

* Error checking on input.
* Ability to zoom in on a subset of regions with the `zoom` parameter.
* Ability to explore the data visually with the `num_colors` parameter.
* Easy labeling of the title and legend with the `title` and `legend` parameters.
