Eflores89 / inegiR

R package to interact with INEGI API's :bar_chart: :chart_with_upwards_trend:
http://enelmargen.org/inegiR/
51 stars 12 forks source link

API de Sakbé #13

Open Eflores89 opened 8 years ago

Eflores89 commented 8 years ago

INEGI publicó un nuevo API que parece interesante para trazar rutas.

Hasta ahora solo eh podido incluir el de búsqueda de id's de destinos, (ver la función inegi_destiny).

Le heche un vistazo también a la de líneas y tengo un avance:

#' Returns INEGI line ID
#'
#' Returns data.frame with id's that match coordinates. 
#'
#' @param latitud latitud vector in decimals
#' @param longitud longitud vector in decimals
#' @param scale scale of map visualization (1:scale)
#' @param keep_trying if TRUE the function will start from scale downwards by a degree of 10 until a line is reached
#' @param token API token supplied by INEGI
#' 
#' @return Data.frame
#'
#' @author Eduardo Flores 
#' 
#' @examples
#' # All id's in Monterrey, Mex.
#' \dontrun{
#' token <- "webservice_token"
#' line_ids <- inegi_lines(latitud, longitud, token = token)
#' }
#'
#' @importFrom jsonlite fromJSON
#' @importFrom XML xmlToList
#' @importFrom plyr ldply
#' @export
inegi_lines <- function(latitud, longitud, scale = 100000, keep_trying = TRUE, token){

  if(keep_trying){
  d <- NULL
   while(is.null(d$linea)){
    print(paste0("Using scale: ", scale))
     if(scale<1){d$linea <- NA # if already extended all attempts
     }else{ 
    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=IL",
                "&x=", latitud,
                "&y=", longitud, 
                "&escala=", scale, 
                "&type=xml&key=", token)

    d <- XML::xmlToList(q)
    scale <- scale/10
     }
   }
  }else{
    print(paste0("Using scale: ", scale))
    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=IL",
                "&x=", latitud,
                "&y=", longitud, 
                "&escala=", scale, 
                "&type=xml&key=", token)

    d <- XML::xmlToList(q)
  }

  if(is.null(d$linea)){stop("Nothing returned. Try with different scale or keep_trying = true")}
  if(suppressWarnings(is.na(d$linea))){stop("Nothing returned. Try with different scale or keep_trying = true")}

  df <- as.data.frame(d$linea)
  print(df)
  #geolist <- apply(X = df$geojson, 
   #                MARGIN = 1, inegiR::ext_geo)
  #df$GEO_TYPE <- ldply(geolist, "[")['TYPE']
  #df$LAT <- ldply(geolist, "[")['LAT']
  #df$LONG <- ldply(geolist, "[")['LONG']

  df
}

Y la documentación del INEGI me trae vuelto loco con el trace de rutas. Pareciera que tiene muchas opciones y muchas no están explicadas correctamente. Llevo un avance en una funcionsita así...

inegi_route <- function(from, to, route_type = 2, vehicle = 1, axis = 0, type = 1, token){
  if(type == 1){
    # linea a linea
    if(length(from)==2){}else{stop("Length of from parameter must be 2. A vector with source id and target id, in that order")}
    if(length(to)==2){}else{stop("Length of to parameter must be 2. A vector with source id and target id, in that order")}

    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR
                &id_i=1
                &source_i=", 
                from[1], 
                "&target_i=",
                from[2],
                "&id_f=", from[2],
                "&source_f=", to[1],
                "&target_f=", to[2],
                "&p=", route_type,
                "&v=", vehicle,
                "&e=", axis,
                "&type=xml
                  &key=", token)
    m <- "line to line"
  }else{
    if(type == 2){
      q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR",
                  "&dest_i=",from, 
                  "&dest_f=", to, 
                  "&p=", route_type, 
                  "&v=", vehicle, 
                  "&e=", axis,
                  "&type=xml&key=",token)
      m <- "destiny to destiny"
    }else{

      if(type == 3){

      }else{
        if(type == 4){

        }else{stop("Type of query unrecognized. Use 1 through 4.")}
      }
    }
  }
  d <- XML::xmlToList(q)
  df <- data.frame("ROUTE_STRING" = paste0("Route, ", m, 
                                           " id: ", from[1], 
                                           " to id: ", to[1]),
                   "SQL_STRING" = ifelse(is.null(d$ruta$sql), NA, as.character(d$ruta$sql)), 
                   "ROUTE_LENGTH" = ifelse(is.null(d$ruta$long_km), NA, as.numeric(d$ruta$long_km)), 
                   "ROUTE_TIME" = ifelse(is.null(d$ruta$tiempo_min), NA, as.numeric(d$ruta$tiempo_min)),
                   "ROUTE_TOLL" = ifelse(is.null(d$ruta$peaje), NA, 
                                         ifelse(as.character(d$ruta$peaje)=="t", TRUE,FALSE)),
                   "ROUTE_COST" = ifelse(is.null(d$ruta$costo_caseta), NA, as.numeric(d$ruta$costo_caseta)),
                   "ROUTE_EXCEED" = ifelse(is.null(d$ruta$eje_excedente), NA, as.numeric(d$ruta$eje_excedente))
                   )
  l <- list("METADATA" = df, 
            "GEO" = jsonlite::fromJSON(as.character(d$ruta$geojson)))
  return(l)
}

@arturocm crees que podrías ayudarme a darle carpetazo al tema? Siento que ya tengo ceguera de taller y tal vez tengas ideas nuevas. Quiero sacar una versión 2.0 con este nuevo API, te pongo como coauthor, que ya haz hecho bastante también del DENUE.

arturocm commented 8 years ago

Justamente estaba buscando algo así con el API de Google, pero tiene limite de 2500 consultas diarias y no me es suficiente. Lo veo en estos días

arturocm commented 8 years ago

Ya empecé a jugar con el código, y tengo dos observaciones/dudas iniciales:

1)los siguientes parámetros de entrada no están nada claro:

2) Al cambiar el layout de lo que hay en la funcion paste0 pude hacer funcionar el codigo (me estaba arrojando error con los \n)

from <- c(82233,82234,633281) #copie los valores del ejemplo del API
to <- c(218261,113968)

inegi_route <- function(from, to, route_type = 2, vehicle = 1, axis = 0, type = 1, token){
  if(type == 1){
    # linea a linea
    if(length(from)==2){}else{stop("Length of from parameter must be 2. A vector with source id and target id, in that order")}
    if(length(to)==2){}else{stop("Length of to parameter must be 2. A vector with source id and target id, in that order")}

    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR&id_i=1&source_i=", 
                from[1], 
                "&target_i=",
                from[2],
                "&id_f=", from[3],
                "&source_f=", to[1],
                "&target_f=", to[2],
                "&p=", route_type,
                "&v=", vehicle,
                "&e=", axis,
                "&type=xml&key=", token)
    m <- "line to line"
  }else{
    if(type == 2){
      q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR",
                  "&dest_i=",from, 
                  "&dest_f=", to, 
                  "&p=", route_type, 
                  "&v=", vehicle, 
                  "&e=", axis,
                  "&type=xml&key=",token)
      m <- "destiny to destiny"
    }else{

      if(type == 3){

      }else{
        if(type == 4){

        }else{stop("Type of query unrecognized. Use 1 through 4.")}
      }
    }
  }
  d <- XML::xmlToList(q)
  df <- data.frame("ROUTE_STRING" = paste0("Route, ", m, 
                                           " id: ", from[1], 
                                           " to id: ", to[1]),
                   "SQL_STRING" = ifelse(is.null(d$ruta$sql), NA, as.character(d$ruta$sql)), 
                   "ROUTE_LENGTH" = ifelse(is.null(d$ruta$long_km), NA, as.numeric(d$ruta$long_km)), 
                   "ROUTE_TIME" = ifelse(is.null(d$ruta$tiempo_min), NA, as.numeric(d$ruta$tiempo_min)),
                   "ROUTE_TOLL" = ifelse(is.null(d$ruta$peaje), NA, 
                                         ifelse(as.character(d$ruta$peaje)=="t", TRUE,FALSE)),
                   "ROUTE_COST" = ifelse(is.null(d$ruta$costo_caseta), NA, as.numeric(d$ruta$costo_caseta)),
                   "ROUTE_EXCEED" = ifelse(is.null(d$ruta$eje_excedente), NA, as.numeric(d$ruta$eje_excedente))
  )
  l <- list("METADATA" = df, 
            "GEO" = jsonlite::fromJSON(as.character(d$ruta$geojson)))
  return(l)
}

Continuaré con esto...

Eflores89 commented 8 years ago

Exactamente!! Traté de encontrar por todos lados, pero no se preocupan NADA por dejar claro que son los parámetros...

arturocm commented 8 years ago

Segun http://antares.inegi.org.mx/analisis/red_hidro/SIATL/ cuando deseas trazar una ruta te sale el siguiente aviso: screen shot 2016-08-17 at 1 08 57 pm

Encontré un poco más de información en el siguiente documento:

http://www.inegi.org.mx/saladeprensa/aproposito/2015/caminero0.pdf

arturocm commented 8 years ago

Ok... aquí va lo que llevo.

inegi_lines <- function(latitud, longitud, scale = 100000, keep_trying = TRUE, token){
  options(scipen = 999)
  if(keep_trying){
    d <- NULL
    while(is.null(d$linea)){
      print(paste0("Using scale: ", scale))
      if(scale<1){d$linea <- NA # if already extended all attempts
      }else{ 
        q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=IL",
                    "&x=", longitud,
                    "&y=", latitud, 
                    "&escala=", scale, 
                    "&type=xml&key=", token)
        d <- XML::xmlToList(q)
        scale <- scale/10
      }
    }
  }else{
    print(paste0("Using scale: ", scale))
    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=IL",
                "&x=", longitud,
                "&y=", latitud, 
                "&escala=", scale, 
                "&type=xml&key=", token)
    d <- XML::xmlToList(q)
  }
  if(is.null(d$linea)){stop("Nothing returned. Try with different scale or keep_trying = true")}
  if(suppressWarnings(is.na(d$linea))){stop("Nothing returned. Try with different scale or keep_trying = true")}
  df <- as.data.frame(d$linea)
  print(df)
  return(df)
}

Unica modificación que hice a la función inegi_lines() es la del formato de los números para evitar que se utilicen notaciones científicas (esto me generó problemas al tratar de correr la formula en mi computadora)

En cuanto a inegi_route() aquí empieza lo interesante:

  1. Las variables from_io y to_io deben de ser coordenadas en el formato lat/lon
  2. Se hace uso de la función inegi_lines() para obtener los valores de id, source y target de las coordenadas mencionadas en 1
  3. Hasta el momento solo hice uso de type==1
inegi_route <- function(from_io, to_io, route_type = 2, vehicle = 1, axis = 0, type = 1, token){
  #use inegi_lines() to obtain id, source and target for the routes
  from <- inegi_lines(latitud=from_io[1],longitud=from_io[2],scale=10000000,keep_trying = TRUE, token = token)
  to <- inegi_lines(latitud=to_io[1],longitud=to_io[2],scale=10000000,keep_trying = TRUE, token = token)
  if(type == 1){
    # linea a linea
    if(length(from)==5){}else{stop("Length of from parameter must be 2. A vector with source id and target id, in that order")}
    if(length(to)==5){}else{stop("Length of to parameter must be 2. A vector with source id and target id, in that order")}
    q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR&",
                "id_i=",from[1,1], #need this [1,1] to specifiy the correct value from the variable
                "&source_i=", from[1,2], 
                "&target_i=",from[1,2],
                "&id_f=", to[1,2],
                "&source_f=", to[1,2],
                "&target_f=", to[1,2],
                "&p=", route_type,
                "&v=", vehicle,
                "&e=", axis,
                "&type=xml&key=", token)
    m <- "line to line"
  }else{
    if(type == 2){
      q <- paste0("http://gaia.inegi.org.mx/sakbe/wservice?make=CR",
                  "&dest_i=",from, 
                  "&dest_f=", to, 
                  "&p=", route_type, 
                  "&v=", vehicle, 
                  "&e=", axis,
                  "&type=xml&key=",token)
      m <- "destiny to destiny"
    }else{
      # pending
      if(type == 3){
      # pending  
      }else{
        if(type == 4){
        # pending
        }else{stop("Type of query unrecognized. Use 1 through 4.")}
      }
    }
  }
  d <- XML::xmlToList(q)
  df <- data.frame("ROUTE_STRING" = paste0("Route, ", m, 
                                           " id: ", from[1,1], 
                                           " to id: ", to[1,1]),
                   "SQL_STRING" = ifelse(is.null(d$ruta$sql), NA, as.character(d$ruta$sql)), 
                   "ROUTE_LENGTH" = ifelse(is.null(d$ruta$long_km), NA, as.numeric(d$ruta$long_km)), 
                   "ROUTE_TIME" = ifelse(is.null(d$ruta$tiempo_min), NA, as.numeric(d$ruta$tiempo_min)),
                   "ROUTE_TOLL" = ifelse(is.null(d$ruta$peaje), NA, 
                                         ifelse(as.character(d$ruta$peaje)=="t", TRUE,FALSE)),
                   "ROUTE_COST" = ifelse(is.null(d$ruta$costo_caseta), NA, as.numeric(d$ruta$costo_caseta)),
                   "ROUTE_EXCEED" = ifelse(is.null(d$ruta$eje_excedente), NA, as.numeric(d$ruta$eje_excedente))
  )
  l <- list("METADATA" = df, 
            "GEO" = jsonlite::fromJSON(as.character(d$ruta$geojson)))
  return(l)
}

A manera de prueba, busque la ruta desde el TEC de Monterrey hasta el Museo de Historia del Barrio Antiguo (Monterrey)

p1=c(25.671528, -100.306492) #museo de historia mexicano (monterrey)
p2=c(25.650443,-100.289725) #tec de monterrey
p3=c(29.081782, -110.961874) #centro hermosillo

test <- inegi_route(p1,p2,token=token)
mapa <- test$GEO$coordinates

library(leaflet)
map <- leaflet() %>%
  addTiles() %>%
  setView(p1[2], p1[1], zoom = 10) %>%
  addCircleMarkers(lat = mapa[,2],
                   lng = mapa[,1],
                   radius = 3,
                   stroke = TRUE, fillOpacity = 0.5) %>%
  addProviderTiles("CartoDB.Positron") 
map

En caso de querer generar una función para "mapear" las coordenadas de distancias más largas, es necesario usar un do.call(). El siguiente ejemplo es la ruta desde el Tec de Monterrey hasta el centro de Hermosillo

p1=c(25.671528, -100.306492) #museo de historia mexicano (monterrey)
p2=c(25.650443,-100.289725) #tec de monterrey
p3=c(29.081782, -110.961874) #centro hermosillo

test <- inegi_route(p2,p3,token=token)
mapa <- do.call("rbind",test$GEO$coordinates)

library(leaflet)
map <- leaflet() %>%
  addTiles() %>%
  setView(p1[2], p1[1], zoom = 10) %>%
  addCircleMarkers(lat = mapa[,2],
                   lng = mapa[,1],
                   radius = 3,
                   stroke = TRUE, fillOpacity = 0.5) %>%
  addProviderTiles("CartoDB.Positron") 
map

Espero que vayamos en la dirección correcta.

Comentarios?

Eflores89 commented 8 years ago

O sea, como lo entiendo, la más directa es type == 1 que es coordenada a coordenada? Las demás podríamos simplemente omitir, ¿no? Por que en teoría con un "search" ya sale la coordenada...

arturocm commented 8 years ago

Tengo pendiente trabajar en el "error handling" de estas dos funciones. Y también tengo interés en el detalle de ruta

Eflores89 commented 7 years ago

duda, en qué quedó todo esto? ajaja

estaría bien acabarlo estos días que tengo vacaciones para ya publicarlo!