forked from jessesadler/intro-to-r
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgreat-circles-sp-sf.R
223 lines (173 loc) · 6.75 KB
/
great-circles-sp-sf.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
## Great circles with sp and sf ##
# This script goes along with the blog post of the same name,
# which can be found at https://www.jessesadler.com/post/great-circles-sp-sf/
# See the Rmarkdown document for the contents of the blog post.
# Load packages
library(tidyverse)
library(sp)
library(geosphere)
library(sf)
library(rnaturalearth)
# Load the data
letters <- read_csv("data/correspondence-data-1585.csv")
locations <- read_csv("data/locations.csv")
# Load the background maps
countries_sp <- ne_countries(scale = "medium")
countries_sf <- ne_countries(scale = "medium", returnclass = "sf")
####################################
## Great circle vs Rhumb line map ##
####################################
# Create points data
la_sfg <- st_point(c(-118.2615805, 34.1168926))
amsterdam_sfg <- st_point(c(4.8979755, 52.3745403))
points_sfc <- st_sfc(la_sfg, amsterdam_sfg, crs = 4326)
points_data <- data.frame(name = c("Los Angeles", "Amsterdam"))
points_sf <- st_sf(points_data, geometry = points_sfc)
# Create lines
rhumb_line <- st_linestring(rbind(c(-118.2615805, 34.1168926), c(4.8979755, 52.3745403))) %>%
st_sfc(crs = 4326)
# Make a great circle line from LA to Amsterdam as sfc object
great_circle <- st_linestring(rbind(c(-118.2615805, 34.1168926), c(4.8979755, 52.3745403))) %>%
st_sfc(crs = 4326) %>%
st_segmentize(units::set_units(50, km))
# Combine two sfc objects
lines_sfc <- c(rhumb_line, great_circle)
# Labels and transformation to sf object
lines_data <- data.frame(name = c("Rhumb line", "Great circle"))
lines_sf <- st_sf(lines_data, geometry = lines_sfc)
# Background map
map <- ne_countries(scale = "medium", returnclass = "sf") %>%
select(sovereignt)
ggplot() +
geom_sf(data = map, fill = gray(0.7), color = gray(0.7)) +
geom_sf(data = lines_sf, aes(color = name), size = 1.5, show.legend = FALSE) +
geom_sf(data = points_sf, aes(color = name), size = 3, show.legend = FALSE) +
coord_sf(xlim = c(-120, 20), ylim = c(20, 70)) +
theme_minimal()
########################
## Preparing the data ##
########################
# Create data frame of routes and count for letters per route
routes <- letters %>%
group_by(source, destination) %>%
count() %>%
ungroup() %>%
arrange(n)
# Print routes
routes
##########################################
## SpatialLinesDataFrame with geosphere ##
##########################################
# tibble of longitude and latitude values of sources
sources_tbl <- routes %>%
left_join(locations, by = c("source" = "place")) %>%
select(lon, lat)
# tibble of longitude and latitude values of destinations
destinations_tbl <- routes %>%
left_join(locations, by = c("destination" = "place")) %>%
select(lon, lat)
# Great circles as a SpatialLines object
routes_sl <- gcIntermediate(sources_tbl, destinations_tbl,
n = 50, addStartEnd = TRUE,
sp = TRUE)
# Class of routes_sl
class(routes_sl)
# Slots in routes_sl
slotNames(routes_sl)
# CRS of routes_sl
routes_sl@proj4string
# Make bbox of countries_sp the same as routes_sl
countries_sp@bbox <- bbox(routes_sl)
# Plot map
par(mar = c(1, 1, 3, 1))
plot(countries_sp, col = gray(0.8), border = gray(0.7),
main = "SpatialLines great circles")
plot(routes_sl, col = "dodgerblue", add = TRUE)
# Great circles as a SpatialLinesDataFrame object
routes_sldf <- SpatialLinesDataFrame(routes_sl, data = routes)
# Map with SpatialLinesDataFrame object
par(mar = c(1, 1, 3, 1))
plot(countries_sp, col = gray(0.8), border = gray(0.7),
main = "SpatialLinesDataFrame great circles")
plot(routes_sldf,
lwd = sqrt(routes_sldf$n/3) + 0.25,
col = "dodgerblue",
add = TRUE)
########################################
## Great circles with sf: tidy method ##
########################################
# Add id column to routes
routes_id <- rowid_to_column(routes, var = "id")
# Transform routes to long format
routes_long <- routes_id %>%
gather(key = "type", value = "place", source, destination)
# Add coordinate values
routes_long_geo <- left_join(routes_long, locations, by = "place")
# Convert coordinate data to sf object
routes_long_sf <- st_as_sf(routes_long_geo, coords = c("lon", "lat"), crs = 4326)
# Convert POINT geometry to MULTIPOINT, then LINESTRING
routes_lines <- routes_long_sf %>%
group_by(id) %>%
summarise(do_union = FALSE) %>%
st_cast("LINESTRING")
# Print routes_lines
routes_lines
# Join sf object with attributes data
routes_lines <- left_join(routes_lines, routes_id, by = "id")
# Convert rhumb lines to great circles
routes_sf_tidy <- routes_lines %>%
st_segmentize(units::set_units(20, km))
# Compare number of points in routes_lines and routes_sf_tidy
nrow(st_coordinates(routes_lines))
nrow(st_coordinates(routes_sf_tidy))
# Rhumb lines vs great circles
ggplot() +
geom_sf(data = countries_sf, fill = gray(0.8), color = gray(0.7)) +
geom_sf(data = routes_lines, color = "magenta", show.legend = "point") +
geom_sf(data = routes_sf_tidy) +
coord_sf(xlim = c(2, 14), ylim = c(45, 54), datum = NA) +
ggtitle("Rhumb lines vs Great circles") +
theme_minimal()
# Interactive comparison of gcIntermediate and st_segmentize
library(mapview)
mapview(routes_sldf, color = "magenta") +
mapview(routes_sf_tidy, color = "black")
############################################
## Great circles with sf: for loop method ##
############################################
# Create a line between Venice and Haarlem
st_linestring(rbind(c(12.315515, 45.44085), c(4.646219, 52.38739)))
# Matrix of longitude and latitude values of sources
sources_m <- routes %>%
left_join(locations, by = c("source" = "place")) %>%
select(lon:lat) %>%
as.matrix()
# Matrix of longitude and latitude values of destinations
destinations_m <- routes %>%
left_join(locations, by = c("destination" = "place")) %>%
select(lon:lat) %>%
as.matrix()
# Create empty list object of length equal to number of routes
linestrings_sfg <- vector("list", nrow(routes))
# Define sequence and body of loop
for (i in 1:nrow(routes)) {
linestrings_sfg[[i]] <- st_linestring(rbind(sources_m[i, ], destinations_m[i, ]))
}
# sfc object of great circles
linestrings_sfc <- st_sfc(linestrings_sfg, crs = 4326) %>%
st_segmentize(units::set_units(20, km))
# Print linestrings_sfc
linestrings_sfc
# Create sf object from data frame and sfc geometry set
routes_sf <- st_sf(routes, geometry = linestrings_sfc)
# Show routes_sf_tidy and routes_sf are equivalent
select(routes_sf_tidy, -id) %>%
all.equal(routes_sf)
# ggplot2 of great cirlce routes
ggplot() +
geom_sf(data = countries_sf, fill = gray(0.8), color = gray(0.7)) +
geom_sf(data = routes_sf, aes( color = n)) +
scale_color_viridis_c() +
labs(color = "Letters", title = "Great circles with sf") +
coord_sf(xlim = c(2, 14), ylim = c(45, 54), datum = NA) +
theme_minimal()