使用ggplot/ggmap在R中绘制凸包

kokeuurv  于 12个月前  发布在  其他
关注(0)|答案(1)|浏览(128)

我感兴趣的是绘制艾姆斯数据集中邻域的多边形/凸船体表示,类似于O 'Reilly版本的Tidy-modeling with R中使用的方法。
看起来,同一段落的第4章有两个不同的图像,这取决于您使用的来源,第一个图像来自here的O 'Reilly版本,第二个来自tmwr.org
x1c 0d1x的数据

就个人而言,我更喜欢第一个,因为它更容易看到相邻区域的重叠。Tmwr.org有一个GitHub repo,但是凸船体代码没有在那里捕获。
从他们的仓库中,我可以看到他们从this网站下载了一个形状文件,并将其用于背景中的道路Map,然后覆盖多边形。
我试图创建多边形,并使用谷歌Map作为背景,但我没有太大的成功添加谷歌Map层。
在代码的底部,我可以绘制谷歌Map层,但当我引入ames_sf时,我得到以下错误:

Error in `geom_sf()`:
! Problem while computing aesthetics.
ℹ Error occurred in the 4th layer.
Caused by error:
! object 'lon' not found

字符串
从我用ggplot生成的凸包和谷歌Map图像可以看出,他们使用了不同的坐标值,这可能是问题所在。我很困惑,因为在第46行ames_sf是用crs = 3857定义的,然后在第90行的底部ggmap(map)被传递给coord_sf,其中st_crs也被设置为3857。
下面是我拼凑的代码(其中一些来自here),目前我正在寻找一些帮助,在googleMap的背景分层。任何可以分享的帮助将不胜感激。

# LOAD THE REQUIRED LIBRARIES
pacman::p_load(sf, ggplot2, dplyr, RColorBrewer, AmesHousing)

# ASSIGN A PALETTE OF 8 COLORS FROM THE 'DARK2' PALETTE TO A VARIABLE
col_vals <- brewer.pal(8, "Dark2")

# CREATE A NAMED VECTOR 'ames_cols' WITH NEIGHBORHOOD NAMES MAPPED TO SPECIFIC COLORS FROM 'col_vals'
# COPIED FROM THE TMWR.ORG REPO
ames_cols <- c(
  North_Ames = col_vals[3],
  College_Creek = col_vals[4],
  Old_Town = col_vals[8],
  Edwards = col_vals[5],
  Somerset = col_vals[8],
  Northridge_Heights = col_vals[4],
  Gilbert = col_vals[2],
  Sawyer = col_vals[7],
  Northwest_Ames = col_vals[6],
  Sawyer_West = col_vals[8],
  Mitchell = col_vals[3],
  Brookside = col_vals[1],
  Crawford = col_vals[4],
  Iowa_DOT_and_Rail_Road = col_vals[2],
  Timberland = col_vals[2],
  Northridge = col_vals[1],
  Stone_Brook = col_vals[5],
  South_and_West_of_Iowa_State_University = col_vals[3],
  Clear_Creek = col_vals[1],
  Meadow_Village = col_vals[1],
  Briardale = col_vals[7],
  Bloomington_Heights = col_vals[1],
  Veenker = col_vals[2],
  Northpark_Villa = col_vals[2],
  Blueste = col_vals[5],
  Greens = col_vals[6],
  Green_Hills = col_vals[8],
  Landmark = col_vals[3],
  Hayden_Lake = "red" # 'Hayden_Lake' SPECIFICALLY ASSIGNED THE COLOR RED
)

# LOAD AND PREPARE THE AMES HOUSING DATA
ames_data <- make_ames() %>%
  janitor::clean_names()

# CONVERT AMES DATA INTO AN SF (SIMPLE FEATURES) OBJECT FOR SPATIAL ANALYSIS AND
# TRANSFORM EPSG 3857 (Pseudo-Mercator, what Google uses)
ames_sf <- st_as_sf(ames_data, coords = c("longitude", "latitude"), crs = 3857)

# CREATE POLYGONS FOR EACH NEIGHBORHOOD BY AGGREGATING POINTS INTO A CONVEX HULL
ames_sf <- ames_sf %>%
  group_by(neighborhood) %>%
  summarize() %>%
  st_convex_hull()

# PLOT THE CONVEX HULLS USING GGPLOT
#ggplot(data = ames_sf) +
#  geom_sf(aes(fill = neighborhood), color = "white", size = 0.5) +
#  scale_fill_manual(values = ames_cols) +
#  theme_minimal() +
#  labs(title = 'Neighborhoods in Ames') +
#  theme(legend.position = "none")

map <- get_map("Ames, Iowa", maptype = "roadmap", zoom = "auto", scale = "auto", source = "google")

# Define a function to fix the bbox to be in EPSG:3857
ggmap_bbox <- function(map) {
  if (!inherits(map, "ggmap")) stop("map must be a ggmap object")
  # Extract the bounding box (in lat/lon) from the ggmap to a numeric vector,
  # and set the names to what sf::st_bbox expects:
  map_bbox <- setNames(
    unlist(attr(map, "bb")),
    c("ymin", "xmin", "ymax", "xmax")
  )

  # Coonvert the bbox to an sf polygon, transform it to 3857,
  # and convert back to a bbox (convoluted, but it works)
  bbox_3857 <- st_bbox(st_transform(st_as_sfc(st_bbox(map_bbox, crs = 4326)), 3857))

  # Overwrite the bbox of the ggmap object with the transformed coordinates
  attr(map, "bb")$ll.lat <- bbox_3857["ymin"]
  attr(map, "bb")$ll.lon <- bbox_3857["xmin"]
  attr(map, "bb")$ur.lat <- bbox_3857["ymax"]
  attr(map, "bb")$ur.lon <- bbox_3857["xmax"]
  map
}

# Use the function:
map <- ggmap_bbox(map)

ggmap(map) +
  coord_sf(crs = st_crs(3857)) + # force the ggplot2 map to be in 3857
  geom_sf(data = ames_sf, aes(fill = neighborhood), color = "white", size = 0.5, alpha = 0.5)


使用ggmaps我可以创建多边形并将它们打印到坐标网格中,但我很难添加到Map背景中。

uoifb46i

uoifb46i1#

当你在你的问题中比较这两个图时,你应该注意的第一件事(或至少第二件事)是纬度/经度值的差异。即使不太确定纬度/经度的正常范围应该是什么,将美国的住房数据放在赤道上(纬度~0)是一个非常清楚的指标,最好后退几步。
这里的罪魁祸首是crs处理,使用st_as_sf(ames_data, ... ,crs = 3857),您实际上并没有转换坐标,而是将经度/纬度值定义为3857伪墨卡托的东距和北距,这里的crs应该与输入坐标的实际crs匹配。要尽早发现此类问题,请在每一步之后尽可能多地可视化地理空间数据,如果需要的话。使用像mapview()这样的东西要方便得多
以下示例使用OpenStreetMap数据的ggspatial和Carto Light渲染,而不是ggmap和Google tiles。

library(AmesHousing)
library(sf)
#> Linking to GEOS 3.11.2, GDAL 3.6.2, PROJ 9.2.0; sf_use_s2() is TRUE
library(ggplot2)
library(dplyr)
library(mapview)
library(ggspatial)

ames_sf <- make_ames() %>% 
  select(Neighborhood, Latitude, Longitude) %>% 
  janitor::clean_names() %>% 
  # crs in st_as_sf does not transform but defines the projection of source coordinates,
  # when dealing with lat/lon, it's almost always WGS84 aka EPSG:4326
  st_as_sf(coords = c("longitude", "latitude"), crs = "WGS84") %>% 
  group_by(neighborhood) %>% 
  summarise() %>% 
  st_convex_hull()
print(ames_sf, n = 3)
#> Simple feature collection with 28 features and 1 field
#> Geometry type: GEOMETRY
#> Dimension:     XY
#> Bounding box:  xmin: -93.69315 ymin: 41.9865 xmax: -93.57743 ymax: 42.06339
#> Geodetic CRS:  WGS 84
#> # A tibble: 28 × 2
#>   neighborhood                                                          geometry
#> * <fct>                                                            <POLYGON [°]>
#> 1 North_Ames    ((-93.60507 42.03456, -93.60795 42.03456, -93.62474 42.03461, -…
#> 2 College_Creek ((-93.6843 42.01339, -93.68792 42.01404, -93.69205 42.01621, -9…
#> 3 Old_Town      ((-93.62186 42.0258, -93.62374 42.02648, -93.62416 42.02997, -9…
#> # ℹ 25 more rows

# visualize with mapview to check that coordinates / location are OK
mapview(ames_sf, zcol = "neighborhood")

字符串
x1c 0d1x的数据

ggplot(ames_sf) +
  annotation_map_tile(type = "cartolight", zoomin = 0 ) +
  geom_sf(aes(fill = neighborhood), alpha = .2, show.legend = FALSE)
#> Zoom: 13
#> Fetching 16 missing tiles
#> ...complete!

创建于2023-11-06附带reprex v2.0.2

相关问题