Moving parts of a country over a map

[This article was first published on Clean Code, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

I love making maps, I also love making gifs.
In this short post I make an animated gif of parts of a map moving. In this case the parts of the map only move in the xy direction, but you can also turn them, and make them bigger or smaller.
Today I show you how I made a part of the Netherlands ‘float away’. It is part of a larger nonsense project (I have many silly projects), and mostly just to document my path to learning about spatial analytics.

End result :

General principles

  • make small functions that do one thing well (not that small in this case)
  • combine those
  • make imagemagick canvas
  • write to the canvas
  • animate the canvas

loading libraries and data

<span class="n">suppressMessages</span><span class="p">(</span><span class="n">library</span><span class="p">(</span><span class="n">tidyverse</span><span class="p">))</span><span class="w"> </span><span class="c1"># ggplot2, dplyr, purrr, etc.</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">magick</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">sf</span><span class="p">)</span><span class="w">
</span><span class="n">library</span><span class="p">(</span><span class="n">paletti</span><span class="p">)</span><span class="w"> </span><span class="c1"># thanks @edwinthoen</span><span class="w">

</span><span class="c1"># colorscheme</span><span class="w">
</span><span class="n">dutchmasters_fill</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">get_scale_fill</span><span class="p">(</span><span class="n">get_pal</span><span class="p">(</span><span class="n">dutchmasters</span><span class="p">))</span><span class="w">
</span><span class="c1"># the data</span><span class="w">
</span><span class="n">NLD</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">read_sf</span><span class="p">(</span><span class="s2">"data/NLD_adm1.shp"</span><span class="p">)</span><span class="w"> </span><span class="c1"># I cannot redistribute the data from GADM, but you can download and use it for your projects</span><span class="w">
</span>

basic functions

I created a function that takes a name, uses that to filter the data and apply a transformation on that part only. (a mutate_if() could also work, but I didn’t know how). And also one that uses that function to plot. The final function takes a matrix of xy values and sequentially applies every row to the plotting function.

  • modify data
  • plot a single ggplot version
  • loop or apply over range
<span class="c1"># basic function that moves an a province</span><span class="w">
</span><span class="n">move_province</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">provincename</span><span class="p">,</span><span class="w"> </span><span class="n">movement</span><span class="p">){</span><span class="w">
    </span><span class="n">mov</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">quo</span><span class="p">(</span><span class="n">movement</span><span class="p">)</span><span class="w">
    </span><span class="n">rest</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">NLD</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">filter</span><span class="p">(</span><span class="n">NAME_1</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="o">!!</span><span class="n">provincename</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">filter</span><span class="p">(</span><span class="n">TYPE_1</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s2">"Water body"</span><span class="p">)</span><span class="w">
    </span><span class="c1">#rest %>% st_centroid() %>% st_as_text()</span><span class="w">
    </span><span class="n">province</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">NLD</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">filter</span><span class="p">(</span><span class="n">NAME_1</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="o">!!</span><span class="n">provincename</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">mutate</span><span class="p">(</span><span class="n">geometry</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">geometry</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="o">!!</span><span class="n">mov</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">st_set_crs</span><span class="p">(</span><span class="s2">"+proj=longlat +datum=WGS84 +no_defs"</span><span class="p">)</span><span class="w">

    </span><span class="n">data1</span><span class="w"> </span><span class="o"><-</span><span class="w">
        </span><span class="n">rbind</span><span class="p">(</span><span class="n">province</span><span class="p">,</span><span class="w"> </span><span class="n">rest</span><span class="p">)</span><span class="w">
    </span><span class="n">centroids</span><span class="w"> </span><span class="o"><-</span><span class="w">
        </span><span class="n">data1</span><span class="w"> </span><span class="o">%>%</span><span class="w"> </span><span class="n">st_centroid</span><span class="p">()</span><span class="w">  </span><span class="o">%>%</span><span class="w"> </span><span class="n">st_coordinates</span><span class="p">()</span><span class="w">
    </span><span class="n">cbind</span><span class="p">(</span><span class="n">data1</span><span class="p">,</span><span class="w"> </span><span class="n">centroids</span><span class="p">)</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="c1"># make function to create plot</span><span class="w">
</span><span class="c1"># using the previous function to move the province</span><span class="w">
</span><span class="n">plot_netherlands</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">province</span><span class="p">,</span><span class="w"> </span><span class="n">movement</span><span class="p">){</span><span class="w">
    </span><span class="n">plotunit</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">move_province</span><span class="p">(</span><span class="n">provincename</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">province</span><span class="p">,</span><span class="w"> </span><span class="n">movement</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">movement</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
            </span><span class="n">ggplot</span><span class="p">()</span><span class="o">+</span><span class="w">
            </span><span class="n">geom_sf</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">fill</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">NAME_1</span><span class="p">),</span><span class="n">color</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"grey50"</span><span class="p">,</span><span class="w"> </span><span class="n">alpha</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">3</span><span class="o">/</span><span class="m">4</span><span class="p">)</span><span class="o">+</span><span class="w">
            </span><span class="n">geom_text</span><span class="p">(</span><span class="n">aes</span><span class="p">(</span><span class="n">X</span><span class="p">,</span><span class="n">Y</span><span class="p">,</span><span class="w"> </span><span class="n">label</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">NAME_1</span><span class="p">),</span><span class="w"> </span><span class="n">size</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">6</span><span class="p">)</span><span class="o">+</span><span class="w">
            </span><span class="n">lims</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">3.2</span><span class="p">,</span><span class="m">7.1</span><span class="p">),</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">50.8</span><span class="p">,</span><span class="m">55</span><span class="p">))</span><span class="o">+</span><span class="w">
            </span><span class="n">labs</span><span class="p">(</span><span class="n">x</span><span class="o">=</span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="n">caption</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"shapefiles from www.gadm.org"</span><span class="p">,</span><span class="w"> </span><span class="n">title</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Floating Friesland"</span><span class="p">)</span><span class="o">+</span><span class="w">
            </span><span class="n">dutchmasters_fill</span><span class="p">(</span><span class="s2">"little_street"</span><span class="p">)</span><span class="o">+</span><span class="w">
            </span><span class="n">theme</span><span class="p">(</span><span class="w"> </span><span class="n">legend.position</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"empty"</span><span class="p">,</span><span class="w"> </span><span class="c1"># we already labeled the provinces</span><span class="w">
                   </span><span class="n">panel.grid.major</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">element_line</span><span class="p">(</span><span class="n">colour</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"grey80"</span><span class="p">))</span><span class="w">
    </span><span class="n">print</span><span class="p">(</span><span class="n">plotunit</span><span class="p">)</span><span class="w"> </span><span class="c1"># you have to explicitly tell it to print so the image is captured</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="c1"># go over every frame and print</span><span class="w">
</span><span class="n">plot_province_over_range</span><span class="w"> </span><span class="o"><-</span><span class="w">
     </span><span class="k">function</span><span class="p">(</span><span class="n">offset_matrix</span><span class="p">,</span><span class="w"> </span><span class="n">province</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Friesland"</span><span class="p">,</span><span class="w"> </span><span class="n">debug</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">FALSE</span><span class="p">){</span><span class="w">

    </span><span class="k">if</span><span class="p">(</span><span class="nf">any</span><span class="p">(</span><span class="nf">is.na</span><span class="p">(</span><span class="n">offset_matrix</span><span class="p">))){</span><span class="n">stop</span><span class="p">(</span><span class="s2">"I cannot handle empty movements, there are NA's in movement_matrix"</span><span class="p">)}</span><span class="w">
    </span><span class="k">if</span><span class="p">(</span><span class="n">NCOL</span><span class="p">(</span><span class="n">offset_matrix</span><span class="p">)</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w"> </span><span class="n">stop</span><span class="p">(</span><span class="s2">"movement_matrix needs to have exactly 2 columns"</span><span class="p">)</span><span class="w">
    </span><span class="n">actionsframe</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">data_frame</span><span class="p">(</span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">offset_matrix</span><span class="p">[,</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">offset_matrix</span><span class="p">[,</span><span class="m">2</span><span class="p">])</span><span class="w"> </span><span class="o">%>%</span><span class="w">
        </span><span class="n">mutate</span><span class="p">(</span><span class="n">rownumber</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">row_number</span><span class="p">())</span><span class="w">
    </span><span class="n">actionsframe</span><span class="o">$</span><span class="n">name</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="n">formatC</span><span class="p">(</span><span class="n">actionsframe</span><span class="o">$</span><span class="n">rownumber</span><span class="p">,</span><span class="w"> </span><span class="n">flag</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">0</span><span class="p">,</span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">4</span><span class="p">))</span><span class="w">

    </span><span class="n">pb</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">progress_estimated</span><span class="p">(</span><span class="n">NROW</span><span class="p">(</span><span class="n">actionsframe</span><span class="p">))</span><span class="w">

    </span><span class="n">walk</span><span class="p">(</span><span class="n">actionsframe</span><span class="o">$</span><span class="n">name</span><span class="p">,</span><span class="w"> </span><span class="o">~</span><span class="p">{</span><span class="w">

        </span><span class="n">pb</span><span class="o">$</span><span class="n">tick</span><span class="p">()</span><span class="o">$</span><span class="n">print</span><span class="p">()</span><span class="w">
        </span><span class="n">vars</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">filter</span><span class="p">(</span><span class="n">actionsframe</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">.x</span><span class="p">)</span><span class="w">
        </span><span class="k">if</span><span class="p">(</span><span class="n">debug</span><span class="p">){</span><span class="w">
            </span><span class="n">message</span><span class="p">(</span><span class="s2">"using values from: "</span><span class="p">,</span><span class="n">vars</span><span class="p">)</span><span class="w">
        </span><span class="p">}</span><span class="w">

        </span><span class="n">plot_netherlands</span><span class="p">(</span><span class="n">province</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">province</span><span class="p">,</span><span class="n">movement</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="n">vars</span><span class="o">$</span><span class="n">x</span><span class="p">[[</span><span class="m">1</span><span class="p">]],</span><span class="w"> </span><span class="n">vars</span><span class="o">$</span><span class="n">y</span><span class="p">[[</span><span class="m">1</span><span class="p">]]))</span><span class="w">

        </span><span class="p">})</span><span class="w"> </span><span class="c1"># ends the walk action</span><span class="w">
</span><span class="p">}</span><span class="w">
</span>

The plotting and saving

Nothing happened before the next step (except loading data). All the action and calculation happens here.

<span class="c1">## then the creation starts with the movement</span><span class="w">
</span><span class="n">Friesland_moves</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">rbind</span><span class="p">(</span><span class="w">
    </span><span class="n">matrix</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">-.1</span><span class="p">,</span><span class="m">-.2</span><span class="p">,</span><span class="m">-.2</span><span class="p">,</span><span class="m">-.3</span><span class="p">),</span><span class="w"> </span><span class="nf">c</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">.03</span><span class="p">,</span><span class="m">.05</span><span class="p">,</span><span class="m">.1</span><span class="p">,</span><span class="m">.15</span><span class="p">))</span><span class="w"> </span><span class="p">,</span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">),</span><span class="w">
    </span><span class="n">matrix</span><span class="p">(</span><span class="nf">c</span><span class="p">(</span><span class="n">seq</span><span class="p">(</span><span class="n">from</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">-.3</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">-.1</span><span class="p">,</span><span class="w"> </span><span class="n">length.out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">14</span><span class="p">),</span><span class="n">seq</span><span class="p">(</span><span class="n">from</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">.2</span><span class="p">,</span><span class="w"> </span><span class="n">by</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">.1</span><span class="p">,</span><span class="w"> </span><span class="n">length.out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">14</span><span class="p">)),</span><span class="w"> </span><span class="n">ncol</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2</span><span class="p">)</span><span class="w">
</span><span class="p">)</span><span class="w">
</span><span class="c1"># set up print location</span><span class="w">
</span><span class="n">frames</span><span class="w"> </span><span class="o"><-</span><span class="w"> </span><span class="n">image_graph</span><span class="p">(</span><span class="n">width</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1500</span><span class="p">,</span><span class="w"> </span><span class="n">height</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">2500</span><span class="p">,</span><span class="w"> </span><span class="n">res</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">300</span><span class="p">,</span><span class="w"> </span><span class="n">pointsize</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">5</span><span class="p">)</span><span class="w">


</span><span class="n">plot_province_over_range</span><span class="p">(</span><span class="n">offset_matrix</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Friesland_moves</span><span class="p">,</span><span class="w"> </span><span class="n">province</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"Friesland"</span><span class="p">)</span><span class="w">

</span><span class="c1"># animate</span><span class="w">
</span><span class="n">image_animate</span><span class="p">(</span><span class="n">frames</span><span class="p">,</span><span class="w"> </span><span class="m">1</span><span class="p">)</span><span class="w"> </span><span class="o">%>%</span><span class="w">
    </span><span class="n">image_write</span><span class="p">(</span><span class="n">path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"friesland.gif"</span><span class="p">)</span><span class="w">
</span>

Notes

I tried to edit this post on github on my mobile phone, boy, that does not work at all!

Moving parts of a country over a map was originally published by at Clean Code on January 29, 2018.

To leave a comment for the author, please follow the link and comment on their blog: Clean Code.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)