Chapter 6 Mapping in Leaflet with raster historical layers
In this area I would like to consider some specific issues that could appear before a researcher, who needs to visualize the spatial data, using historical landscape. Some issues I would like to address to myself, when I started to examine and work on this topic raise such questions as:
- What mapping libraries we can use and what are the advantages and disadvantages in general and for historical mapping in particular?
- Ggplot2
- Plotly
- Leaflet
- Mapbox
- How to visualize historical layers at the design level? Basically, what kind of pitfalls we can expect on the micro and macro level of historical mapping visualization?
- Micro and Macro levels on the map
- Projections and errors of historical originals and historical reconstructions: - Raster historical and reconstructed maps -Vector maps
- How to visualize history at the technical level? Pitfalls of historical mapping in R and ways of their solution
- In the beginning was the Word: organizing the dataset in an optimal way for further proceeding
- String vars
- Numeric vars
- Double vars
6.1 Preparing the dataset and making a simple map
Let’s think of possible questions that are risen before a historian, who has some data, related to the space. It will be useful to visualize them as on the map.
For instance, we have a table, which contains some geographical names that can be easily identified with longitudes and latitudes.
For example, we have an example of 5 entities with Italian cities that simply can be visualized on a modern map. Let’s use the most popular library for mapping - leaflet
. It’s a simple, powerful, flexible and highly customizable library, which allows to make awesome maps.
## City long lat Province
## 1 Madrid -3.703889 40.41250 Area metropolitana
## 2 Cordoba -4.778700 37.88680 Andalusia
## 3 Segovia -4.118333 40.94806 Castilia y Leon
## 4 Santiago de Compostela -8.544444 42.87778 Galicia
## 5 Santander -3.805000 43.46278 Cantabria
The code of this very basic map is simple. First, you need to create a map and cast leaflet function. Use magritte %>%
to connect functions without multiple calling. Then, create a basemap via addTiles()
. For the next step, let’s add our points. Since we have already prepared a dataset, we will use it with an operator $
which will indicate on particular columns with geodata. We basically need two columns: lng
and lat
, but we can also add a popup for our points. Therefore, we need to use lng=spain_sample$long
, lat=spain_sample$lat
that R understand, which columns contain the information about coordinates.
install.packages("leaflet")
map <- leaflet() %>%
addTiles() %>% # Default OpenStreetMap basemap
addMarkers(lng=spain_sample$long, lat=spain_sample$lat,
popup=spain_sample$Province")
map
This is a basic basemap, but you can add any other map own via addProviderTiles()
. Let’s use something not very typical for maps, for instance a watercolor basemap, provided by Stamen.
map <- leaflet() %>%`
addProviderTiles("Stamen.Watercolor") %>% # All Stamen maps avalible in http://maps.stamen.com NOTE use "" for adding custom maps
addMarkers(lng=spain_sample$long, lat=spain_sample$lat,`
popup=spain_sample$Province")`
map
And we will get an artistic result.
6.2 Marking an application and adding a raster image
As we already know how to make very basic maps, we can now create a map with a raster layer above, without maintaining a Web Map Server, which store geographical data.
I found a map in David Rumsey Historical Map Collection
a map in David Rumsey Historical Map Collection Spain in Portugal in the 19th century (1832).
This resource has enormous number of historical maps, which can be exported as jpg or geotiff. We need jpg. I cuted this image in Photoshop and saved it as .png.
What we need first, is to define, if we wish to make the app.
For doing this, let’s create a Shiny App, via File > New File
. RStudio will offer you to create a directory, where RStudio will storage the project and where you can add resources for the app.
You will get a standard shiny layout with UI - user interface with graphic elements, and server part, where we will implement our functions. There is a huge variety of shiny extensions, but we will use just basics.
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)
We need to create our own app with our data that deploy it in the future. Basically we need only the list of libraries, UI and server, so delete the content of Shiny app example. Let’s add our other libraries we need. We will need leaflet, DT for operations with datatables and htmlwidgets for adding a JavaScript function, I will explain it further.
library(shiny)
library(leaflet)
library(DT)
library(htmlwidgets)
ui <- fluidPage(
# Application title
titlePanel("Map app"),
mainPanel()
)
server <- function(input, output) {
}
shinyApp(ui = ui, server = server)
We need to create our own app with our data that deploy it in the future. Basically we need only the list of libraries, UI and server, so delete the content of Shiny app example.
At this moment this app is empty. Let’s add the data. First of all, we need to place our map on the main panel and use mainPanel(leafletOutput("map"))
.
Then, let’s move to the server part, where we explain R, what is the ‘map’ we call.
For doing this, we define “map” via output and indicate “map” using $
. Then, the code will be the same as we created before.
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles("Stamen.Watercolor") %>%
addMarkers(lng=spain_sample$long, lat=spain_sample$lat, popup=spain_sample$Province)
})
But for adding a raster layer, we need to do a certain trick. First of all, we should create a folder indise our project www
where we will collect raster maps. In this case, our project will look so:
Then, let’s create a function, which will add a raster image to the map. It’s an additional function, created in JavaScript. We should use htmlwidgets
library and onRender function, where we will add our image and its borders. I already prepared the coordinates. Note, these coordinates represent a diagonal between the lowest left and highest right points.
htmlwidgets::onRender("
function(el, x) {
var map = this;
var imageUrl = 'map_spain.png';
var imageBounds = [[35.798941, -10.478349], [44.037202, 4.733163]];
L.imageOverlay(imageUrl, imageBounds).addTo(map);
}
")
Note, this maps is not super accurate that’s connceted with projection errors. For avoiding it, we can use Georefferencing for modern (19th century and below) maps. But it’s a separate topic.
For now let’s make more advanced popups with additional information. For this we shall use htmlwidgets
too, because we will pack the information about the point inside a html
container. For making it, we will use paste
and present the data we wish to show if we are working on html
page:
addMarkers(lng=spain_sample$long, lat=spain_sample$lat, popup =
paste('<font face="helvetica"',
'<font size="3">', '<strong>', spain_sample$Province, '</strong>', '</font>',
'<br/>','<strong>', 'City: ', '</strong>', spain_sample$City,
'</font>',' '))
For the last thing, let’s add style to our map and make it for the whole screen.
tags$style(type = "text/css", "#map {height: calc(90vh - 80px) !important;}")
In total, our application sould be like this example:
library(shiny)
library(readr)
library(DT)
library(leaflet)
#data
spain_sample <- read_delim("spain_sample.csv",
delim = ";", escape_double = FALSE, trim_ws = TRUE)
ui <- fluidPage(
#Application style.
tags$style(type = "text/css", "#map {height: calc(90vh - 80px) !important;}"),
# Application title
titlePanel("Map app"),
mainPanel(leafletOutput('map'))
)
server <- function(input, output) {
output$map <- renderLeaflet({
leaflet() %>%
addProviderTiles("Stamen.Watercolor") %>%
addMarkers(lng=spain_sample$long, lat=spain_sample$lat, popup =
paste('<font face="helvetica"',
'<font size="3">', '<strong>', spain_sample$Province, '</strong>', '</font>',
'<br/>','<strong>', 'City: ', '</strong>', spain_sample$City,
'</font>',' ')) %>%
htmlwidgets::onRender("
function(el, x) {
var map = this;
var imageUrl = 'map_spain.png';
var imageBounds = [[35.798941, -10.478349], [44.037202, 4.733163]];
L.imageOverlay(imageUrl, imageBounds).addTo(map);
}
")
})
}
# Run the application
shinyApp(ui = ui, server = server)
6.3 Conclusion
We finihed this little app and added a historical map without implementing a WMS layer.
This sentence will be printed as a footnote.↩︎