2015-02-26 21 views
8

Vorrei creare una mappa interattiva che mostri le linee di trasporto pubblico di una città. Sto cercando di farlo usando Leaflet in R (ma sono aperto a alternative, suggerimenti?)Come creare un grafico interattivo dei dati GTFS in R utilizzando Leaflet?

Dati: I dati del sistema di trasporto sono in formato GTFS, organizzati in file di testo (.txt), che io letto in R come frame di dati. *

Il problema: non riesco a trovare come indicare l'id di ogni linea Poly (variabile shape_id) in modo che la trama segua effettivamente il percorso di ogni linea di transito. Invece, collega i punti in una sequenza casuale.

Ecco quello che ho provato, finora senza successo:

# Download GTFS data of the Victoria Regional Transit System 
    tf <- tempfile() 
    td <- tempdir() 
    ftp.path <- "http://www.gtfs-data-exchange.com/agency/bc-transit-victoria-regional-transit-system/latest.zip" 
    download.file(ftp.path, tf) 

# Read text file to a data frame 
    zipfile <- unzip(tf , exdir = td) 
    shape <- read.csv(zipfile[9]) 

# Create base map 
    basemap <- leaflet() %>% addTiles() 


# Add transit layer 
    basemap %>% addPolylines(lng=shape$shape_pt_lon, lat=shape$shape_pt_lat, 
          fill = FALSE, 
          layerId =shape$shape_id) 

Sarei felice di avere i vostri commenti su questo.

* So che è possibile importare questi dati in un software GIS (ad esempio QGIS) per creare uno shapefile e quindi leggere lo shapefile in R con readOGR. Robin Lovelace has shown how to do this. MA, sto cercando una soluzione R pura. ;)

ps. Kyle Walker has written a great intro to interactive maps in R using Leaflet. Sfortunatamente, non copre le poly lines nel suo tutorial.

risposta

9

Il tuo problema non è uno di metodo ma di dati: si noti che si scaricano 8 MB e che il file di linea che si tenta di caricare in Leaflet tramite lucido è di 5 MB. Come principio generale, dovresti sempre provare nuovi metodi con piccoli set di dati, prima di ridimensionarli. Questo è quello che faccio sotto per diagnosticare il problema e risolverlo.

Fase 1: Esplorare e sottoinsieme dei dati

pkgs <- c("leaflet", "shiny" # packages we'll use 
    , "maps" # to test antiquated 'maps' data type 
    , "maptools" # to convert 'maps' data type to Spatial* data 
) 
lapply(pkgs, "library", character.only = TRUE) 


class(shape) 
## [1] "data.frame" 

head(shape) 

## shape_id shape_pt_lon shape_pt_lat shape_pt_sequence 
## 1 1-39-220 -123.4194  48.49065     0 
## 2 1-39-220 -123.4195  48.49083     1 
## 3 1-39-220 -123.4195  48.49088     2 
## 4 1-39-220 -123.4196  48.49123     3 
## 5 1-39-220 -123.4197  48.49160     4 
## 6 1-39-220 -123.4196  48.49209     5 

object.size(shape)/1000000 # 5 MB!!! 

## 5.538232 bytes 

summary(shape$shape_id) 
shape$shape_id <- as.character(shape$shape_id) 
ids <- unique(shape$shape_id) 
shape_orig <- shape 
shape <- shape[shape$shape_id == ids[1],] # subset the data 

Fase 2: Convertire uno spaziale * oggetto

E 'questo come gli oggetti dal data.frame mappe?

state.map <- map("state", plot = FALSE, fill = TRUE) 
str(state.map) 

## List of 4 
## $ x : num [1:15599] -87.5 -87.5 -87.5 -87.5 -87.6 ... 
## $ y : num [1:15599] 30.4 30.4 30.4 30.3 30.3 ... 
## $ range: num [1:4] -124.7 -67 25.1 49.4 
## $ names: chr [1:63] "alabama" "arizona" "arkansas" "california" ... 
## - attr(*, "class")= chr "map" 

, è simile, in modo che possiamo usare map2Spatial* per convertirlo:

shape_map <- list(x = shape$shape_pt_lon, y = shape$shape_pt_lat) 
shape_lines <- map2SpatialLines(shape_map, IDs = ids[1]) 
plot(shape_lines) # success - this plots a single line! 

line

Fase 3: unire tutte le linee insieme

A for ciclo lo farà bene Nota usiamo solo le prime 10 linee.Utilizzare 2:length(ids) per tutte le linee:

for(i in 2:10){ 
    shape <- shape_orig[shape_orig$shape_id == ids[i],] 
    shape_map <- list(x = shape$shape_pt_lon, y = shape$shape_pt_lat) 
    shape_temp <- map2SpatialLines(shape_map, IDs = ids[i]) 
    shape_lines <- spRbind(shape_lines, shape_temp) 
} 

Fase 4: Trama

Utilizzando l'oggetto rende il codice un po 'più corto - questo sarà tracciare le prime 10 righe, in questo caso:

leaflet() %>% 
    addTiles() %>% 
    addPolylines(data = shape_lines) 

first-10

Conclusione

È necessario giocare con i dati e manipolarli prima di convertirli in un tipo di dati Spatial * per la stampa, con gli ID corretti. maptools::map2Spatial*, unique() e un intelligente ciclo for possono risolvere il problema.

+0

Funziona perfettamente Robin. Grazie. Stavo pensando al "problema dei dati" che hai menzionato, però. Perché, alla fine, quando applico la tua soluzione per mappare tutte le rotte di transito, il tuo codice carica anche in Leaflet un oggetto di 5 MB (shape_lines). –

+0

È vero, ma ai fini del test e della riproducibilità, i problemi dovrebbero essere ridotti alla loro forma più piccola/più semplice – RobinLovelace