将GPS点插值到R

时间:2019-05-31 18:19:45

标签: r gps interpolation geospatial

我有两个数据集:一个(通常是每30秒收集一次)带有时间戳的GPS点(来自公交车位置),以及另一个属于GPS轨迹的中间点(来自公交车站)。

# GPS Points
gps_points <- structure(list(id_gps = c(4138176L, 4136334L, 4134534L, 4132685L, 
4130891L, 4129035L, 4127232L, 4125387L, 4123620L, 4121861L, 4120114L, 
4118381L, 4116721L, 3380373L, 3374532L, 3369036L, 3363258L, 3357540L, 
3351543L, 3345549L, 3339777L, 3333210L, 3326793L, 3319251L, 3312822L, 
3306501L), hora = structure(c(1535953786, 1535953816, 1535953846, 
1535953876, 1535953906, 1535953936, 1535953966, 1535953996, 1535954026, 
1535954056, 1535954086, 1535954116, 1535954146, 1535954176, 1535954206, 
1535954236, 1535954266, 1535954296, 1535954326, 1535954356, 1535954386, 
1535954416, 1535954446, 1535954476, 1535954506, 1535954536), class = c("POSIXct", 
"POSIXt"), tzone = "UTC"), lon = c(-38.500763, -38.501413, -38.50252, 
-38.503505, -38.504694, -38.505441, -38.506651, -38.507328, -38.507965, 
-38.509063, -38.509735, -38.51022, -38.511546, -38.511778, -38.512788, 
-38.513633, -38.514568, -38.51495, -38.515331, -38.515878, -38.516438, 
-38.516628, -38.517129, -38.517651, -38.518056, -38.518358), 
    lat = c(-3.80892, -3.807633, -3.805113, -3.802854, -3.800343, 
    -3.79881, -3.796178, -3.79474, -3.793426, -3.791048, -3.789561, 
    -3.78856, -3.78569, -3.785216, -3.783108, -3.781245, -3.778751, 
    -3.777118, -3.775673, -3.773774, -3.771845, -3.771159, -3.769336, 
    -3.767198, -3.765478, -3.764019)), row.names = c(NA, -26L
), class = "data.frame", .Names = c("id_gps", "hora", "lon", 
"lat"))


# Stops
stops <- structure(list(stop_id = c(4873, 3215, 5083, 3346, 3363, 3362, 
3542, 3543, 3540, 4629, 3528), lon = c(-38.516766, -38.515311, 
-38.513903, -38.512154, -38.511001, -38.509844, -38.508943, -38.50816, 
-38.507062, -38.505798, -38.504044), lat = c(-3.771828, -3.77695, 
-3.781432, -3.785157, -3.787631, -3.790069, -3.791997, -3.793663, 
-3.796027, -3.798711, -3.802504)), class = c("tbl_df", "tbl", 
"data.frame"), row.names = c(NA, -11L), .Names = c("stop_id", 
"lon", "lat"))

head(gps_points)
 id_gps                hora       lon       lat
1 4138176 2018-09-03 05:49:46 -38.50076 -3.808920
2 4136334 2018-09-03 05:50:16 -38.50141 -3.807633
3 4134534 2018-09-03 05:50:46 -38.50252 -3.805113
4 4132685 2018-09-03 05:51:16 -38.50350 -3.802854
5 4130891 2018-09-03 05:51:46 -38.50469 -3.800343
6 4129035 2018-09-03 05:52:16 -38.50544 -3.798810

head(stops)
stop_id       lon       lat
1    4873 -38.51677 -3.771828
2    3215 -38.51531 -3.776950
3    5083 -38.51390 -3.781432
4    3346 -38.51215 -3.785157
5    3363 -38.51100 -3.787631
6    3362 -38.50984 -3.790069

GPS指向红色,停在蓝色

我想(通过GPS数据集上的线性插值)估计与每个停靠点相关的时间戳(使用R。)。所需的输出将是带有新列的停靠点数据集,用于标识插值时间戳记。

我现在正在解决的方法包括每5秒对GPS点进行插值一次(使用this method),然后在每个停靠点(使用RANN::nn2)计算最接近的GPS发生点。 GPS数据集非常大,因此在计算上是不可行的,而且我仍然没有获得与每个停靠点相关的“确切”时间戳。

# workaround
# Crete combination of timestamps for each 5 seconds
full.time   <- with(gps_points,seq(gps_points$hora[1],tail(gps_points$hora,1),by=5))

library(zoo)
# convert to zoo object
df.zoo <- zoo(gps_points[,c("lon", "lat")],gps_points$hora)

# interpolate; result is also a zoo object
result <- na.approx(df.zoo,xout=full.time)

# transform zoo to df
zoo.to.data.frame <- function(x, index.name="hora") {
  stopifnot(is.zoo(x))
  xn <- if(is.null(dim(x))) deparse(substitute(x)) else colnames(x)
  setNames(data.frame(index(x), x, row.names=NULL), c(index.name,xn))
}

gps_points_interpolated <- zoo.to.data.frame(result) %>% as_tibble()

# Create temp_id for stops
stops <- stops %>%
  mutate(temp_id = 1:n())

# To each GPS point, what's the closest stop?
opa <- RANN::nn2(select(stops, lon, lat), select(gps_points_interpolated, lon, lat), 1)

vamos <- gps_points_interpolated %>%
  mutate(temp_id = opa$nn.idx, dist = opa$nn.dists*111320)

# Bring back stop_id, lon e lat of each stop
vamos <- left_join(vamos, stops, by = "temp_id", suffix = c(".gps", ".stop")) %>%
# Select columns
  select(stop_id, lon = lon.stop, lat = lat.stop, hora, dist)

# Select the observations that have minimun distance to each stop
vamos_fim <- vamos %>%
  group_by(stop_id) %>%
  slice(which.min(dist))

head(vamos_fim)
# A tibble: 6 x 5
# Groups:   stop_id [6]
  stop_id   lon   lat hora                dist[,1]
    <dbl> <dbl> <dbl> <dttm>                 <dbl>
1    3215 -38.5 -3.78 2018-09-03 05:58:21     34.1
2    3346 -38.5 -3.79 2018-09-03 05:56:21     39.9
3    3362 -38.5 -3.79 2018-09-03 05:54:36     37.1
4    3363 -38.5 -3.79 2018-09-03 05:55:26     37.9
5    3528 -38.5 -3.80 2018-09-03 05:51:21     38.7
6    3540 -38.5 -3.80 2018-09-03 05:52:51     34.6

也欢迎使用除线性插值以外的其他方法。谢谢。

1 个答案:

答案 0 :(得分:0)

我设法使用zoo::na.approx函数自己解决了这个问题。首先,您需要计算两个数据集中从同一点开始的连续点之间的累积距离。在我的示例中,我的GPS点按顺序排在第一位,因此我将选择第一个GPS点作为两个数据集的“起点”。

# I just realized that the datasets are growing in opposite direction, so I'll flip the stops
stops <- map_df(stops, rev)


# Function to calculate distance from previous point

get.dist <- function(lon, lat) geosphere::distHaversine(tail(cbind(lon,lat),-1),head(cbind(lon,lat),-1))

# Calculate cumulative distance of gps points (points must be ordered by time)
gps_points <- gps_points %>%
  mutate(dist = c(0, cumsum(get.dist(lon, lat))))

# Input first GPS point and calculate cumulative distance of stops (also must be ordered)
stops <- gps_points %>%
  # Select only the first point  
  slice(1) %>%
  # Select columns to match the stops dataset
  mutate(stop_id = NA) %>%
  select(stop_id, lon, lat) %>%
  # Input the stop points
  rbind(stops) %>%
  # Calculate cumulative dist
  mutate(dist = c(0, cumsum(get.dist(lon, lat))))


# Interpolate ------------------------------
x <- gps_points$hora
y <- gps_points$dist

# to which position we want to interpolate? to the stops!
xout <- stops$dist

interp <- as.POSIXct(zoo::na.approx(x , 
                                    y, 
                                    xout = xout, 
                                    ties = "ordered",
                                    rule = 2), 
                     origin = "1970-01-01")

# Put it together
stops_interp <- stops %>%
  # Input the interpolated times
  mutate(hora = interp) %>%
  # Delete the first row that was inputed from the GPS
  slice(-1)

stops_interp
   stop_id       lon       lat      dist                hora
1     3528 -38.50404 -3.802504  801.8295 2018-09-03 05:51:21
2     4629 -38.50580 -3.798711 1266.8446 2018-09-03 05:52:18
3     3540 -38.50706 -3.796027 1596.9692 2018-09-03 05:52:51
4     3543 -38.50816 -3.793663 1887.0162 2018-09-03 05:53:43
5     3542 -38.50894 -3.791997 2091.8550 2018-09-03 05:54:05
6     3362 -38.50984 -3.790069 2328.6658 2018-09-03 05:54:38
7     3363 -38.51100 -3.787631 2628.9530 2018-09-03 05:55:27
8     3346 -38.51215 -3.785157 2932.6796 2018-09-03 05:56:18
9     5083 -38.51390 -3.781432 3390.5980 2018-09-03 05:57:15
10    3215 -38.51531 -3.776950 3913.4700 2018-09-03 05:58:22
11    4873 -38.51677 -3.771828 4506.1116 2018-09-03 05:59:52

hora代表插值时间。

走得更远

但是,此插值基于点之间的欧式距离。如果我拥有GPS点和停靠点的预期路径(如道路)怎么办?在这种情况下,我建议使用rgeos::gProject函数来计算GPS和路径中停靠点的累计距离。