-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy path16-NetworkAnalysis.qmd
306 lines (225 loc) · 15.2 KB
/
16-NetworkAnalysis.qmd
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
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# Сетевой анализ {#network}
```{r setup-network, echo = FALSE, purl = FALSE, cache = FALSE, include=FALSE}
library(datasets)
knitr::opts_knit$set(global.par = TRUE)
knitr::opts_chunk$set(warning = FALSE, message = FALSE, collapse = TRUE, out.width = '100%')
```
В данной лекции рассматриваются задачи анализа географических сетей. Они охватывают как исследование структурно-топологических свойств сетей (таких как центральность), так и решение практических задач, связанных с маршрутизацией, построением зон доступностей и т.д.
## Кратчайший путь и зоны доступности
### Загрузка данных
```{r, message = FALSE, results = "hide", collapse=TRUE}
library(sf)
library(tidyverse)
library(classInt)
library(osrm) # Использование онлайн-сервиса маршрутизации OSRM
library(sfnetworks)
library(tidygraph)
# Чтение данных
db = 'data/moscow.gpkg'
roads = read_sf(db, "roads") # Дороги
poi = read_sf(db, "poi") # Точки интереса
rayons = read_sf(db, "districts") # Границы районов
stations = read_sf(db, "metro_stations") # Станции метро
water = read_sf(db, "water") # Водные объекты
# Прочитаем текущие параметры компоновки
def = par(no.readonly = TRUE)
# Уберем поля, чтобы карта занимала весь экран
par(mar = c(0,0,0,0))
# Получим ограничивающий прямоугольник слоя дорог в качестве общего охвата карты
frame = roads |>
st_bbox() |>
st_as_sfc() |>
st_geometry()
poi.food = poi |>
select(NAME, AMENITY) |>
filter(AMENITY %in% c("restaurant", "bar", "cafe", "pub", "fast_food"))
## ОБЗОР ИСХОДНЫХ ДАННЫХ -------------------------------------
# Визуализируем входные данные
plot(frame)
plot(water |> st_geometry(),
col = "lightskyblue1",
border = "lightskyblue3",
add = TRUE)
plot(roads |> st_geometry(),
col = "gray70",
add = TRUE)
plot(poi |> st_geometry(),
col = "deepskyblue4",
pch = 20,
cex = 0.2,
add = TRUE)
```
```{r}
plotBasemap = function(add = FALSE){
plot(frame, add = add)
plot(water |> st_geometry(),
col = "lightskyblue1",
border = "lightskyblue3",
add = TRUE)
plot(roads |> st_geometry(),
col = "gray70",
add = TRUE)
plot(poi.food |> st_geometry(),
col = "deepskyblue4",
pch = 20,
cex = 0.3,
add = TRUE)
plot(stations |> st_geometry(),
col = "slategray4",
pch = 20,
cex = 2,
add = TRUE)
text(stations |> st_centroid() %>% st_coordinates(),
labels = "M",
col = "white",
cex = 0.4)
}
```
### Онлайн-анализ через сервис OSRM
#### Анализ зон транспортной доступности {#transport_zones}
Зоны транспортной доступности представляют из себя зоны окружения объектов, построенные не по евклидову расстоянию, а по расстоянию или времени движения по дорожной сети. В задачах логистики и геомаркетинга зоны транспортной доступности часто называют *зонами обслуживания* (service area), поскольку используются для определения территории, которую может покрыть объект, предоставляющий некоторые услуги. Например, для пожарного депо зона 10-минутной доступности показывает территорию города, в любую точку которой пожарная машина может доехать **из** данного депо в течение 10 минут. И наоборот, для торгового центра зона 10-минутной доступности показывает территорию города, **из** любой точки которой можно добраться до ТЦ в течение 10 минут. Очевидно, что продолжительность прямого и обратного маршрута неодинакова, на нее может оказывать влияние схема движения, приоритет дорог и так далее.
Задача, которую мы решим в данном разделе, звучит так: определить все заведения питания, находящиеся в 7 минутах езды от Центрального детского магазина. Для построения зоны доступности мы будем использовать пакет [osrm](https://cran.r-project.org/web/packages/osrm/index.html), предоставляющий интерфейс **R** к онлайн-библиотеке маршрутизации [OSRM](http://project-osrm.org), работающей на основе данных OSM. Для построения зоны доступности (изохроны) нам понадобится функция `osrmIsochrone()` из данного пакета.
> Внимание: для выполнения этого раздела модуля необходимо подключение к Интернету
Поскольку данные, используемые в настоящем модуле, предварительно были конвертированы в проекцию [UTM](https://en.wikipedia.org/wiki/Universal_Transverse_Mercator_coordinate_system) и хранятся в метрах, а OSRM решает все задачи в географических координатах (широте и долготе относительно эллипсоида [WGS84](https://en.wikipedia.org/wiki/World_Geodetic_System)), нам необходимо научиться работать с проекциями данных и преобразовывать системы координат между собой.
```{r, collapse=TRUE}
## АНАЛИЗ ЗОН ТРАНСПОРТНОЙ ДОСТУПНОСТИ -------------------------------------
# Инициализируем систему координат WGS84, используемую в OSRM
WGS84 = st_crs(4326)
# Извлечем информацию о системе координат исходных точек
UTM = st_crs(poi)
# Выберем целевой объект
psel = poi |>
filter(NAME == "Центральный детский магазин" & SHOP == "toys")
# Преобразуем координаты точки в WGS84
psel.wgs = st_transform(psel, WGS84)
# Получаем 5-минутную зону транспортной доступности
# с помощью пакета osrm
service_area = osrmIsochrone(psel.wgs, breaks = 3)
# Преобразуем зону обратно в UTM для дальнейших операций
service_area_utm = st_transform(st_as_sf(service_area), UTM)
# Отбираем точки
selected_poi = poi.food[service_area_utm, ]
# Визуализируем результат
plotBasemap()
plot(service_area_utm |> st_geometry(),
col = adjustcolor("violetred3", alpha.f = 0.2),
border = "violetred3",
add = TRUE)
plot(selected_poi |> st_geometry(),
col = "violetred3",
pch = 20,
cex = 0.5,
add = TRUE)
plot(psel |> st_geometry(),
col = "violetred4",
pch = 20,
cex = 4,
add = TRUE)
```
Итак, в данном разделе мы научились строить зоны транспортной доступности в виде полигонов, ограниченных изохроной времени движения.
#### Построение маршрутов и матриц времени движения {#routes}
В этом разделе модуля пространственного анализа мы посмотрим, каким образом можно построить оптимальный маршрут между двумя точками, а также получить матрицу времени движения между точками (на примере станций метро). Для решения этих задач используем следующие функции пакета osrm:
- `osrmRoute(src, dest)` --- строит оптимальный маршрут между точками `src` и `dest`
- `osrmTable(loc)` --- строит матрицу времени движения между всеми парами точек в `loc`
Так же, как и в предыдущем разделе, нам понадобятся преобразования координат. Построим оптимальный маршрут между книжным магазином "Молодая Гвардия" на Полянке и чебуречной "Дружба" на метро Сухаревская:
```{r, collapse = TRUE}
## ПОСТРОЕНИЕ МАРШРУТОВ -------------------------------------
# Выбираем и проецируем начальную точку
origin = poi |> filter(NAME == 'Молодая Гвардия')
origin_wgs = st_transform(origin, WGS84)
# Выбираем и проецируем конечную точку
destination = poi |> filter(NAME == 'Чебуречная "Дружба"')
destination_wgs = st_transform(destination, WGS84)
# Строим маршрут
route = osrmRoute(origin_wgs,
destination_wgs,
overview = "full", # запретить генерализацию линий
returnclass = 'sf') # вернуть результат в виде объекта класса Spatial
# Преобразуем результат обратно в UTM
route.utm = st_transform(route, UTM)
# Визуализируем результат:
plotBasemap()
plot(route.utm |> st_geometry(),
lwd = 3,
col = "orange",
add = TRUE)
plot(origin |> st_geometry(),
col = "tomato3",
pch = 20,
cex = 3,
add = TRUE)
text(origin |> st_coordinates(),
labels = "O",
col = "tomato4",
cex = 0.5)
plot(destination |> st_geometry(),
col = "tomato",
pch = 20,
cex = 4,
add = TRUE)
text(destination |> st_coordinates(),
labels = "D",
col = "tomato4",
cex = 0.7)
```
## Структурный анализ
#### Подготовка данных
Пакет `sfnetworks` использует методы пакетов `tidygraph` и `igraph` для анализа сетевых данных. Если OSRM содержит только базовые функции сетевого анализа, то sfnetworks позволяет выполнять достаточно сложные теоретические расчеты на географических сетях. Рассмотрим их на примере имеющегося у нас датасета по центру Москвы.
Чтобы граф построился корректно, необходимо продублировать линии, не являющиеся односторонними, а также округлить координаты. Первая операция нужна для того чтобы разрешить проеезд по двусторонним ребрам в обе стороны. Вторая операция важна для того чтобы устранить ошибки пристыковки линий, из-за которых они могут быть не распознаны как топологически связанные.
```{r}
lines = roads |>
st_cast('LINESTRING')
twoway = lines |>
filter(is.na(ONEWAY) | ONEWAY != 'yes') |>
st_reverse() |>
bind_rows(lines)
net = twoway |>
st_geometry() |>
lapply(function(x) round(x, 0)) |>
st_sfc(crs = st_crs(roads)) |>
as_sfnetwork()
# net = as_sfnetwork(lines)
net
```
Визуализировать граф можно как посредством стандартной функции `plot`, так и с помощью функции `autoplot`, которая задействует функциональность `ggplot2`:
```{r}
plot(net)
autoplot(net)
```
Для того чтобы работать с компонентами графа (ребрами и вершинами), необходимо их *активировать*.
```{r}
net |>
activate("edges")
net |>
activate("nodes")
```
В частности, для выполнения анализа необходимо вычислить веса всех ребер графа. Обычно вес зависит от времени передвижения, но за неимением такой информации можно использовать и длину:
```{r}
net = net |>
activate("edges") |>
mutate(weight = edge_length())
```
#### Вычисление центральности
```{r}
net = net |>
activate("edges") |>
mutate(bc = centrality_edge_betweenness())
ggplot() +
geom_sf(data = st_as_sf(net, "edges"),
aes(col = bc, linewidth = bc)) +
scale_color_viridis_c() +
ggtitle("Центральность по промежуточности")
```
## Краткий обзор {#temporal_review}
Для просмотра презентации щелкните на ней один раз левой кнопкой мыши и листайте, используя кнопки на клавиатуре:
```{r, echo=FALSE}
knitr::include_url('https://tsamsonov.github.io/r-geo-course-slides/16_NetworkAnalysis.html#1', height = '390px')
```
> Презентацию можно открыть в отдельном окне или вкладке браузере. Для этого щелкните по ней правой кнопкой мыши и выберите соответствующую команду.
## Контрольные вопросы и упражнения {#qtasks_network_analysis}
### Вопросы {#questions_network_analysis}
### Упражнения {#tasks_network_analysis}
1. Используя данные из набора [`moscow.gpkg`](https://github.com/tsamsonov/r-geo-course/blob/master/data/moscow.gpkg) для текущего задания, повторите проведенный сетевой анализ на примере других точек.
| |
|------------------------------------------------------------------------|
| *Самсонов Т.Е.* **Визуализация и анализ географических данных на языке R.** М.: Географический факультет МГУ, `r lubridate::year(Sys.Date())`. DOI: [10.5281/zenodo.901911](https://doi.org/10.5281/zenodo.901911) |