web-dev-qa-db-fra.com

Tracer le cercle avec un certain rayon autour du point sur une carte dans ggplot2

J'ai une carte avec les 8 points tracés dessus:

library(ggplot2)
library(ggmap)
data = data.frame(
    ID = as.numeric(c(1:8)),
    longitude = as.numeric(c(-63.27462, -63.26499, -63.25658, -63.2519, -63.2311, -63.2175, -63.23623, -63.25958)),
    latitude = as.numeric(c(17.6328, 17.64614, 17.64755, 17.64632, 17.64888, 17.63113, 17.61252, 17.62463))
)

island = get_map(location = c(lon = -63.247593, lat = 17.631598), zoom = 13, maptype = "satellite")
islandMap = ggmap(island, extent = "panel", legend = "bottomright")
RL = geom_point(aes(x = longitude, y = latitude), data = data, color = "#ff0000")
islandMap + RL + scale_x_continuous(limits = c(-63.280, -63.21), expand = c(0, 0)) + scale_y_continuous(limits = c(17.605, 17.66), expand = c(0, 0))

Maintenant, je veux tracer un cercle autour de chacun des 8 emplacements tracés. Le cercle doit avoir un rayon de 450 mètres. 

C’est ce que je veux dire, mais avec ggplot: https://gis.stackexchange.com/questions/119736/ggmap-create-circle-symbol-where-radius-represents-distance-miles-or-km

Comment puis-je atteindre cet objectif?

13
Guido167

Si vous ne travaillez que sur une petite partie de la terre, voici une approximation. Chaque degré de latitude représente 40075/360 km. Chaque degré de longitude représente (40075/360) * cos (latitude) kilomètres. Avec cela, nous pouvons calculer approximativement un bloc de données incluant tous les points sur les cercles, en connaissant le centre et le rayon des cercles. 

library(ggplot2)
library(ggmap)
data = data.frame(
    ID = as.numeric(c(1:8)),
    longitude = as.numeric(c(-63.27462, -63.26499, -63.25658, -63.2519, -63.2311, -63.2175, -63.23623, -63.25958)),
    latitude = as.numeric(c(17.6328, 17.64614, 17.64755, 17.64632, 17.64888, 17.63113, 17.61252, 17.62463))
)

#################################################################################
# create circles data frame from the centers data frame
make_circles <- function(centers, radius, nPoints = 100){
    # centers: the data frame of centers with ID
    # radius: radius measured in kilometer
    #
    meanLat <- mean(centers$latitude)
    # length per longitude changes with lattitude, so need correction
    radiusLon <- radius /111 / cos(meanLat/57.3) 
    radiusLat <- radius / 111
    circleDF <- data.frame(ID = rep(centers$ID, each = nPoints))
    angle <- seq(0,2*pi,length.out = nPoints)

    circleDF$lon <- unlist(lapply(centers$longitude, function(x) x + radiusLon * cos(angle)))
    circleDF$lat <- unlist(lapply(centers$latitude, function(x) x + radiusLat * sin(angle)))
    return(circleDF)
}

# here is the data frame for all circles
myCircles <- make_circles(data, 0.45)
##################################################################################


island = get_map(location = c(lon = -63.247593, lat = 17.631598), zoom = 13, maptype = "satellite")
islandMap = ggmap(island, extent = "panel", legend = "bottomright")
RL = geom_point(aes(x = longitude, y = latitude), data = data, color = "#ff0000")
islandMap + RL + 
    scale_x_continuous(limits = c(-63.280, -63.21), expand = c(0, 0)) + 
    scale_y_continuous(limits = c(17.605, 17.66), expand = c(0, 0)) +
    ########### add circles
    geom_polygon(data = myCircles, aes(lon, lat, group = ID), color = "red", alpha = 0)
13
GL_Li

Comme le suggère déjà le affichage référencé - passez à une projection basée sur des mètres, puis inversement: 

library(rgeos)
library(sp)
d <- SpatialPointsDataFrame(coords = data[, -1], 
                            data = data, 
                            proj4string = CRS("+init=epsg:4326"))
d_mrc <- spTransform(d, CRS("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +no_defs"))

Maintenant, la width peut être spécifiée en mètres: 

d_mrc_bff_mrc <- gBuffer(d_mrc, byid = TRUE, width = 450)

Transformez-le et ajoutez-le au graphique en utilisant geom_path

d_mrc_bff <- spTransform(d_mrc_bff_mrc, CRS("+init=epsg:4326"))
d_mrc_bff_fort <- fortify(d_mrc_bff)
islandMap + 
  RL + 
  geom_path(data=d_mrc_bff_fort, aes(long, lat, group=group), color="red") + 
  scale_x_continuous(limits = c(-63.280, -63.21), expand = c(0, 0)) + 
  scale_y_continuous(limits = c(17.605, 17.66), expand = c(0, 0)) 

enter image description here

6
lukeA

Calculer la distance en km étant donné la latitude et la longitude, ce n'est pas très simple; 1 degré lat/long correspond à une distance plus grande à l'équateur qu'aux pôles, par exemple. Si vous souhaitez une solution de contournement simple et précise, vous pouvez essayer:

islandMap + RL + 
  scale_x_continuous(limits = c(-63.280, -63.21), expand = c(0, 0)) + 
  scale_y_continuous(limits = c(17.605, 17.66), expand = c(0, 0)) + 
  geom_point(aes(x = longitude, y = latitude), data = data, size = 20, shape = 1,  color = "#ff0000")

 enter image description here

Vous devrez ajuster le paramètre size dans le 2nd geom_point pour vous rapprocher de ce que vous voulez. J'espère que ça aide!

4
Nancy

Une solution précise consiste à utiliser la fonction geosphere :: destPoint (). Cela fonctionne sans changer de projection. 

Définir une fonction pour déterminer 360 points avec un certain rayon autour d’un point:

library(dplyr)
library(geosphere)

fn_circle <- function(id1, lon1, lat1, radius){ 
   data.frame(ID = id1, degree = 1:360) %>%
      rowwise() %>%
      mutate(lon = destPoint(c(lon1, lat1), degree, radius)[1]) %>%
      mutate(lat = destPoint(c(lon1, lat1), degree, radius)[2]) 
}

Appliquez une fonction à chaque ligne de data et convertissez-le en data.frame:

circle <- apply(data, 1, function(x) fn_circle(x[1], x[2], x[3], 450))
circle <- do.call(rbind, circle)

Ensuite, la carte peut être facilement obtenue par:

islandMap + 
   RL +
   scale_x_continuous(limits = c(-63.280, -63.21), expand = c(0, 0)) + 
   scale_y_continuous(limits = c(17.605, 17.66), expand = c(0, 0)) +
   geom_polygon(data = circle, aes(lon, lat, group = ID), color = "red", alpha = 0)

 enter image description here

0
mharinga