diff --git a/09-mapping.Rmd b/09-mapping.Rmd index aa5400eb4..01e27d16d 100644 --- a/09-mapping.Rmd +++ b/09-mapping.Rmd @@ -35,7 +35,7 @@ library(leaflet) # for interactive maps library(ggplot2) # tidyverse data visualization package ``` -- You also need to read in a couple of datasets as follows for Section \@ref(spatial-ras): +- You also need to read-in a couple of datasets as follows for Section \@ref(spatial-ras): ```{r 04-spatial-operations-1-1} nz_elev = rast(system.file("raster/nz_elev.tif", package = "spDataLarge")) @@ -53,7 +53,7 @@ Map-making is no exception, hence this chapter's coverage of one package (**tmap In addition to being fun and creative, cartography also has important practical applications. A carefully crafted map can be the best way of communicating the results of your work, but poorly designed maps can leave a bad impression. -Common design issues include poor placement, size and readability of text and careless selection of colors, as outlined in the style [guide](https://files.taylorandfrancis.com/TJOM-suppmaterial-quick-guide.pdf) of the Journal of Maps. +Common design issues include poor placement, size and readability of text and careless selection of colors, as outlined in the [style guide](https://files.taylorandfrancis.com/TJOM-suppmaterial-quick-guide.pdf) of the *Journal of Maps*. Furthermore, poor map-making can hinder the communication of results [@brewer_designing_2015]: > Amateur-looking maps can undermine your audience’s ability to understand important information and weaken the presentation of a professional data investigation. @@ -61,9 +61,9 @@ Maps have been used for several thousand years for a wide variety of purposes. Historic examples include maps of buildings and land ownership in the Old Babylonian dynasty more than 3000 years ago and Ptolemy's world map in his masterpiece *Geography*\index{geography} nearly 2000 years ago [@talbert_ancient_2014]. Map-making has historically been an activity undertaken only by, or on behalf of, the elite. -This has changed with the emergence of open source mapping software such as the R package **tmap** and the 'print layout' in QGIS\index{QGIS} which enable anyone to make high-quality maps, enabling 'citizen science'. +This has changed with the emergence of open source mapping software such as the R package **tmap** and the 'print layout' in QGIS\index{QGIS}, which allow anyone to make high-quality maps, enabling 'citizen science'. Maps are also often the best way to present the findings of geocomputational research in a way that is accessible. -Map-making is therefore a critical part of geocomputation\index{geocomputation} and its emphasis not only on describing, but also *changing* the world. +Map-making is therefore a critical part of geocomputation\index{geocomputation} and its emphasis is not only on describing, but also *changing* the world. This chapter shows how to make a wide range of maps. The next section covers a range of static maps, including aesthetic considerations, facets and inset maps. @@ -79,10 +79,10 @@ Initially, static maps were the only type of maps that R could produce. Things have advanced with the release of **sp** [see @pebesma_classes_2005], and many map-making techniques, functions, and packages have been developed since then. However, despite the innovation of interactive mapping, static plotting was still the emphasis of geographic data visualization in R a decade later [@cheshire_spatial_2015]. -The generic `plot()` function is often the fastest way to create static maps from vector and raster spatial objects (see sections \@ref(basic-map) and \@ref(basic-map-raster)). +The generic `plot()` function is often the fastest way to create static maps from vector and raster spatial objects (see Sections \@ref(basic-map) and \@ref(basic-map-raster)). Sometimes, simplicity and speed are priorities, especially during the development phase of a project, and this is where `plot()` excels. The base R approach is also extensible, with `plot()` offering dozens of arguments. -Another approach is the **grid** package which allows low-level control of static maps, as illustrated in Chapter [14](https://www.stat.auckland.ac.nz/~paul/RG2e/chapter14.html) of @murrell_r_2016. +Another approach is the **grid** package which allows low-level control of static maps, as illustrated in chapter [14](https://www.stat.auckland.ac.nz/~paul/RG2e/chapter14.html) of @murrell_r_2016. This part of the book focuses on **tmap** and emphasizes the essential aesthetic and layout options. \index{tmap (package)} @@ -124,13 +124,13 @@ This is an intuitive approach to map-making: the common task of *adding* new layers is undertaken by the addition operator `+`, followed by `tm_*()`. The asterisk (\*) refers to a wide range of layer types which have self-explanatory names including: -- `tm_fill()`: shaded areas for (mutli)polygons -- `tm_borders()`: border outlines for (mutli)polygons -- `tm_polygons()`: both, shaded areas and border outlines for (mutli)polygons -- `tm_lines()`: lines for (mutli)linestrings -- `tm_symbols()`: symbols for (mutli)points, (mutli)linestrings, and (mutli)polygons +- `tm_fill()`: shaded areas for (multi)polygons +- `tm_borders()`: border outlines for (multi)polygons +- `tm_polygons()`: both, shaded areas and border outlines for (multi)polygons +- `tm_lines()`: lines for (multi)linestrings +- `tm_symbols()`: symbols for (multi)points, (multi)linestrings, and (multi)polygons - `tm_raster()`: colored cells of raster data (there is also `tm_rgb()` for rasters with three layers) -- `tm_text()`: text information for (mutli)points, (mutli)linestrings, and (mutli)polygons +- `tm_text()`: text information for (multi)points, (multi)linestrings, and (multi)polygons This layering is illustrated in the right panel of Figure \@ref(fig:tmshape), the result of adding a border *on top of* the fill layer. @@ -152,7 +152,7 @@ map_nz = tm_shape(nz) + tm_polygons() class(map_nz) ``` -`map_nz` can be plotted later, for example by adding additional layers (as shown below) or simply running `map_nz` in the console, which is equivalent to `print(map_nz)`. +`map_nz` can be plotted later, for example by adding other layers (as shown below) or simply running `map_nz` in the console, which is equivalent to `print(map_nz)`. New *shapes* can be added with `+ tm_shape(new_obj)`. In this case, `new_obj` represents a new spatial object to be plotted on top of preceding layers. @@ -188,7 +188,7 @@ map_nz3 = map_nz2 + A useful and little known feature of **tmap** is that multiple map objects can be arranged in a single 'metaplot' with `tmap_arrange()`. This is demonstrated in the code chunk below which plots `map_nz1` to `map_nz3`, resulting in Figure \@ref(fig:tmlayers). -```{r tmlayers, message=FALSE, fig.cap="Maps with additional layers added to the final map of Figure 9.1.", fig.scap="Additional layers added to the output of Figure 9.1."} +```{r tmlayers, message=FALSE, fig.cap="Maps with added layers to the final map of Figure 9.1.", fig.scap="Additional layers added to the output of Figure 9.1."} tmap_arrange(map_nz1, map_nz2, map_nz3) ``` @@ -221,7 +221,7 @@ If there is a clash between a fixed value and a column name, the column name tak ] The impact of setting these with fixed values is illustrated in Figure \@ref(fig:tmstatic). -```{r tmstatic, message=FALSE, fig.cap="The impact of changing commonly used fill and border aesthetics to fixed values.", fig.scap="The impact of changing commonly used aesthetics."} +```{r tmstatic, message=FALSE, fig.cap="Impact of changing commonly used fill and border aesthetics to fixed values.", fig.scap="The impact of changing commonly used aesthetics."} ma1 = tm_shape(nz) + tm_polygons(fill = "red") ma2 = tm_shape(nz) + tm_polygons(fill = "red", fill_alpha = 0.3) ma3 = tm_shape(nz) + tm_polygons(col = "blue") @@ -261,7 +261,7 @@ The `.free` argument is relevant only for maps with many facets to determine if ### Scales \index{tmap (package)!scales} -Scales control how the values are represented on the map and in the legend, and largely depend on the selected visual variable. +Scales control how the values are represented on the map and in the legend, and they largely depend on the selected visual variable. For example, when our visual variable is `col`, then `col.scale` controls how the colors of spatial objects are related to the provided values; and when our visual variable is `size`, then `size.scale` controls how the sizes represent the provided values. By default, the used scale is `tm_scale()`, which selects the visual settings automatically given by the input data type (factor, numeric, and integer). @@ -285,7 +285,7 @@ tm_shape(nz) + tm_polygons(fill = "Median_income", fill.scale = tm_scale(values = "BuGn")) ``` -```{r tmpal, message=FALSE, fig.cap="Illustration of settings that affect color settings. The results show (from left to right): default settings, manual breaks, n breaks, and the impact of changing the palette.", fig.scap="Illustration of settings that affect color settings.", echo=FALSE, fig.asp=0.56, warning=FALSE} +```{r tmpal, message=FALSE, fig.cap="Illustration of color settings. The results show (from left to right): default settings, manual breaks, n breaks, and the impact of changing the palette.", fig.scap="Illustration of color settings.", echo=FALSE, fig.asp=0.56, warning=FALSE} source("https://github.com/geocompx/geocompr/raw/main/code/09-tmpal.R", print.eval = TRUE) ``` @@ -319,7 +319,7 @@ In addition to manually setting `breaks`, **tmap** allows users to specify algor The default is `tm_scale_intervals(style = "pretty")`, which rounds breaks into whole numbers where possible and spaces them evenly. Other options are listed below and presented in Figure \@ref(fig:break-styles). -- `style = "equal"`: divides input values into bins of equal range and is appropriate for variables with a uniform distribution (not recommended for variables with a skewed distribution as the resulting map may end-up having little color diversity) +- `style = "equal"`: divides input values into bins of equal range and is appropriate for variables with a uniform distribution (not recommended for variables with a skewed distribution as the resulting map may end up having little color diversity) - `style = "quantile"`: ensures the same number of observations fall into each category (with the potential downside that bin ranges can vary widely) - `style = "jenks"`: identifies groups of similar values in the data and maximizes the differences between categories - `style = "log10_pretty"`: a common logarithmic (the logarithm to base 10) version of the regular pretty style used for variables with a right-skewed distribution @@ -328,17 +328,17 @@ Other options are listed below and presented in Figure \@ref(fig:break-styles). Although `style` is an argument of **tmap** functions, in fact it originates as an argument in `classInt::classIntervals()` --- see the help page of this function for details. ``` -```{r break-styles, message=FALSE, fig.cap="Illustration of different interval scales' methods set using the style argument in tmap.", fig.scap="Illustration of different binning methods using tmap.", echo=FALSE, warning=FALSE, fig.width=8} +```{r break-styles, message=FALSE, fig.cap="Different interval scale methods set using the style argument in tmap.", fig.scap="Illustration of different binning methods using tmap.", echo=FALSE, warning=FALSE, fig.width=8} source("code/09-break-styles.R", print.eval = TRUE) ``` \index{tmap (package)!continuous scale} The `tm_scale_continuous()` function presents a continuous color field and is particularly suited for continuous rasters (Figure \@ref(fig:concat), left panel). -In case of variables with skewed distribution you can also use its variants -- `tm_scale_continuous_log()` and `tm_scale_continuous_log1p()`. +In case of variables with q skewed distribution, you can also use its variants -- `tm_scale_continuous_log()` and `tm_scale_continuous_log1p()`. \index{tmap (package)!categorical scale} -Finally, `tm_scale_categorical()` was designed to represent categorical values and assures that each category receives a unique color (Figure \@ref(fig:concat), right panel). +Finally, `tm_scale_categorical()` was designed to represent categorical values and ensures that each category receives a unique color (Figure \@ref(fig:concat), right panel). -```{r concat, message=FALSE, fig.cap="Illustration of continuous and categorical scales in tmap.", echo=FALSE, fig.width=8} +```{r concat, message=FALSE, fig.cap="Continuous and categorical scales in tmap.", echo=FALSE, fig.width=8} library(tmap) library(spData) library(spDataLarge) @@ -355,12 +355,12 @@ tmap_arrange(m_cont1, m_cat1) \index{tmap (package)!palettes} Palettes define the color ranges associated with the bins and determined by the `tm_scale_*()` functions, and its `breaks` and `n` arguments described above. -It expects a vector of colors or a new color palette name, which can be find interactively with `cols4all::c4a_gui()`. +It expects a vector of colors or a new color palette name, which can be found interactively with `cols4all::c4a_gui()`. You can also add a `-` as the color palette name prefix to reverse the palette order. ```{block2 visual-vars-values, type='rmdnote'} All of the default `values` of the visual variables, such as default color palettes for different types of input variables, can be found with `tmap_options()`. -For example run `tmap_options()$values.var`. +For example, run `tmap_options()$values.var`. ``` \index{color palettes} @@ -418,7 +418,7 @@ many_palette_plotter(c(all_default_pals$div, all_default_pals$seq, all_default_p There are two important principles for consideration when working with colors: perceptibility and accessibility. Firstly, colors on maps should match our perception. This means that certain colors are viewed through our experience and also cultural lenses. -For example, green colors usually represent vegetation or lowlands and blue is connected with water or cool. +For example, green colors usually represent vegetation or lowlands, and blue is connected with water or coolness. Color palettes should also be easy to understand to effectively convey information. It should be clear which values are lower and which are higher, and colors should change gradually. Secondly, changes in colors should be accessible to the largest number of people. @@ -430,7 +430,7 @@ Therefore, it is important to use colorblind friendly palettes as often as possi After we decided on our visual variable and its properties, we should move our attention toward the related map legend style. Using the `tm_legend()` function, we may change its title, position, orientation, or even disable it. The most important argument in this function is `title`, which sets the title of the associated legend. -In general, a map legend title should provide two pieces of information: what the legend represents and what are the units of the presented variable. +In general, a map legend title should provide two pieces of information: what the legend represents and what the units are of the presented variable. The following code chunk demonstrates this functionality by providing a more attractive name than the variable name `Land_area` (note the use of `expression()` to create superscript text): ```{r 08-mapping-11} @@ -480,12 +480,12 @@ Alternatively, we may just provide a vector of two values (or two numbers betwee \index{tmap (package)!layouts} The map layout refers to the combination of all map elements into a cohesive map. -Map elements include among others the objects to be mapped, the map grid, the scale bar, the title, and margins, while the color settings covered in the previous section relate to the palette and break-points used to affect how the map looks. +Map elements include among others the objects to be mapped, the map grid, the scale bar, the title, and margins, while the color settings covered in the previous section relate to the palette and breakpoints used to affect how the map looks. Both may result in subtle changes that can have an equally large impact on the impression left by your maps. Additional map elements such as graticules \index{tmap (package)!graticules}, north arrows\index{tmap (package)!north arrows}, scale bars\index{tmap (package)!scale bars} and map titles have their own functions: `tm_graticules()`, `tm_compass()`, `tm_scalebar()`, and `tm_title()` (Figure \@ref(fig:na-sb)).^[Another additional map elements include `tm_grid()`, `tm_logo()` and `tm_credits()`.] -```{r na-sb, message=FALSE, fig.cap="Map with additional elements - a north arrow and scale bar.", out.width="65%", fig.asp=1, fig.scap="Map with a north arrow and scale bar."} +```{r na-sb, message=FALSE, fig.cap="Map with additional elements: a north arrow and scale bar.", out.width="65%", fig.asp=1, fig.scap="Map with a north arrow and scale bar."} map_nz + tm_graticules() + tm_compass(type = "8star", position = c("left", "top")) + @@ -501,7 +501,7 @@ map_nz + tm_layout(bg.color = "lightblue") map_nz + tm_layout(frame = FALSE) ``` -```{r layout1, message=FALSE, fig.cap="Layout options specified by (from left to right) scale, bg.color and frame arguments.", fig.scap="Layout options specified by the tmap arguments.", echo=FALSE, fig.asp=0.56} +```{r layout1, message=FALSE, fig.cap="Layout options specified by (from left to right) scale, bg.color, and frame arguments.", fig.scap="Layout options specified by the tmap arguments.", echo=FALSE, fig.asp=0.56} source("https://github.com/geocompx/geocompr/raw/main/code/09-layout1.R", print.eval = TRUE) ``` @@ -514,7 +514,7 @@ Here are some useful layout settings (some of which are illustrated in Figure \@ - Frame width (`frame.lwd`) and an option to allow double lines (`frame.double.line`) - Color settings controlling `color.sepia.intensity` (how *yellowy* the map looks) and `color.saturation` (a color-grayscale) -```{r layout2, message=FALSE, fig.cap="Illustration of selected layout options.", echo=FALSE, warning=FALSE, fig.width=8} +```{r layout2, message=FALSE, fig.cap="Selected layout options.", echo=FALSE, warning=FALSE, fig.width=8} source("code/09-layout2.R", print.eval = TRUE) ``` @@ -530,7 +530,7 @@ However, this risks cluttering the map because it will involve multiple overlapp Typically all individual facets in a faceted map contain the same geometry data repeated multiple times, once for each column in the attribute data (this is the default plotting method for `sf` objects, see Chapter \@ref(spatial-class)). However, facets can also represent shifting geometries such as the evolution of a point pattern over time. -This use case of faceted plot is illustrated in Figure \@ref(fig:urban-facet). +This use case of a faceted plot is illustrated in Figure \@ref(fig:urban-facet). ```{r urban-facet, message=FALSE, fig.cap="Faceted map showing the top 30 largest urban agglomerations from 1970 to 2030 based on population projections by the United Nations.", fig.scap="Faceted map showing urban agglomerations.", fig.asp=0.5} urb_1970_2030 = urban_agglomerations |> @@ -545,7 +545,7 @@ tm_shape(world) + The preceding code chunk demonstrates key features of faceted maps created using the `tm_facets_wrap()` function: -- Shapes that do not have a facet variable are repeated (the countries in `world` in this case) +- Shapes that do not have a facet variable are repeated (countries in `world` in this case) - The `by` argument which varies depending on a variable (`"year"` in this case) - The `nrow`/`ncol` setting specifying the number of rows and columns that facets should be arranged into @@ -572,7 +572,7 @@ nz_region = st_bbox(c(xmin = 1340000, xmax = 1450000, st_as_sfc() ``` -In the second step, we create a base map showing the New Zealand's Southern Alps area. +In the second step, we create a base-map showing New Zealand's Southern Alps area. This is a place where the most important message is stated. ```{r 08-mapping-17} @@ -631,14 +631,14 @@ ins_vp = viewport(width = ins_dim[1] * 0.5, height = ins_dim[2] * 0.5, Finally, we combine the two maps by creating a new, blank canvas, printing out the main map, and then placing the inset map inside of the main map viewport. -```{r insetmap1, message=FALSE, fig.cap="Inset map providing a context - location of the central part of the Southern Alps in New Zealand.", fig.scap="Inset map providing a context.", fig.width=9} +```{r insetmap1, message=FALSE, fig.cap="Inset map providing a context -- location of the central part of the Southern Alps in New Zealand.", fig.scap="Inset map providing a context.", fig.width=9} grid.newpage() print(nz_height_map, vp = main_vp) pushViewport(main_vp) print(nz_map, vp = ins_vp) ``` -Inset map can be saved to file either by using a graphic device (see Section \@ref(visual-outputs)) or the `tmap_save()` function and its arguments - `insets_tm` and `insets_vp`. +Inset maps can be saved to file either by using a graphic device (see Section \@ref(visual-outputs)) or the `tmap_save()` function and its arguments: `insets_tm` and `insets_vp`. Inset maps are also used to create one map of non-contiguous areas. Probably, the most often used example is a map of the United States, which consists of the contiguous United States, Hawaii and Alaska. @@ -673,7 +673,7 @@ print(hawaii_map, vp = grid::viewport(0.35, 0.1, width = 0.2, height = 0.1)) print(alaska_map, vp = grid::viewport(0.15, 0.15, width = 0.3, height = 0.3)) ``` -The code presented above is compact and can be used as the basis for other inset maps but the results, in Figure \@ref(fig:insetmap2), provide a poor representation of the locations and sizes of Hawaii and Alaska. +The code presented above is compact and can be used as the basis for other inset maps, but the results, in Figure \@ref(fig:insetmap2), provide a poor representation of the locations and sizes of Hawaii and Alaska. For a more in-depth approach, see the [`us-map`](https://geocompx.github.io/geocompkg/articles/us-map.html) vignette from the **geocompkg**. ## Animated maps @@ -686,7 +686,7 @@ Furthermore, the fact that each facet is physically separated on the screen or p Animated maps solve these issues. Although they depend on digital publication, this is becoming less of an issue as more and more content moves online. -Animated maps can still enhance paper reports: you can always link readers to a web-page containing an animated (or interactive) version of a printed map to help make it come alive. +Animated maps can still enhance paper reports: you can always link readers to a webpage containing an animated (or interactive) version of a printed map to help make it come alive. There are several ways to generate animations in R, including with animation packages such as **gganimate**, which builds on **ggplot2** (see Section \@ref(other-mapping-packages)). This section focuses on creating animated maps with **tmap** because its syntax will be familiar from previous sections and the flexibility of the approach. @@ -721,7 +721,7 @@ urb_anim = tm_shape(world) + tm_polygons() + The resulting `urb_anim` represents a set of separate maps for each year. The final stage is to combine them and save the result as a `.gif` file with `tmap_animation()`. -The following command creates the animation illustrated in Figure \@ref(fig:urban-animated), with a few elements missing, that we will add in during the exercises: +The following command creates the animation illustrated in Figure \@ref(fig:urban-animated), with a few elements missing, that we will add during the exercises: ```{r 08-mapping-23, eval=FALSE} tmap_animation(urb_anim, filename = "urb_anim.gif", delay = 25) @@ -755,7 +755,7 @@ Less advanced interactivity levels include pop-ups which appear when you click o More advanced levels of interactivity include the ability to tilt and rotate maps, as demonstrated in the **mapdeck** example below, and the provision of "dynamically linked" sub-plots which automatically update when the user pans and zooms [@pezanowski_senseplace3_2018]. The most important type of interactivity, however, is the display of geographic data on interactive or 'slippy' web maps. -The release of the **leaflet** package in 2015 (that uses the leaflet JavaScript library) revolutionized interactive web map creation from within R and a number of packages have built on these foundations adding new features (e.g., **leaflet.extras2**) and making the creation of web maps as simple as creating static maps (e.g., **mapview** and **tmap**). +The release of the **leaflet** package in 2015 (that uses the leaflet JavaScript library) revolutionized interactive web map creation from within R, and a number of packages have built on these foundations adding new features (e.g., **leaflet.extras2**) and making the creation of web maps as simple as creating static maps (e.g., **mapview** and **tmap**). This section illustrates each approach in the opposite order. We will explore how to make slippy maps with **tmap** (the syntax of which we have already learned), **mapview**\index{mapview (package)}, **mapdeck**\index{mapdeck (package)} and finally **leaflet**\index{leaflet (package)} (which provides low-level control over interactive maps). @@ -821,7 +821,7 @@ knitr::include_graphics("images/mapview.png") # mv@map ``` -**mapview** has a concise syntax yet is powerful. +**mapview** has a concise syntax, yet, it is powerful. By default, it has some standard GIS functionality such as mouse position information, attribute queries (via pop-ups), scale bar, and zoom-to-layer buttons. It also offers advanced controls including the ability to 'burst' datasets into multiple layers and the addition of multiple layers with `+` followed by the name of a geographic object. Additionally, it provides automatic coloring of attributes via the `zcol` argument. @@ -841,7 +841,7 @@ trails |> breweries ``` -```{r mapview2, message=FALSE, fig.cap="Using mapview at the end of a sf-based pipe expression.", echo=FALSE, warning=FALSE} +```{r mapview2, message=FALSE, fig.cap="Using mapview at the end of an sf-based pipe expression.", echo=FALSE, warning=FALSE} knitr::include_graphics("images/mapview-example.png") # knitr::include_graphics("https://user-images.githubusercontent.com/1825120/39979271-5f515256-573d-11e8-9ede-e472ca007d73.png") ``` @@ -885,7 +885,7 @@ mapdeck(style = ms, pitch = 45, location = c(0, 52), zoom = 4) |> elevation_scale = 50, colour_range = hcl.colors(6, "plasma")) ``` -```{r mapdeck, echo=FALSE, fig.cap="Map generated by mapdeck, representing road traffic casualties across the UK. Height of 1 km cells represents number of crashes.", fig.scap="Map generated by mapdeck."} +```{r mapdeck, echo=FALSE, fig.cap="Map generated by mapdeck, representing road traffic casualties across the UK. Height of 1-km cells represents number of crashes.", fig.scap="Map generated by mapdeck."} knitr::include_graphics("images/mapdeck-mini.png") ``` @@ -902,7 +902,7 @@ mapdeck(style = ms, pitch = 45, location = c(0, 52), zoom = 4) |> add_polygon(data = lnd, layer_id = "polygon_layer") ``` -Last but not least is **leaflet** which is the most mature and widely used interactive mapping package in R\index{leaflet (package)}. +Last is **leaflet** which is the most mature and widely used interactive mapping package in R\index{leaflet (package)}. **leaflet** provides a relatively low-level interface to the Leaflet JavaScript library and many of its arguments can be understood by reading the documentation of the original JavaScript library (see [leafletjs.com](https://leafletjs.com/)). Leaflet maps are created with `leaflet()`, the result of which is a `leaflet` map object which can be piped to other **leaflet** functions. @@ -944,26 +944,26 @@ if (knitr::is_latex_output() || knitr::is_html_output()){ \index{map-making!mapping applications} The interactive web maps demonstrated in Section \@ref(interactive-maps) can go far. -Careful selection of layers to display, base-maps and pop-ups can be used to communicate the main results of many projects involving geocomputation. -But the web mapping approach to interactivity has limitations: +Careful selection of layers to display, basemaps and pop-ups can be used to communicate the main results of many projects involving geocomputation. +But the web-mapping approach to interactivity has limitations: - Although the map is interactive in terms of panning, zooming and clicking, the code is static, meaning the user interface is fixed - All map content is generally static in a web map, meaning that web maps cannot scale to handle large datasets easily -- Additional layers of interactivity, such a graphs showing relationships between variables and 'dashboards' are difficult to create using the web-mapping approach +- Additional layers of interactivity, such a graphs showing relationships between variables and 'dashboards', are difficult to create using the web-mapping-approach -Overcoming these limitations involves going beyond static web mapping and towards geospatial frameworks and map servers. +Overcoming these limitations involves going beyond static web mapping and toward geospatial frameworks and map servers. Products in this field include [GeoDjango](https://docs.djangoproject.com/en/4.0/ref/contrib/gis/)\index{GeoDjango} (which extends the Django web framework and is written in [Python](https://github.com/django/django))\index{Python}, [MapServer](https://github.com/mapserver/mapserver)\index{MapServer} (a framework for developing web applications, largely written in C and C++)\index{C++} and [GeoServer](https://github.com/geoserver/geoserver) (a mature and powerful map server written in Java\index{Java}). Each of these is scalable, enabling maps to be served to thousands of people daily, assuming there is sufficient public interest in your maps! -The bad news is that such server-side solutions require much skilled developer time to set-up and maintain, often involving teams of people with roles such as a dedicated geospatial database administrator ([DBA](https://wiki.gis.com/wiki/index.php/Database_administrator)). +The bad news is that such server-side solutions require much skilled developer time to set up and maintain, often involving teams of people with roles such as a dedicated geospatial database administrator ([DBA](https://wiki.gis.com/wiki/index.php/Database_administrator)). -Fortunately for R programmers, web mapping applications can now be rapidly created with **shiny**.\index{shiny (package)} +Fortunately for R programmers, web-mapping applications can now be rapidly created with **shiny**.\index{shiny (package)} As described in the open source book [Mastering Shiny](https://mastering-shiny.org/), **shiny** is an R package and framework for converting R code into interactive web applications [@wickham_mastering_2021]. You can embed interactive maps in shiny apps thanks to functions such as [`leaflet::renderLeaflet()`](https://rstudio.github.io/leaflet/shiny.html). -This section gives some context, teaches the basics of **shiny** from a web mapping perspective and culminates in a full-screen mapping application in less than 100 lines of code. +This section gives some context, teaches the basics of **shiny** from a web-mapping perspective, and culminates in a full-screen mapping application in less than 100 lines of code. **shiny** is well documented at [shiny.posit.co](https://shiny.posit.co/), which highlights the two components of every **shiny** app: 'front end' (the bit the user sees) and 'back end' code. In **shiny** apps, these elements are typically created in objects named `ui` and `server` within an R script named `app.R`, which lives in an 'app folder'. -This allows web mapping applications to be represented in a single file, such as the [`CycleHireApp/app.R`](https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) file in the book's GitHub repo. +This allows web-mapping applications to be represented in a single file, such as the [`CycleHireApp/app.R`](https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) file in the book's GitHub repo. ```{block2 shiny, type = 'rmdnote'} In **shiny** apps these are often split into `ui.R` (short for user interface) and `server.R` files, naming conventions used by `shiny-server`, a server-side Linux application for serving shiny apps on public-facing websites. @@ -993,7 +993,7 @@ server = function(input, output) { shinyApp(ui, server) ``` -```{r lifeApp, echo=FALSE, message=FALSE, fig.cap="Screenshot showing minimal example of a web mapping application created with shiny.", fig.scap="Minimal example of a web mapping application."} +```{r lifeApp, echo=FALSE, message=FALSE, fig.cap="Screenshot showing minimal example of a web-mapping application created with shiny.", fig.scap="Minimal example of a web-mapping application."} # knitr::include_graphics("https://user-images.githubusercontent.com/1825120/39690606-8f9400c8-51d2-11e8-84d7-f4a66a477d2a.png") knitr::include_graphics("images/shiny-app.png") ``` @@ -1009,12 +1009,12 @@ The function `shinyApp()` combines both the `ui` and `server` elements and serve When you move the slider in the map shown in Figure \@ref(fig:lifeApp), you are actually causing R code to re-run, although this is hidden from view in the user interface. Building on this basic example and knowing where to find help (see `?shiny`), the best way forward now may be to stop reading and start programming! -The recommended next step is to open the previously mentioned [`CycleHireApp/app.R`](https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) script in an IDE of choice, modify it and re-run it repeatedly. +The recommended next step is to open the previously mentioned [`CycleHireApp/app.R`](https://github.com/geocompx/geocompr/blob/main/apps/CycleHireApp/app.R) script in an integrated development environment (IDE) of choice, modify it and re-run it repeatedly. The example contains some of the components of a web mapping application implemented in **shiny** and should 'shine' a light on how they behave. -The `CycleHireApp/app.R` script contains **shiny** functions that go beyond those demonstrated in the simple 'lifeApp' example. +The `CycleHireApp/app.R` script contains **shiny** functions that go beyond those demonstrated in the simple 'lifeApp' example (Figure \@ref(fig:CycleHireApp-html)). These include `reactive()` and `observe()` (for creating outputs that respond to the user interface --- see `?reactive`) and `leafletProxy()` (for modifying a `leaflet` object that has already been created). -Such elements are critical to the creation of web mapping applications implemented in **shiny**. +Such elements are critical to the creation of web-mapping applications implemented in **shiny**. A range of 'events' can be programmed including advanced functionality such as drawing new layers or subsetting data, as described in the shiny section of RStudio's **leaflet** [website](https://rstudio.github.io/leaflet/shiny.html). ```{block2 shinynote, type='rmdnote'} @@ -1024,12 +1024,12 @@ For RStudio users, the simplest way is probably to click on the 'Run App' button You can also launch apps from a Unix command line with the command `Rscript -e 'shiny::runApp("CycleHireApp")'`. ``` -Experimenting with apps such as `CycleHireApp` will build not only your knowledge of web mapping applications in R, but also your practical skills. +Experimenting with apps such as `CycleHireApp` will build not only your knowledge of web-mapping applications in R, but also your practical skills. Changing the contents of `setView()`, for example, will change the starting bounding box that the user sees when the app is initiated. Such experimentation should not be done at random, but with reference to relevant documentation, starting with `?shiny`, and motivated by a desire to solve problems such as those posed in the exercises. **shiny** used in this way can make prototyping mapping applications faster and more accessible than ever before (deploying **shiny** apps, https://shiny.posit.co/deploy/, is a separate topic beyond the scope of this chapter). -Even if your applications are eventually deployed using different technologies, **shiny** undoubtedly allows web mapping applications to be developed in relatively few lines of code (86 in the case of CycleHireApp). +Even if your applications are eventually deployed using different technologies, **shiny** undoubtedly allows web-mapping applications to be developed in relatively few lines of code (86 in the case of CycleHireApp). That does not stop shiny apps getting rather large. The Propensity to Cycle Tool (PCT) hosted at [pct.bike](https://www.pct.bike/), for example, is a national mapping tool funded by the UK's Department for Transport. The PCT is used by dozens of people each day and has multiple interactive elements based on more than 1000 lines of [code](https://github.com/npct/pct-shiny/blob/master/regions_www/m/server.R) [@lovelace_propensity_2017]. @@ -1039,7 +1039,7 @@ One potential problem with the ease of developing prototypes with **shiny** is t For that reason, despite advocating **shiny**, we recommend starting with the longer established technology of a pen and paper as the first stage for interactive mapping projects. This way your prototype web applications should be limited not by technical considerations, but by your motivations and imagination. -```{r CycleHireApp-html, echo=FALSE, message=FALSE, fig.cap="CycleHireApp, a simple web mapping application for finding the closest cycle hiring station based on your location and requirement of cycles. Interactive version available online at r.geocompx.org.",fig.scap="Cycle Hire App, a simple web mapping application.", eval=knitr::is_html_output(), out.width="690"} +```{r CycleHireApp-html, echo=FALSE, message=FALSE, fig.cap="CycleHireApp, a simple web-mapping application for finding the closest cycle hiring station based on your location and requirement of cycles. Interactive version available online at: r.geocompx.org.",fig.scap="Cycle Hire App, a simple web-mapping application.", eval=knitr::is_html_output(), out.width="690"} knitr::include_app("https://shiny.robinlovelace.net/CycleHireApp/") ``` @@ -1051,7 +1051,7 @@ knitr::include_graphics("images/09_cycle_hire_app.png") **tmap** provides a powerful interface for creating a wide range of static maps (Section \@ref(static-maps)) and also supports interactive maps (Section \@ref(interactive-maps)). But there are many other options for creating maps in R. -The aim of this section is to provide a taster of some of these and pointers for additional resources: map-making is a surprisingly active area of R package development, so there is more to learn than can be covered here. +The aim of this section is to provide a taste of some of these and pointers for additional resources: map-making is a surprisingly active area of R package development, so there is more to learn than can be covered here. The most mature option is to use `plot()` methods provided by core spatial packages **sf** and **terra**, covered in Sections \@ref(basic-map) and \@ref(basic-map-raster), respectively. What we have not mentioned in those sections was that plot methods for vector and raster objects can be combined when the results draw onto the same plot area (elements such as keys in **sf** plots and multi-band rasters will interfere with this). @@ -1138,7 +1138,7 @@ knitr::kable(map_gpkg_df, ``` Table \@ref(tab:map-gpkg) shows a range of mapping packages that are available, and there are many others not listed in this table. -Of note is **mapsf**, which can generate range of geographic visualizations including choropleth, 'proportional symbol' and 'flow' maps. +Of note is **mapsf**, which can generate a range of geographic visualizations including choropleth, 'proportional symbol' and 'flow' maps. These are documented in the [`mapsf`](https://cran.r-project.org/package=mapsf/vignettes/mapsf.html)\index{mapsf (package)} vignette. Several packages focus on specific map types, as illustrated in Table \@ref(tab:map-spkg). @@ -1161,13 +1161,13 @@ In the next paragraph, we focus solely on the **cartogram** package [@R-cartogra Therefore, we suggest to read the [geogrid](https://github.com/jbaileyh/geogrid)\index{geogrid (package)}, [geofacet](https://github.com/hafen/geofacet)\index{geofacet (package)}, [linemap](https://github.com/riatelab/linemap)\index{linemap (package)}, [tanaka](https://github.com/riatelab/tanaka)\index{tanaka (package)}, and [rayshader](https://github.com/tylermorganwall/rayshader)\index{rayshader (package)} documentations to learn more about them. A cartogram is a map in which the geometry is proportionately distorted to represent a mapping variable. -Creation of this type of map is possible in R with **cartogram**, which allows for creating continuous and non-contiguous area cartograms. +Creation of this type of map is possible in R with **cartogram**, which allows for creating contiguous and non-contiguous area cartograms. It is not a mapping package per se, but it allows for construction of distorted spatial objects that could be plotted using any generic mapping package. -The `cartogram_cont()` function creates continuous area cartograms. +The `cartogram_cont()` function creates contiguous area cartograms. It accepts an `sf` object and name of the variable (column) as inputs. -Additionally, it is possible to modify the `intermax` argument - maximum number of iterations for the cartogram transformation. -For example, we could represent median income in New Zeleand's regions as a continuous cartogram (Figure \@ref(fig:cartomap1), right panel) as follows: +Additionally, it is possible to modify the `intermax` argument -- maximum number of iterations for the cartogram transformation. +For example, we could represent median income in New Zeleand's regions as a contiguous cartogram (Figure \@ref(fig:cartomap1), right panel) as follows: ```{r 08-mapping-39, fig.show='hide', message=FALSE} library(cartogram) @@ -1175,7 +1175,7 @@ nz_carto = cartogram_cont(nz, "Median_income", itermax = 5) tm_shape(nz_carto) + tm_polygons("Median_income") ``` -```{r cartomap1, echo=FALSE, message=FALSE, fig.cap="Comparison of standard map (left) and continuous area cartogram (right).", fig.scap="Comparison of standard map and continuous area cartogram.", warning=FALSE, fig.width=9} +```{r cartomap1, echo=FALSE, message=FALSE, fig.cap="Comparison of standard map (left) and contiguous area cartogram (right).", fig.scap="Comparison of standard map and contiguous area cartogram.", warning=FALSE, fig.width=9} carto_map1 = tm_shape(nz) + tm_polygons("Median_income", fill.scale = tm_scale(values = "Greens"), @@ -1199,7 +1199,7 @@ us_states9311_ncont = cartogram_ncont(us_states9311, "total_pop_15") us_states9311_dorling = cartogram_dorling(us_states9311, "total_pop_15") ``` -```{r cartomap2, echo=FALSE, message=FALSE, fig.cap="Comparison of non-continuous area cartogram (left) and Dorling cartogram (right).", fig.scap="Comparison of cartograms.", fig.asp=0.32, warning=FALSE} +```{r cartomap2, echo=FALSE, message=FALSE, fig.cap="Comparison of non-contiguous area cartogram (left) and Dorling cartogram (right).", fig.scap="Comparison of cartograms.", fig.asp=0.32, warning=FALSE} carto_map3 = tm_shape(us_states9311_ncont) + tm_polygons("total_pop_15", fill.scale = tm_scale(values = "BuPu"), diff --git a/_09-ex.Rmd b/_09-ex.Rmd index 0722c77ef..747332d1d 100644 --- a/_09-ex.Rmd +++ b/_09-ex.Rmd @@ -54,9 +54,8 @@ library(mapsf) mf_map(x = africa, var = "HDI", type = "choro") ``` -E2. Extend the **tmap** created for the previous exercise so the legend has three bins: "High" (`HDI` above 0.7), "Medium" (`HDI` between 0.55 and 0.7) and "Low" (`HDI` below 0.55). - -- Bonus: improve the map aesthetics, for example by changing the legend title, class labels and color palette. +E2. Extend the **tmap** created for the previous exercise so the legend has three bins: "High" (`HDI` above 0.7), "Medium" (`HDI` between 0.55 and 0.7) and "Low" (`HDI` below 0.55). +Bonus: improve the map aesthetics, for example by changing the legend title, class labels and color palette. ```{r} library(tmap) @@ -86,11 +85,11 @@ ahdi = tm_shape(africa) + tmap_arrange(ahdi, asubregions) ``` -E4. Create a land cover map of the Zion National Park. +E4. Create a land cover map of Zion National Park. - Change the default colors to match your perception of the land cover categories - Add a scale bar and north arrow and change the position of both to improve the map's aesthetic appeal -- Bonus: Add an inset map of Zion National Park's location in the context of the Utah state. (Hint: an object representing Utah can be subset from the `us_states` dataset.) +- Bonus: Add an inset map of Zion National Park's location in the context of the state of Utah. (Hint: an object representing Utah can be subset from the `us_states` dataset.) ```{r} tm_shape(nlcd) + @@ -213,7 +212,7 @@ leaflet(africa4326) |> addScaleBar() ``` -E8. Sketch on paper ideas for a web mapping app that could be used to make transport or land-use policies more evidence based: +E8. Sketch on paper ideas for a web-mapping application that could be used to make transport or land-use policies more evidence-based: - In the city you live, for a couple of users per day - In the country you live, for dozens of users per day