Как декодировать закодированные полилинии из OSRM и построить геометрию маршрута?

Я использую экземпляр OSRM (OpenStreetMap Routing Machine) для оценки расстояния и времени из разных точек. Используя API, я могу получить информацию, которая мне нужна, особенно реальный маршрут в виде ломаной линии.

До сегодняшнего дня я проводил прямые линии между начальной и конечной точкой.

segments(
         lon_patient,lat_patient,lon_lieu,lat_lieu,col = transp_time,lwd = 3
         )

Теперь я хочу построить полилинии. Но он закодирован (https://github.com/Project-OSRM/osrm-backend/wiki/Server-api#response-2). Как его нарисовать?

Спасибо!


person Aman Gast    schedule 09.09.2015    source источник
comment
Вы должны спросить об этом на gis.stackexchange.   -  person    schedule 09.09.2015
comment
@Pascal не обязательно. Я думаю, Арман спрашивает, как декодировать закодированные полилинии, которые это вполне разумный запрос здесь ИМО   -  person hrbrmstr    schedule 09.09.2015
comment
@hrbrmstr ИМО, это так.   -  person    schedule 09.09.2015


Ответы (1)


Один (быстрый) способ добиться этого - загрузить polyline.js из репозитория github mapbox, затем используйте пакет V8, чтобы проделать тяжелую работу за вас:

library(V8)

ctx <- new_context()
ctx$source("polyline.js")
ctx$call("polyline.decode", "_p~iF~ps|U_ulLnnqC_mqNvxq`@")

##        [,1]     [,2]
## [1,] 38.500 -120.200
## [2,] 40.700 -120.950
## [3,] 43.252 -126.453

Он возвращает матрицу пар широты и долготы, с которой вы должны уметь работать.

Однако чистый ответ R / Rcpp в конечном итоге будет лучше.

ОБНОВЛЕНИЕ

Существует один! Это пришло из: https://gist.github.com/diegovalle/916889 (я добавил requires и объединил несколько многословных 0 заданий):

DecodeLineR <- function(encoded) {
  require(bitops)
  require(stringr)
  len = str_length(encoded)
  encoded <- strsplit(encoded, NULL)[[1]]
  index = 1
  N <- 100000
  df.index <- 1
  array = matrix(nrow = N, ncol = 2)
  lat <- dlat <- lng <- dlnt <- b <- shift <- result <- 0

  while(index <= len) {

    shift <- result <- 0

    repeat {
      b = as.integer(charToRaw(encoded[index])) - 63
      index <- index + 1
      result = bitOr(result, bitShiftL(bitAnd(b, 0x1f), shift))
      shift = shift + 5
      if(b < 0x20) break
    }
    dlat = ifelse(bitAnd(result, 1),
                 -(result - (bitShiftR(result, 1))),
                 bitShiftR(result, 1))
    lat = lat + dlat;

    shift <- result <- b <- 0

    repeat {
      b = as.integer(charToRaw(encoded[index])) - 63
      index <- index + 1
      result = bitOr(result, bitShiftL(bitAnd(b, 0x1f), shift))
      shift = shift + 5
      if(b < 0x20) break
    }
    dlng = ifelse(bitAnd(result, 1),
                  -(result - (bitShiftR(result, 1))),
                  bitShiftR(result, 1))
    lng = lng + dlng

    array[df.index,] <- c(lat = lat * 1e-05, lng = lng * 1e-5)
    df.index <- df.index + 1
  }

  ret <- data.frame(array[1:df.index - 1,])
  names(ret) <- c("lat", "lng")
  return(ret)
}

DecodeLineR("_p~iF~ps|U_ulLnnqC_mqNvxq`@")

##      lat      lng
## 1 38.500 -120.200
## 2 40.700 -120.950
## 3 43.252 -126.453

Это дает вам фрейм данных против матрицы. И это чистый R. Не уверен, какой из них будет быстрее (если скорость нужна).

ОБНОВЛЕНИЕ №2

Здесь есть еще одна чистая реализация R: http://s4rdd.blogspot.com/2012/12/google-maps-api-decoding-polylines-for.html, и он намного быстрее, чем показанный выше (тесты см. ниже).

decodeLine <- function(encoded){
  require(bitops)

  vlen <- nchar(encoded)
  vindex <- 0
  varray <- NULL
  vlat <- 0
  vlng <- 0

  while(vindex < vlen){
    vb <- NULL
    vshift <- 0
    vresult <- 0
    repeat{
      if(vindex + 1 <= vlen){
        vindex <- vindex + 1
        vb <- as.integer(charToRaw(substr(encoded, vindex, vindex))) - 63  
      }

      vresult <- bitOr(vresult, bitShiftL(bitAnd(vb, 31), vshift))
      vshift <- vshift + 5
      if(vb < 32) break
    }

    dlat <- ifelse(
      bitAnd(vresult, 1)
      , -(bitShiftR(vresult, 1)+1)
      , bitShiftR(vresult, 1)
    )
    vlat <- vlat + dlat

    vshift <- 0
    vresult <- 0
    repeat{
      if(vindex + 1 <= vlen) {
        vindex <- vindex+1
        vb <- as.integer(charToRaw(substr(encoded, vindex, vindex))) - 63        
      }

      vresult <- bitOr(vresult, bitShiftL(bitAnd(vb, 31), vshift))
      vshift <- vshift + 5
      if(vb < 32) break
    }

    dlng <- ifelse(
      bitAnd(vresult, 1)
      , -(bitShiftR(vresult, 1)+1)
      , bitShiftR(vresult, 1)
    )
    vlng <- vlng + dlng

    varray <- rbind(varray, c(vlat * 1e-5, vlng * 1e-5))
  }
  coords <- data.frame(varray)
  names(coords) <- c("lat", "lon")
  coords
}

Вот версия Rcpp / C ++ 11, любезно предоставленная https://mapzen.com/documentation/mobility/decoding/:

#include <Rcpp.h>
#include <vector>

using namespace Rcpp;

// [[Rcpp::plugins(cpp11)]]

// [[Rcpp::export]]
DataFrame decode_polyline(const std::string& encoded) {
  size_t i = 0;     // what byte are we looking at

  constexpr double kPolylinePrecision = 1E6;
  constexpr double kInvPolylinePrecision = 1.0 / kPolylinePrecision;

  auto deserialize = [&encoded, &i](const int previous) {
    int byte, shift = 0, result = 0;
    do {
      byte = static_cast<int>(encoded[i++]) - 63;
      result |= (byte & 0x1f) << shift;
      shift += 5;
    } while (byte >= 0x20);
    return previous + (result & 1 ? ~(result >> 1) : (result >> 1));
  };

  std::vector<double> lonv, latv;
  int last_lon = 0, last_lat = 0;
  while (i < encoded.length()) {
    int lat = deserialize(last_lat);
    int lon = deserialize(last_lon);

    latv.emplace_back(static_cast<float>(static_cast<double>(lat) * kInvPolylinePrecision));
    lonv.emplace_back(static_cast<float>(static_cast<double>(lon) * kInvPolylinePrecision));

    last_lon = lon;
    last_lat = lat;
  }

  return DataFrame::create(_["lon"] = lonv, _["lat"] = latv);
}

Сохраните это в polyline.cpp и просто:

Rcpp::sourceCpp("polyline.cpp")

Тогда ты можешь:

decode_polyline("_p~iF~ps|U_ulLnnqC_mqNvxq`@")
##        lon    lat
## 1 -120.200 38.500
## 2 -120.950 40.700
#3 3 -126.453 43.252

Контрольные точки

Я добавил две функции R в глобальную среду и сделал эквиваленты js и C ++ для реализаций javascript и C ++.

введите описание изображения здесь

Максимальное значение для DecodeLineR вполне «там», независимо от того, какие параметры микробенчмарка я использую. Версия decodeLine() на чистом R кажется достаточно производительной, чтобы не гарантировать наличия зависимости V8 или Rcpp / C ++ 11, но YMMV.

ЗАКЛЮЧИТЕЛЬНОЕ ОБНОВЛЕНИЕ (МАРКИРОВКИ)

Я включил функцию googleway::decode_pl() в новые тесты и использовал гораздо более длинную ломаную линию. Ниже приведен код эталонного теста, а под ним - новый график.

library(microbenchmark)
library(Rcpp)
library(inline)
library(V8)
library(googleway)
library(ggplot2)

sourceCpp("polyline.cpp")

ctx <- v8()
ctx$source("polyline.js")

source("DecodeLineR.R")
source("decodeline.R")

line_str <- "{ae{HntiQtCcDzG_I|^uc@rFgHhC{CxAiA~AaA~BkAvB}A|F_G|AgBbBkCtAwCd@sA|BoIVw@Pc@|@gBt@}@|@y@lCwBvA_B`@k@~@aBt@iBlAaE~@oEp@sDX{BP_BJaDAcEIeCe@gHo@yMUaEk@uDm@iD]mCAwBNsDXyDL}@nByIZyCt@cLr@gNB_ABoEAkFJmDTkBVeAZ_Af@gAnDwF|@gBbAoChHgUPWlAT`@B|@GbE_@dAW`Cu@vBe@tDs@xD{@`Bg@bBq@hBaAtB}@dCi@bF}@jBg@pBeAj@SNE\\C^@\\DbAZ`Ah@~C`A\\H|ALzAFLA^Gl@UdBgAjBaBZSh@Qz@MjD_@`FoAtCa@j@Ez@DxE|@xF\\nBP~@TxHvBf@Tb@\\pBvC\\^`@XxAf@fBT|BDfAIr@MfBe@rBa@rBMvBYxBg@xA_@^Ir@@NF|@l@nBfAjAj@dBV`Bb@lBbAbB~ALPhC`FV`@n@z@^VNBX?LGZa@d@eAp@qAt@Sx@Cz@G\\IZOhCcBb@c@T]jA_CrE_HfEiFz@}@p@k@|@o@`C{A`A{@rBwBx@oAbByCp@wArAoDLWxA}BhAcBjAqAlAiB~AaDr@sBp@{CD[TkC^}FZyD^oCx@gF`@qAh@kAz@yAtAgBpD_E|JoKdDuEjBcCfC{ExCqGdAgBlBuBrAyBpEkIpEsI\\]^YbAg@|GaBzKeEfBe@lCW`AQr@U|A_AtAkAhDyCpAeA|Aq@`EeCrDgBvA{@tD}C`BmAzBm@t@QvAQxBOl@Q~Ai@~BsAlCcB"

microbenchmark(
  googleway = decode_pl(line_str),
  rcpp = decode(line_str),
  js = ctx$call("polyline_decode", line_str),
  DecodeLineR = DecodeLineR(line_str),
  decodeLine = decodeLine(line_str),
  control=list(warmup=50),
  times=1000
) -> mb

mb
## Unit: microseconds
##         expr      min         lq       mean    median        uq        max neval cld
##    googleway  404.322   471.8475   817.8312   526.270   579.095 135564.973  1000 a  
##         rcpp  253.062   302.9550   363.9325   359.765   401.054    831.699  1000 a  
##           js 2793.677  3099.3390  3359.6190  3219.312  3427.636  15580.609  1000  b 
##  DecodeLineR 9366.714  9656.4140 12029.3991 10128.676 12570.216 181536.940  1000   c
##   decodeLine 9907.869 10218.0465 11413.5732 10655.949 11491.305 150588.501  1000   c

update_geom_defaults("violin", list(fill="maroon"))

autoplot(mb) +
  scale_y_log10(name="Time [microseconds]", label=scales::comma) +
  hrbrmisc::theme_hrbrmstr(grid="X")

введите описание изображения здесь

person hrbrmstr    schedule 09.09.2015
comment
Спасибо ! Я нашел js-скрипт от OSRM, но не смог заставить его работать с R. Я все равно буду помнить о решении V8 для других вопросов, это кажется интересным. - person Aman Gast; 09.09.2015
comment
Небольшое сотрудничество @hrbrmstr: для точности это 1e-6 (см. OSRM: github.com/Project-OSRM/osrm-frontend/blob/master/WebContent/) - person Aman Gast; 09.09.2015
comment
здорово. Я немного модифицирую его (было чисто вырезано / вставлено). Я также посмотрю, смогу ли я убедить @ oliver-keyes сделать версию Rcpp :-) - person hrbrmstr; 09.09.2015
comment
Я собираюсь добавить свой собственный googleway::decode_pl(). Это еще одна реализация на C ++, и, судя по моим собственным локальным тестам, она работает лучше других. - person SymbolixAU; 21.12.2016
comment
@SymbolixAU завтра добавлю в тесты - person hrbrmstr; 21.12.2016
comment
Это max удивительно. Я должен это изучить. Спасибо за сравнение - person SymbolixAU; 22.12.2016
comment
Так было во многих системах (я запускал это в нескольких системах Linux и системах MacOS). Я был в равной степени сбит с толку, особенно на огромных # для чистых решений R. - person hrbrmstr; 22.12.2016
comment
вы смотрели результат каждой функции? Rcpp дает мне координаты около lon: -0,3, lat: 5,18. Остальные дают мне координаты в Уэльсе (долг: -3,0, широта: 51,8). (может быть, в 10 раз больше?) - person SymbolixAU; 22.12.2016