11 Understanding Geographical Maps

11.1 Creating Base Maps

A base map is the foundational layer of your map with necessary contextual information. It provides context for additional layers—those with analytical information—that are added on top of the base map. Most commonly, base maps provide location references for features that do not change often like shorelines, boundaries, rivers, lakes, roads.

11.2 Core concepts and their practical implementations

11.2.1 Projection Issues

See, https://en.wikipedia.org/wiki/List_of_map_projections.

Website https://thetruesize.com/ is a nice tool for demonstrating how projection affects our perceprion of reality. On te following two screenshots you can see how the “sizes” of Russia (~17,1 mln km2) and China (c. 9,6 mln. km2) change when they change places.

11.2.2 A Digital Map: Layers of Goodness

  • Layers:
    • Analytical Layer
    • Our Data
    • Annotation/Legend
  • Social Geography
    • Political Boundaries
    • Settlements, etc.
  • Physical Geography
    • Types of surface (raster)
    • Continents / Coastal Line
    • Elevation profile (raster)
    • Rivers, Lakes, etc.
  • Base Layer: Graticule

11.3 Main Types of Data: Points, Lines, Polygons

SOURCE: There are 3 types of vector objects: points, lines or polygons. Each object type has a different structure. Image Source: Colin Williams (NEON), via: www.earthdatascience.org

  • Analytical Layers:

    • Our Data
      • Points:
        • item1, x[1], point(lat, lon)[2]; item2, x[1], point(lat, lon)[2]; item3, x[1], point(lat, lon)[2]; … itemX, x[1], point(lat, lon)[2];
      • Lines:
        • line1, x[1], {from(lat, lon)[2], to(lat, lon); from(lat, lon)[2], to(lat, lon); from(lat, lon)[2], to(lat, lon); … from(lat, lon)[2], to(lat, lon);}[2] … lineX …
      • Polygons:
        • polygon1, x, area(lat1, lon1; lat2, lon2; … latX, lonX; … lat1, lon1)[2] … polygonX …
    • Annotation/Legend

[1] where x is a categorical parameter; [2] lat/lon: decimal coordinates (not DMS)

11.4 Base maps

Base map is essentially the foundation map on which you will be adding data that you want to analyze. It is good to have the base map put together and ready for reuse.

For a detailed walkthrough for creating a base map, see https://maximromanov.github.io/rgis_univie2021/l08-gis-ii.html. Here we will use a shortened version.

11.4.1 New libraries

library(tidyverse)

library(sf)
library(rnaturalearth)
library(rnaturalearthdata)

library(ggspatial)
library(ggrepel)

11.4.1.1 Geospatial layers

First, download necessary geospatial data. world is loaded from rnaturalearth libraries, while the other three files are preprocessed and prepared by me. Download them and place them into your course folder according to the paths given below!

world <- ne_countries(scale = "medium", returnclass = "sf")
rivers_df <- readRDS("./Data/map_objects/rivers_df.rds")
aral_sea_df <- readRDS("./Data/map_objects/aral_sea_df.rds")
routes_df <- readRDS("./Data/map_objects/routes_df.rds")

and, let’s load the PUA data (still working with the old data):

PUA <- readRDS("./Data/PUA_processed/PUA_allDataTables_asList.rds")

11.4.2 Compiling the base map

With the following code we can generate our first base map. you can adjust a number of parameters to change the look of your map. The main ones are: colors and map limits.

waterColor <- "lightsteelblue2" #
roadColor  <- "grey90"
xlim <- c(-12,80); ylim <- c(10,50)

ggplot(data = world) +
  geom_sf(fill="white", color="white") +
  # routes from Althurayya
  geom_path(data = routes_df,aes(x = long, y = lat, group = group), color = roadColor, linewidth = .2) +
  # rivers and the aral sea
  geom_path(data = rivers_df,aes(x = long, y = lat, group = group), color = waterColor, linewidth = .2) +
  geom_polygon(data = aral_sea_df,aes(x = long, y = lat, group = group), color = waterColor, fill = waterColor, linewidth = .2) +
  # map limits and theme
  coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
  theme(panel.background = element_rect(fill = waterColor))

We can add a scale bar. This we can do with the library ggspatial (location parameters are as follows: tl, tr, bl and br—for top left, top right, bottom left, and bottom right).

ggplot(data = world) +
  geom_sf(fill="white", color="white") +
  # routes from Althurayya
  geom_path(data = routes_df,aes(x = long, y = lat, group = group), color = roadColor, linewidth = .2) +
  # rivers and the aral sea
  geom_path(data = rivers_df,aes(x = long, y = lat, group = group), color = waterColor, linewidth = .2) +
  geom_polygon(data = aral_sea_df,aes(x = long, y = lat, group = group), color = waterColor, fill = waterColor, linewidth = .2) +
  # annotation scale
  annotation_scale(location = "bl", width_hint = 0.25) +
  annotation_north_arrow(location = "bl", which_north = "true", pad_x = unit(0.0, "in"), pad_y = unit(0.2, "in"), style = north_arrow_fancy_orienteering) +  
  # map limits and theme
  coord_sf(xlim = xlim, ylim = ylim, expand = FALSE) +
  theme(panel.background = element_rect(fill = waterColor), axis.title.y=element_blank(), axis.title.x=element_blank())

For convenience, we can store the main layers into variables:

baseplot <- ggplot(data = world) +
  geom_sf(fill="white", color="white") +
  # routes from Althurayya
  geom_path(data = routes_df,aes(x = long, y = lat, group = group), color = roadColor, linewidth = .2) +
  # rivers and the aral sea
  geom_path(data = rivers_df,aes(x = long, y = lat, group = group), color = waterColor, size = .2) +
  geom_polygon(data = aral_sea_df,aes(x = long, y = lat, group = group), color = waterColor, fill = waterColor, size = .2) +
  # annotation scale
  annotation_scale(location = "bl", width_hint = 0.25) +
  annotation_north_arrow(location = "bl", which_north = "true", pad_x = unit(0.0, "in"), pad_y = unit(0.2, "in"), style = north_arrow_fancy_orienteering) +  
  # map limits and theme
  coord_sf(xlim = xlim, ylim = ylim, expand = FALSE)

themeParameters <- theme(panel.background = element_rect(fill = waterColor), axis.title.y=element_blank(), axis.title.x=element_blank(), panel.grid.major = element_line(color = waterColor, linetype = "dotted", linewidth = 0.5))

And then, call baseplot + themeParameters to print out the map. We can add a layer of additional data between these two layers to visualize some our our research data.

11.4.3 Adding data

We can add some of the PUA data on our base map to get the final view. But first we may want to also refocus our on al-Andalus, which can be done by changing xlim and ylim parameters.

xlimAnd=c(-10,5); ylimAnd=c(35,45)

baseplotAnd <- ggplot(data = world) +
  geom_sf(fill="white", color="white") +
  # routes from Althurayya
  geom_path(data = routes_df,aes(x = long, y = lat, group = group), color = roadColor, linewidth = .2) +
  # rivers and the aral sea
  geom_path(data = rivers_df,aes(x = long, y = lat, group = group), color = waterColor, size = .2) +
  geom_polygon(data = aral_sea_df,aes(x = long, y = lat, group = group), color = waterColor, fill = waterColor, size = .2) +
  # annotation scale
  annotation_scale(location = "bl", width_hint = 0.25) +
  annotation_north_arrow(location = "bl", which_north = "true", pad_x = unit(0.0, "in"), pad_y = unit(0.2, "in"), style = north_arrow_fancy_orienteering) +  
  # map limits and theme
  coord_sf(xlim = xlimAnd, ylim = ylimAnd, expand = FALSE)

Let’s try to map all the Andalusian locations:

cities <- PUA$lugar %>%
  filter(FLAG_alandalus == 1)

graph01 <- baseplot + 
  geom_point(data = cities, aes(x=lng, y=lat), alpha = 0.5, col="grey", size = 0.5) +
  scale_size(range=c(0.01, 2)) +
  geom_text_repel(data = cities, aes(x=lng, y=lat, label = nombre_castellano), force = 2, segment.alpha = 0) + 
  themeParameters

graph01

We can see here that PUA data has some more flaws, as some Andalusian locations are not in al-Andalus. Something that needs to be fixed (Cova & Ensar).

For now we can implement some quick fix:

  • filter our cities by coordinates;
  • focus on high frequency locations; (not using fixed data yet!)
citiesTop <- PUA$personaje_lugar %>%
  group_by(idLugar) %>%
  summarize(count = n()) %>%
  left_join(cities) %>%
  filter(lat >= ylimAnd[1] & lat <= ylimAnd[2]) %>%
  filter(lng >= xlimAnd[1] & lng <= xlimAnd[2]) %>%
  mutate(label = ifelse(count > 100, nombre_castellano, NA))

graph02 <- baseplotAnd + 
  geom_point(data = citiesTop, aes(x=lng, y=lat, size = count), alpha = 0.5, col="grey") +
  scale_size(range=c(0.01, 10)) +
  geom_text_repel(data = citiesTop, aes(x=lng, y=lat, label = label), size = 4, force = 2, segment.alpha = 0) + 
  themeParameters

graph02

You can save maps in the following manner. Like with graphs before, you may need to play around with sizing to get the best possible view for your map.

ggsave("./Classes/Class_09/PUAR_Cl09_Map01.png", plot=graph02, width = 420, height = 297, units = "mm", dpi = "retina")
ggsave("./Classes/Class_09/PUAR_Cl09_Map01.svg", plot=graph02, width = 10, height = 10, units = "in")

11.5 Reusable Base Map for al-Andalus

We can also save our base map, which we can then quickly re-load whenever we need it. In this case we need to use .RData format, rather than .RDS.

world <- ne_countries(scale = "medium", returnclass = "sf")
rivers_df <- readRDS("./data/map_objects/rivers_df.rds")
aral_sea_df <- readRDS("./data/map_objects/aral_sea_df.rds")
routes_df <- readRDS("./data/map_objects/routes_df.rds")

# focus on al-Andalus: we simply need to change xlim and ylim parameters:
xlimAnd=c(-10,5); ylimAnd=c(35,44)

base_plot_andalus <- ggplot(data = world) +
  geom_sf(fill="white", color="white") +
  # routes from Althurayya
  geom_path(data = routes_df,aes(x = long, y = lat, group = group),
            color = roadColor, linewidth = .2) +
  # rivers and the aral sea
  geom_path(data = rivers_df,aes(x = long, y = lat, group = group),
            color = waterColor, linewidth = .2) +
  geom_polygon(data = aral_sea_df,aes(x = long, y = lat, group = group),
               color = waterColor, fill = waterColor, size = .2) +
  # annotation scale
  annotation_scale(location = "bl", width_hint = 0.25) +
  annotation_north_arrow(location = "bl", which_north = "true",
                         pad_x = unit(0.0, "in"), pad_y = unit(0.2, "in"),
                         style = north_arrow_fancy_orienteering) +
  # map limits and theme
  coord_sf(xlim = xlimAnd, ylim = ylimAnd, expand = FALSE)

save(base_plot_andalus, file = "./data/basemap/base_plot_andalus.RData")


# NOW TRY THEMED
waterColor <- "lightsteelblue2" #
roadColor  <- "grey90"

themeParameters <- theme(panel.background = element_rect(fill = waterColor),
                         axis.title.y=element_blank(),
                         axis.title.x=element_blank(),
                         panel.grid.major = element_line(color = waterColor,
                                                         linetype = "dotted",
                                                         linewidth = 0.5))

base_plot_andalus_themed <- base_plot_andalus + themeParameters
save(base_plot_andalus_themed, file = "./data/basemap/base_plot_andalus_themed.RData")

A base map saved in such a way can be reloaded in the following manner:

load("./data/basemap/base_plot_andalus_themed.RData")
base_plot_andalus_themed

11.6 Raster base maps

There are other options for base maps that rely on raster data. Since practically all raster data is moderns, we do not have much use for it. Yet, if you work on maps of cities, modern data (both raster and vector) may be quite handy in order to map old entities into the modern realities of cities. You can check how to use that here: https://maximromanov.github.io/rgis_univie2021/l08-gis-ii.html.

11.7 Homework

Try to map different subsets of data (go back to the examples of previous lessons):

NB: I am not throwing you under the bus here, but rather wanting you to try a real life scenario, when, in order to do something that you want you need to find something similar and adapt it to your data.