Mapeando los paraderos del corredor TGA

[share]

Nota: El sitio del Datos Abiertos mencionado, aparentemente ya no existe a la fecha (2017/04/15)

Este es un ejemplo nada elaborado del uso del servicio de datos abiertos disponibles por parte del municipio de Lima Metropolitana, en el cual ponemos en un mapa los paraderos de corredor TGA (Tacna-Garcilazo-Armedáriz) en un par de formas distintas.

El servicio en cuestión usa Junar como plataforma. Existen librerías para varios lenguajes de programación, pero no para R; pero felizmente el API es basado en REST y la respuesta de las llamadas es un arreglo en JSON, de manera que es factible consumir los datos usando la librería jsonlite.

Obteniendo y procesando los datos

Vamos a usar el API del Portal de Datos Abiertos de Lima Metropolitana.

Primero: Definimos un par de funciones que podamos reusar

library(jsonlite)
# Pasamos el URL de API endpoint, y la API developer key
# por defecto sólo retorna los datos, sin metadata
get_data <- function (api_url, api_key, include_metadata=FALSE) {
    api_data <- fromJSON(paste0(api_url, "?auth_key=", api_key))
    data <- extract_dataframe(api_data)
    if (include_metadata==FALSE) {
        return(data)
    } else {
        metadata <- api_data[c("id", "title", "description", "user",
                               "tags", "created_at", "source", "link")]
        metadata["timestamp"] <- as.Date(api_data$result$fTimestamp,
                                         origin = "1970-01-01")
        return(list(metadata=metadata, data=data))
    }
}

# Hace el trabajo de convertir los datos de un formato secuencial
# a una data.frame de R
extract_dataframe <- function(api_data) {
    header <- subset(api_data$result$fArray, fHeader==TRUE)$fStr
    values <- subset(api_data$result$fArray, is.na(fHeader))$fStr
    header_len <- api_data$result$fCols
    df <- as.data.frame(
        matrix(values,
               ncol=header_len,
               byrow = TRUE),
        stringsAsFactors=FALSE)
    colnames(df) <- header
    return(df)
}

Segundo: Conseguimos los datos de la geolocalización de los paraderos del corredor TGA troncal en Lima.

# cargando las librerías necesarias
require(jsonlite, quietly=TRUE)
source("api_key.R")
# formato: api_key <- "YOUR API KEY"
api_url <- "http://api.lima.datosabiertos.pe/datastreams/invoke/PARAD-GEO-REFER-DE-LA"
troncal_fulldata <- get_data(api_url, api_key, TRUE)
troncal <- troncal_fulldata$data

Tercero: Ahora los datos de las rutas alimentadoras

api_url <- "http://api.lima.datosabiertos.pe/datastreams/invoke/PARAD-GEO-REFER-DEL-69193"
alimentadora_fulldata <- get_data(api_url, api_key, TRUE)
alimentadora <- alimentadora_fulldata$data

Cuarto: Combinamos las dos data frames y ajustamos los tipos de algunas columnas

stops <- rbind(troncal,alimentadora)
# corregir los tipos de datos de algunas columnas
stops$LONGITUD <- as.numeric(stops$LONGITUD)
stops$LATITUD <- as.numeric(stops$LATITUD)
stops$CORREDOR <- as.factor(stops$CORREDOR)
stops$TAP <- as.factor(stops$TAP)

Un mapa simple, no interactivo

Usamos el paquete ggmap para generar un mapa estático (formato PNG).

require(ggmap, quietly=TRUE)
# usamos el tipo "toner" de stamen
lima_tga <- get_map(c(lon=mean(stops$LONGITUD), lat=mean(stops$LATITUD)),
                    source="stamen", maptype="toner", zoom=12)
tga_map <- ggmap(lima_tga, extent="device")
tga_map + geom_point(data=stops, size=2,
                    aes(x=LONGITUD, y=LATITUD, col=TAP))
Paraderos corredor TGA georeferenciados

Paraderos corredor TGA georeferenciados

Un mapa interactivo

Y usando el paquete googleVis, incrustamos un mapa interactivo usando el API de Google Maps.

require(googleVis, quietly=TRUE)
rutas <- as.character(stops$TAP)
d2 <- data.frame("latlong"=paste(stops$LATITUD, stops$LONGITUD, sep=":"),
           "tip" = paste(paste0("<b>Corredor: TGA - Ruta: ",
                                rutas, "</b>"),
                          stops$NOMBRE, sep="<br/>"))
# usemos íconos marcadores de http://www.icons-land.com/
mapmarkers <- "{default: {
        normal: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/32/Map-Marker-Flag-4-Right-Azure-icon.png',
        selected: 'http://icons.iconarchive.com/icons/icons-land/vista-map-markers/32/Map-Marker-Flag-4-Right-Chartreuse-icon.png'
      }}"
tga_map2 <- gvisMap(d2, locationvar="latlong", tipvar="tip",
                    options=list(
                        showTip=TRUE,
                        enableScrollWheel=TRUE,
                        mapType='normal',
                        useMapTypeControl=TRUE,
                        width=600, height=600,
                        icons=mapmarkers))
print(tga_map2, "chart")

Problemas encontrados

Durante la exploración del API de Datos Abiertos de Lima Metropolitana, encontré algunos problemas, mayormente subsanables.

A la fecha de publicación, varios URLs generan 404, por ejemplo:

Varias colecciones llevan a páginas sin listado de datos:

En al menos un caso, no han anonimizado los datos apropiadamente, lo cual es un problema en vista de la Ley de Protección de Datos Personales1. El conjunto de datos en cuestión es la lista de operadores de transporte público. Además, hay inconsistencia de datos como DNIs con menos de 8 digitos.

Otro defecto es que a muchos recursos les falta documentación del significado, unidades, tipo de variable y rangos de valores para los campos. Falta también documentar como se ha codificado valores faltantes (missing data) en cada caso.

Finalmente, un campo importante (fTimestamp) que indica cuan “fresca” es la información en el recurso, está mal documentado, según la documentación 2 debe de contener una Unix timestamp, lo cual se expresa usualmente en segundos. Al ver uno de los ejemplos (http://es.wiki.junar.com/index.php/Lima_API#Ejemplo), encontramos que se tiene un valor para este campo de: fTimestamp = 1379683400514, y asumiendo que son segundos, esto implicaría que la fecha referida sería 45690-06-04 21:01:54 UTC (??!!). Como imagino que los responsables de este recurso no tienen una Tardis (o “time machine” equivalente ), y teniendo en cuenta de que el orden de magnitud del valor es $10^{12}$ cuando lo esperado es de un orden $10^9$, entonces, es probable de que el campo esté en milisegundos y no en segundos como está documentado. Si usamos milisegundos, obtenemos una fecha mas razonable: 2013-09-20 13:23:20 UTC

Información para reproducibilidad

Este artículo es una versión modificada de lo publicado anteriormente en los siguientes URLs:

sessionInfo()
## R version 3.1.1 (2014-07-10)
## Platform: x86_64-pc-linux-gnu (64-bit)
##
## locale:
##  [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C
##  [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8
##  [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8
##  [7] LC_PAPER=en_US.UTF-8       LC_NAME=C
##  [9] LC_ADDRESS=C               LC_TELEPHONE=C
## [11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
##
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base
##
## other attached packages:
## [1] googleVis_0.5.2 mapproj_1.2-1   maps_2.3-2      ggmap_2.3
## [5] ggplot2_1.0.0   jsonlite_0.9.8  knitr_1.6       setwidth_1.0-3
##
## loaded via a namespace (and not attached):
##  [1] colorspace_1.2-2    digest_0.6.4        evaluate_0.5.5
##  [4] formatR_0.10        grid_3.1.1          gtable_0.1.2
##  [7] httr_0.3            labeling_0.2        MASS_7.3-33
## [10] munsell_0.4.2       plyr_1.8.1          png_0.1-7
## [13] proto_0.3-10        Rcpp_0.11.2         RCurl_1.95-4.1
## [16] reshape2_1.4        RgoogleMaps_1.2.0.6 rjson_0.2.14
## [19] RJSONIO_1.2-0.2     scales_0.2.4        stringr_0.6.2
## [22] tools_3.1.1