<p><div><div><div><div data-width="100" data-show-faces="false" data-send="true" data-layout="button_count" data-height="21" data-href="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/"></div></div><div><a data-url="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/" data-counturl="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/" data-count="horizontal" data-text="Animate maps with mapmate: R package for map- and globe-based still image sequences" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
(This article was first published on <strong><a href="https://blog.snap.uaf.edu/2016/10/10/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/"> rbloggers – SNAP Tech</a></strong>, and kindly contributed to <a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers)</a> </div>
<h1>Introduction to mapmate</h1>
<p><code>mapmate</code> (map animate) is an R package for map animation. It is used to generate and save a sequence of plots to disk as a still image sequence intended for later use in data animation production.</p>
<p>The <code>mapmate</code> package is used for map- and globe-based data animation pre-production. Specifically, <code>mapmate</code> functions are used to generate and save to disk a series of map graphics that make up a still image sequence, which can then be used in video editing and rendering software of the user’s choice. This package does not make simple animations directly within R, which can be done with packages like <code>animation</code>. <code>mapmate</code> is more specific to maps, hence the name, and particularly suited to 3D globe plots of the Earth.</p>
<p>This introduction covers the following toy examples. Generate a sequence of still frames of:</p>
<ul>
<li>map data for use in a flat map animation.</li>
<li>dynamic/temporally changing map data projected onto a static globe (3D Earth)</li>
<li>static map data projected onto rotating globe</li>
<li>dynamic map data projected onto rotating globe</li>
</ul>
<p><span></span></p>
<p>Functionality and fine-grain user control of inputs and outputs are limited in the current package version. Other features and functionality will be added in future package versions. While this is a very early developmental stage package, it can also be considered sufficiently complete for its purposes. The primary purpose I had for throwing this package together was to provide a relatively small and simple reproducible example of still image sequence generation similar to those I have used in data animations I’ve shared previously (see bottom of post for examples). The common request I receive is of course for R code.</p>
<p>While I prefer a world in which all code is shared freely, there are cases where this can be challenging. Some of the R code I write that involves heavy duty data processing is written in the context of particular computing environments, ones which I know almost no one is working in. It’s not the most helpful to share non-reproducible code that no one can use, or code that is written for an environment in which I have access to hundreds of CPUs and terabytes of RAM, if someone wants to duplicate my work on their laptop. That’s why at times I have only been able to provide rough code templates to give people an idea of what I am coding. This package aims to provide some extremely basic but nonetheless explicit, reproducible examples.</p>
<p>Ultimately, there is no free lunch. But I have done my best here to put together some code that will work in the usual computing environments, which is not parallelized, and which will not be unacceptably slow to process on the small example datasets provided in the package.</p>
<h2>Generate a still image sequence</h2>
<p>Let’s begin with some highly spatially aggregated map data in a data frame with columns <code>lon</code>, <code>lat</code>, and <code>z</code>.</p>
<pre>
devtools::install_github(&quot;leonawicz/mapmate&quot;)
library(mapmate)
library(dplyr)
library(purrr)
data(annualtemps)
annualtemps
</pre>
<p>The <code>save_map</code> function below saves png files to disk as a still image sequence. Set your working directory or be explicit about your file destination. Make sure to obtain the range of data across not only space (a single map) but time (all maps). This is to ensure a constant color to value mapping with your chosen color palette across all maps in the set of png files. For simplicity, set the number of still frames to be equal to the number of unique time points in the data frame, in this one frame for each year.</p>
<p>Since each point in this data frame represents a coarse grid cell, we use <code>type=&quot;maptiles&quot;</code> in the call to <code>save_map</code>. <code>save_map</code> is used for tiles, polygon lines (<code>type=&quot;maplines&quot;</code>) and great circle arcs (<code>type=&quot;network&quot;</code>, not covered in this post). One of these three types must be specified and the data must be appropriate for the type.</p>
<p>Tiles are the simplest, but while we are working with data frames, the data typically would originate from a rasterized object, hence the nice even spacing of the coordinates. Last of all, but important, the data frame must contain a <code>frameID</code> column. Here we will just add a <code>frameID</code> column based on the unique years in the data frame. For these static examples, arguments pertaining specifically to image sequences can be ignored for now (<code>n.period</code>, <code>n.frames</code>, <code>z.range</code>) and since we are not saving files to disk we can also ignore arguments like <code>dir</code> and <code>suffix</code>, but we will return to all these immediately after.</p>
<h3>Initial static 2D and 3D map examples</h3>
<pre>
library(RColorBrewer)
pal &lt;- rev(brewer.pal(11, &quot;RdYlBu&quot;))
temps &lt;- mutate(annualtemps, frameID = Year - min(Year) + 1)
frame1 &lt;- filter(temps, frameID == 1)  # subset to first frame

save_map(frame1, ortho = FALSE, col = pal, type = &quot;maptiles&quot;, save.plot = FALSE, 
    return.plot = TRUE)
save_map(frame1, col = pal, type = &quot;maptiles&quot;, save.plot = FALSE, return.plot = TRUE)
</pre>
<p><a target="_blank" href="https://i1.wp.com/uasnap.files.wordpress.com/2016/10/unnamed-chunk-4-1.png?ssl=1"><img alt="unnamed-chunk-4-1" class="wp-image-1357" src="https://i1.wp.com/uasnap.files.wordpress.com/2016/10/unnamed-chunk-4-1.png?fit=450%2C600&ssl=1" data-recalc-dims="1"></a><br>
<a target="_blank" href="https://i2.wp.com/uasnap.files.wordpress.com/2016/10/unnamed-chunk-4-2.png?ssl=1"><img alt="unnamed-chunk-4-2" class="wp-image-1358" src="https://i2.wp.com/uasnap.files.wordpress.com/2016/10/unnamed-chunk-4-2.png?fit=450%2C600&ssl=1" data-recalc-dims="1"></a></p>
<h3>Flat map sequence</h3>
<h4>Dynamic data, static map</h4>
<p><code>save_map</code> is not typically used in the manner above. The above examples are just to show static plot output, but there is no convenience in using <code>mapmate</code> for that when we can just use <code>ggplot2</code> or other plotting packages directly. Below are some examples of how <code>save_map</code> is usually called as a convenient still image sequence generator.</p>
<p>Notice the importance of passing the known full range of the data. The optional filename <code>suffix</code> argument is also used. For a flat map we still do not need to pay attention to the globe-specific arguments (<code>lon</code>, <code>lat</code>, <code>rotation.axis</code>) and those dealing with displaying data while globe rotation occurs across the image sequence can also be ignored (<code>n.period</code>).</p>
<pre>
rng &lt;- range(annualtemps$z, na.rm=TRUE)
n &lt;- length(unique(annualtemps$Year))
suffix &lt;- &quot;annual_2D&quot;
temps &lt;- split(temps, temps$frameID)
walk(temps, ~save_map(.x, ortho=FALSE, col=pal, type=&quot;maptiles&quot;, suffix=suffix, z.range=rng))

</pre>
<h3>Globe sequence</h3>
<h4>Dynamic data, static map</h4>
<p>For plotting data on the globe, set <code>ortho=TRUE</code> (default). In this example the globe remains in a fixed position and orientation. The default when passing a single, scalar longitude value to the <code>lon</code> argument is to use it as a starting point for rotation. Therefore, we need to pass our own vector of longitudes defining a custom longitude sequence to override this behavior. Since the globe is to remain in a fixed view, set <code>lon=rep(-70, n)</code>. Note that a scalar <code>lat</code> argument does not behave as a starting point for rotation in the perpendicular direction so no explicitly repeating vector is needed.</p>
<p>Also pay attention to <code>n.period</code>, which defines the number of frames or plot iterations required to complete one globe rotation (the degree increment). <code>n.period</code> can be any amount when providing a scalar <code>lon</code> value. The longitude sequence will propagate to a length of <code>n.period</code>. However, if providing a vector representing a custom path sequence of longitudes to <code>lon</code>, there is no assumption that the sequence is meant to be cyclical (do your own repetition if necessary).</p>
<p>It could just be a single pass, custom path to change the globe view. In this case, <code>n.period</code> must be equal to the length of the <code>lon</code> and <code>lat</code> vectors or an error will be thrown. This is more a conceptual restriction than anything. With a custom path defined by <code>lon</code> and <code>lat</code> vectors, which may not be at all cyclical, it only makes sense to define the period as the length of the sequence itself.</p>
<pre>
suffix &lt;- &quot;annual_3D_fixed&quot;
walk(temps, ~save_map(.x, lon = rep(-70, n), lat = 50, n.period = n, n.frames = n, 
    col = pal, type = &quot;maptiles&quot;, suffix = suffix, z.range = rng))

</pre>
<h4>Static data, dynamic map</h4>
<p>Now let the globe rotate, drawing static data on the surface. In this example the nation borders are drawn repeatedly while allowing the Earth to spin through the still image sequence from frame to frame. <code>walk</code> from the <code>purrr</code> package is used to easily duplicate the <code>borders</code> data frame in a list while adding a <code>frameID</code> column that increments over the list.</p>
<p>It's quite redundant and this is worse if the dataset is large, but this is how <code>save_map</code> currently accepts the data. Fortunately, it would make little difference if the data change from frame to frame, which is the main purpose of <code>save_map</code>. Datasets which do not vary from frame to frame generally serve the role of providing a background layer to other data being plotted, as with the nation borders here and as we will see later with a bathymetric surface.</p>
<p>In the case of repeating data passed to <code>save_map</code>, it only makes sense when plotting on a globe and when using a perspective that has some kind of periodicity to it. This limits how many times the same data must be redrawn. For example, if the Earth completes one rotation in 90 frames, it does not matter that maps of time series data may be plotted over a course of thousands of frames. When layering those maps on top of a constant background layer on the rotating globe, only 90 plots of the background data need to be generated. Those 90 saved images can be looped in a video editor until they reach the end of the time series maps image sequence.</p>
<p>The plot type has changed to <code>maplines</code>. <code>z.range</code> can be ignored because it only applies to map tiles (essentially square grid polygon fill coloring) which have values associated with surface pixels. It is irrelevant for polygon outlines as well as for <code>network</code> plots of line segment data. A single color can be used for the lines so the previously used palette for tiles has been replaced.</p>
<pre>
data(borders)
borders &lt;- map(1:n, ~mutate(borders, frameID = .x))
suffix &lt;- &quot;borders_3D_rotating&quot;
walk(borders, ~save_map(.x, lon = -70, lat = 50, n.period = 30, n.frames = n, 
    col = &quot;orange&quot;, type = &quot;maplines&quot;, suffix = suffix))

</pre>
<p>Using one more example, return to the list of annual temperature anomalies data frames used previously. Those were used to show changing data on a fixed-perspective globe plot. Here we plot the first layer, repeatedly, as was just done with the <code>borders</code> dataset, allowing the Earth to rotate. Remember to update the <code>frameID</code> values. The key difference with this example of fixed data and a changing perspective is that providing <code>z.range</code> is crucial to maintaining constant color mapping.</p>
<pre>
temps1 &lt;- map(1:n, ~mutate(temps[[1]], frameID = .x))
rng1 &lt;- range(temps1[[1]]$z, na.rm = TRUE)
suffix &lt;- &quot;year1_3D_rotating&quot;
walk(temps1, ~save_map(.x, lon = -70, lat = 50, n.period = 30, n.frames = n, 
    col = pal, type = &quot;maptiles&quot;, suffix = suffix, z.range = rng1))

</pre>
<h4>Dynamic data, dynamic map</h4>
<p>Finally we have the case of changing data drawn on a map with a changing perspective. This example plots the full time series list of annual temperature anomalies data frames with globe rotation.</p>
<pre>
suffix &lt;- &quot;annual_3D_rotating&quot;
walk(temps, ~save_map(.x, lon = -70, lat = 50, n.period = 30, n.frames = n, 
    col = pal, type = &quot;maptiles&quot;, suffix = suffix, z.range = rng))

</pre>
<p>Of course, there is no reason to draw static data on a static flat map or static (e.g., non-rotating) globe because that would yield a still image sequence of a constant image. The utility of the function is in generating a series of plots where either the data changes, the perspective changes, or both.</p>
<h3>Multiple image sequences</h3>
<p>Putting it all together, we can generate three still image sequences to subsequently be layered on top of one another in an animation. Below is an example using a bathymetry surface map as the base layer, the temperature anomalies that will be the middle layer, and the nation borders which will stack on top. This also is why the saved png images have a default transparent background. There is an expectation of eventual layering.</p>
<pre>
data(bathymetry)
bath &lt;- map(1:n, ~mutate(bathymetry, frameID = .x))
rng_bath &lt;- range(bath[[1]]$z, na.rm = TRUE)
pal_bath &lt;- c(&quot;black&quot;, &quot;steelblue4&quot;)

walk(bath, ~save_map(.x, n.frames = n, col = pal_bath, type = &quot;maptiles&quot;, suffix = &quot;background&quot;, 
    z.range = rng_bath))
walk(borders, ~save_map(.x, n.frames = n, col = &quot;black&quot;, type = &quot;maplines&quot;, 
    suffix = &quot;foreground&quot;))
walk(temps, ~save_map(.x, n.frames = n, col = pal, type = &quot;maptiles&quot;, suffix = &quot;timeseries&quot;, 
    z.range = rng))

</pre>
<h3>Parallel processing</h3>
<p>For larger datasets and longer sequences, this is much faster the more it can be parallelized. <code>mapmate</code> is designed to add convenience for making relatively heavy duty animations. The emphasis is on images which will look sharp and lend themselves to smooth frame transitions. They may also do so while displaying a large amount of data. High-resolution images (even larger than 4K) can be used to allow zooming during animations without loss of quality. For all these reasons, processing large amounts of data and generating still image sequences can take a long time depending on how large, long, and complex the desired plots sequences are.</p>
<p>The toy examples given earlier are not ideal for serious production. The code is provided for simple reproduction and tutorial purposes. The <code>save_map</code> function can be used more powerfully on a Linux server with a large number of CPU cores, and enough RAM to meet the needs of your data. It uses <code>mclapply</code> from the base R <code>parallel</code> package. It will not work on Windows. The choice was made for convenience.</p>
<p>The example below is like the previous one, but using <code>mclapply</code>. It assumes you have a 32-CPU Linux server node. If you have multiple nodes, you could even go so far as to explore the <code>Rmpi</code> package to link across, say, 10 nodes to yield the power of 320 CPUs. But if you have access to that kind of computing power, you probably already know it and can explore it on your own. It is beyond the scope of this tutorial.</p>
<pre>
mclapply(bath, save_map, n.frames = n, col = pal_bath, type = &quot;maptiles&quot;, suffix = &quot;background&quot;, 
    z.range = rng_bath, mc.cores = 32)
mclapply(borders, save_map, n.frames = n, col = &quot;orange&quot;, type = &quot;maplines&quot;, 
    suffix = &quot;foreground&quot;, mc.cores = 32)
mclapply(temps, save_map, n.frames = n, col = pal, type = &quot;maptiles&quot;, suffix = &quot;timeseries&quot;, 
    z.range = rng, mc.cores = 32)

</pre>
<h1>Examples using mapmate</h1>
<h2>Historical and projected global temperature anomalies</h2>
<p><iframe height="466" width="774" allowfullscreen="true" src="https://www.youtube.com/embed/xhqEkyJDBho?version=3&rel=1&%23038;fs=1&%23038;autohide=2&%23038;showsearch=0&%23038;showinfo=1&%23038;iv_load_policy=1&%23038;wmode=transparent" type="text/html"></iframe></p>
<h2>Global UAF/SNAP Shiny Apps web traffic</h2>
<p><iframe height="466" width="774" allowfullscreen="true" src="https://www.youtube.com/embed/uQYR91qixgo?version=3&rel=1&%23038;fs=1&%23038;autohide=2&%23038;showsearch=0&%23038;showinfo=1&%23038;iv_load_policy=1&%23038;wmode=transparent" type="text/html"></iframe></p>
<h2>Flat map great circle animation example</h2>
<p><iframe height="466" width="774" allowfullscreen="true" src="https://www.youtube.com/embed/yoyIUMvIP3Q?version=3&rel=1&%23038;fs=1&%23038;autohide=2&%23038;showsearch=0&%23038;showinfo=1&%23038;iv_load_policy=1&%23038;wmode=transparent" type="text/html"></iframe></p>
<p><img alt="" src="https://i1.wp.com/pixel.wp.com/b.gif?resize=1%2C1&ssl=1" border="0" data-recalc-dims="1"></p>
<p><div><div><div><div data-width="55" data-show-faces="false" data-send="true" data-layout="box_count" data-height="62" data-href="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/"></div></div><div><a data-url="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/" data-counturl="https://www.r-bloggers.com/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/" data-count="vertical" data-text="Animate maps with mapmate: R package for map- and globe-based still image sequences" class="twitter-share-button" data-via="rbloggers" href="https://twitter.com/share"></a></div><div></div></div><div></div></div>
<div>
<div>To <strong>leave a comment</strong> for the author, please follow the link and comment on their blog: <strong><a href="https://blog.snap.uaf.edu/2016/10/10/animate-maps-with-mapmate-r-package-for-map-and-globe-based-still-image-sequences/"> rbloggers – SNAP Tech</a></strong>.</div>
<hr>
<a rel="nofollow" href="https://www.r-bloggers.com/">R-bloggers.com</a> offers <strong><a rel="nofollow" href="https://feedburner.google.com/fb/a/mailverify?uri=RBloggers">daily e-mail updates</a></strong> about <a title="The R Project for Statistical Computing" rel="nofollow" href="https://www.r-project.org/">R</a> news and <a title="R tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=tutorial">tutorials</a> on topics such as: <a title="Data science" rel="nofollow" href="https://www.r-bloggers.com/?s=data%20science">Data science</a>, <a title="Big Data" rel="nofollow" href="https://www.r-bloggers.com/?s=Big%20Data">Big Data, <a title="R jobs" rel="nofollow" href="https://www.r-users.com/">R jobs</a>, visualization (<a title="ggplot and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=ggplot2">ggplot2</a>, <a title="Boxplots using lattice and ggplot2 tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=boxplot">Boxplots</a>, <a title="Maps and gis" rel="nofollow" href="https://www.r-bloggers.com/?s=map">maps</a>, <a title="Animation in R" rel="nofollow" href="https://www.r-bloggers.com/?s=animation">animation</a>), programming (<a title="RStudio IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=RStudio">RStudio</a>, <a title="Sweave and literate programming" rel="nofollow" href="https://www.r-bloggers.com/?s=sweave">Sweave</a>, <a title="LaTeX in R" rel="nofollow" href="https://www.r-bloggers.com/?s=LaTeX">LaTeX</a>, <a title="SQL and databases" rel="nofollow" href="https://www.r-bloggers.com/?s=SQL">SQL</a>, <a title="Eclipse IDE for R" rel="nofollow" href="https://www.r-bloggers.com/?s=eclipse">Eclipse</a>, <a title="git and github, Version Control System" rel="nofollow" href="https://www.r-bloggers.com/?s=git">git</a>, <a title="Large data in R using Hadoop" rel="nofollow" href="https://www.r-bloggers.com/?s=hadoop">hadoop</a>, <a title="Web Scraping of google, facebook, yahoo, twitter and more using R" rel="nofollow" href="https://www.r-bloggers.com/?s=Web+Scraping">Web Scraping</a>) statistics (<a title="Regressions and ANOVA analysis tutorials" rel="nofollow" href="https://www.r-bloggers.com/?s=regression">regression</a>, <a title="principal component analysis tutorial" rel="nofollow" href="https://www.r-bloggers.com/?s=PCA">PCA</a>, <a title="Time series" rel="nofollow" href="https://www.r-bloggers.com/?s=time+series">time series</a>, <a title="finance trading" rel="nofollow" href="https://www.r-bloggers.com/?s=trading">trading</a>) and more...
<img height="1" alt="" width="1" src="http://feeds.feedburner.com/~r/RBloggers/~4/or2jSGwTGEk"></a></div>