web-dev-qa-db-ja.com

ggplot2のマップ上の点の周りに特定の半径を持つ円をプロットします

8つのポイントがプロットされたマップがあります。

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))

次に、プロットされた8つの場所のそれぞれの周りに円をプロットします。円の半径は450メートルである必要があります。

これは私が意味することですが、ggplotを使用します: https://gis.stackexchange.com/questions/119736/ggmap-create-circle-symbol-where-radius-represents-distance-miles-or-km

どうすればこれを達成できますか?

14
Guido167

地球の小さな領域でのみ作業する場合は、ここに概算があります。緯度の各度は、40075/360キロメートルを表します。経度の各度は、(40075/360)* cos(緯度)キロメンバーを表します。これにより、円の中心と半径がわかっているので、円上のすべての点を含むおおよそのデータフレームを計算できます。

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)
16
GL_Li

さて、 参照された投稿 がすでに示唆しているように-メートル単位の投影に切り替えてから、戻ってください:

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"))

これで、widthをメートルで指定できるようになりました。

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

それを元に戻し、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

7
lukeA

緯度と経度を指定して距離をkmで計算するのは、それほど簡単ではありません。たとえば、1度の緯度/経度は、極よりも赤道での距離が大きくなります。正確さを確認できる簡単な回避策が必要な場合は、次のことを試してください。

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

2番目のgeom_pointsizeパラメータを調整して、目的に近づける必要があります。お役に立てば幸いです。

4
Nancy

正確な解決策は、geosphere :: destPoint()関数を使用することです。これは、プロジェクションを切り替えることなく機能します。

1点の周りに特定の半径を持つ360点を決定する関数を定義します。

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]) 
}

dataの各行に関数を適用し、data.frameに変換します。

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

次に、マップは次の方法で簡単に取得できます。

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

2
mharinga