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"))
26
demandé sur Jaap 2015-07-28 08:42:02

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 localitys 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
46
répondu Jaap 2017-03-05 13:16:52