Distance géographique / géospatiale entre 2 listes de points lat/lon (coordonnées)
J'ai 2 listes (list1
, list2
) avec la latitude / longitude de différents endroits. Une liste (list2
) a des noms de localité que list1
n'a pas.
Je veux également une localité approximative pour chaque point de list1. Je tiens donc à prendre un point dans list1
, essayer de chercher le point le plus proche de list2
et prendre cette localité. Je répète pour chaque point dans list1
. Il veut aussi la distance (en mètres) et l'index du point (dans list1
) afin que je puisse construire des règles métier autour d'elle - essentiellement, ce sont 2 nouveaux cols qui devraient être ajoutés à list1
(near_dist
, indx
).
J'utilise la fonction gdist
, mais je ne peux pas faire fonctionner cela avec les entrées de trame de données.
Exemple de listes d'entrées:
list1 <- data.frame(longitude = c(80.15998, 72.89125, 77.65032, 77.60599,
72.88120, 76.65460, 72.88232, 77.49186,
72.82228, 72.88871),
latitude = c(12.90524, 19.08120, 12.97238, 12.90927,
19.08225, 12.81447, 19.08241, 13.00984,
18.99347, 19.07990))
list2 <- data.frame(longitude = c(72.89537, 77.65094, 73.95325, 72.96746,
77.65058, 77.66715, 77.64214, 77.58415,
77.76180, 76.65460),
latitude = c(19.07726, 13.03902, 18.50330, 19.16764,
12.90871, 13.01693, 13.00954, 12.92079,
13.02212, 12.81447),
locality = c("A", "A", "B", "B", "C", "C", "C", "D", "D", "E"))
1 réponses
Pour calculer la distance géographique entre deux points avec des coordonnées de latitude/longitude, vous pouvez utiliser plusieurs formules. le paquet geosphere
a le distCosine
, distHaversine
, distVincentySphere
et distVincentyEllipsoid
pour calculer la distance. Parmi ceux-ci, le distVincentyEllipsoid
est considéré comme le plus précis, mais est plus intensif en calcul que les autres.
Avec l'une de ces fonctions, vous pouvez créer une matrice de distance. Sur la base de cette matrice, vous pouvez ensuite attribuer des noms locality
en fonction de la distance la plus courte avec which.min
et la distance correspondante avec min
(voir la dernière partie de la réponse) comme ceci:
library(geosphere)
# create distance matrix
mat <- distm(list1[,c('longitude','latitude')], list2[,c('longitude','latitude')], fun=distVincentyEllipsoid)
# assign the name to the point in list1 based on shortest distance in the matrix
list1$locality <- list2$locality[max.col(-mat)]
Cela donne:
> list1
longitude latitude locality
1 80.15998 12.90524 D
2 72.89125 19.08120 A
3 77.65032 12.97238 C
4 77.60599 12.90927 D
5 72.88120 19.08225 A
6 76.65460 12.81447 E
7 72.88232 19.08241 A
8 77.49186 13.00984 D
9 72.82228 18.99347 A
10 72.88871 19.07990 A
Une autre possibilité est d'attribuer le locality
en fonction des valeurs moyennes de longitude et de latitude des locality
s dans list2
:
library(dplyr)
list2a <- list2 %>% group_by(locality) %>% summarise_each(funs(mean)) %>% ungroup()
mat2 <- distm(list1[,c('longitude','latitude')], list2a[,c('longitude','latitude')], fun=distVincentyEllipsoid)
list1 <- list1 %>% mutate(locality2 = list2a$locality[max.col(-mat2)])
Ou avec data.table
:
library(data.table)
list2a <- setDT(list2)[,lapply(.SD, mean), by=locality]
mat2 <- distm(setDT(list1)[,.(longitude,latitude)], list2a[,.(longitude,latitude)], fun=distVincentyEllipsoid)
list1[, locality2 := list2a$locality[max.col(-mat2)] ]
Cela donne:
> list1
longitude latitude locality locality2
1 80.15998 12.90524 D D
2 72.89125 19.08120 A B
3 77.65032 12.97238 C C
4 77.60599 12.90927 D C
5 72.88120 19.08225 A B
6 76.65460 12.81447 E E
7 72.88232 19.08241 A B
8 77.49186 13.00984 D C
9 72.82228 18.99347 A B
10 72.88871 19.07990 A B
Comme vous pouvez le voir, cela conduit dans la plupart des cas (7 sur 10) à un autre locality
assigné.
Vous pouvez ajouter le distance avec:
list1$near_dist <- apply(mat2, 1, min)
Ou une autre approche avec max.col
(ce qui est hautement probable plus rapide):
list1$near_dist <- mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)]
# or using dplyr
list1 <- list1 %>% mutate(near_dist = mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)])
# or using data.table (if not already a data.table, convert it with 'setDT(list1)' )
list1[, near_dist := mat2[matrix(c(1:10, max.col(-mat2)), ncol = 2)] ]
Le résultat:
> list1
longitude latitude locality locality2 near_dist
1: 80.15998 12.90524 D D 269966.8970
2: 72.89125 19.08120 A B 65820.2047
3: 77.65032 12.97238 C C 739.1885
4: 77.60599 12.90927 D C 9209.8165
5: 72.88120 19.08225 A B 66832.7223
6: 76.65460 12.81447 E E 0.0000
7: 72.88232 19.08241 A B 66732.3127
8: 77.49186 13.00984 D C 17855.3083
9: 72.82228 18.99347 A B 69456.3382
10: 72.88871 19.07990 A B 66004.9900