Общая площадь под несколькими перекрывающимися полигонами

У меня есть файлы форм за несколько лет (всего восемь лет), представляющие обработку инсектицидами определенной площади. Я могу легко найти общую обработанную площадь и сравнить перекрытие за два отдельных года. Однако я застрял в разработке краткого кода для определения общей площади, обработанной два раза, три раза, за раз и т. д.

Упрощенное изображение прикреплено в качестве примера: каждый год имеет разный цвет. Некоторые области обрабатываются только один раз (один цвет), некоторые обрабатываются дважды (перекрывающиеся цвета), а некоторые обрабатываются более 2 раз.

Как мне найти общую площадь, обработанную 1, 2, 3, 4 и т. д. раз?

Я не могу создать пример кода, например изображения карты (или поделиться своими файлами форм). Однако вот очень упрощенный пример площади, обработанной за год, с некоторым перекрытием между годами (по запросу):

library(tidyverse)
library(sf)

polygon1 <- st_polygon(list(
  matrix(c(0, 0, 1, 0, 1, 1, 0, 1, 0, 0), ncol = 2, byrow = TRUE)
))

polygon2 <- st_polygon(list(
  matrix(c(2, 2, 3, 2, 3, 3, 2, 3, 2, 2), ncol = 2, byrow = TRUE)
))

polygon3 <- st_polygon(list(
  matrix(c(0.5, 0.5, 1.5, 0.5, 1.5, 1.3, 0.5, 1.3, 0.5, 0.5), ncol = 2, byrow = TRUE)
))

polygon4 <- st_polygon(list(
  matrix(c(2, 0.5, 2.5, 0.5, 2.5, 2.5, 2, 2.5, 2, 0.5), ncol = 2, byrow = TRUE)
))

polygon5 <- st_polygon(list(
  matrix(c(1.2, 1.5, 1.8, 1.5, 1.8, 1.8, 1.2, 1.8, 1.2, 1.5), ncol = 2, byrow = TRUE)
))

polygon6 <- st_polygon(list(
  matrix(c(0.8, 0.8, 2.2, 0.8, 2.2, 1.2, 0.8, 1.2, 0.8, 0.8), ncol = 2, byrow = TRUE)
))

polygon7 <- st_polygon(list(
  matrix(c(1.3, 2.2, 2.1, 2.2, 2.1, 2.8, 1.3, 2.8, 1.3, 2.2), ncol = 2, byrow = TRUE)
))


year1 <- st_multipolygon(list(polygon1, polygon2)) |> 
  st_sfc()

year2 <- st_multipolygon(list(polygon3, polygon4, polygon5))|> 
  st_sfc()

year3 <- st_multipolygon(list(polygon6, polygon7))|> 
  st_sfc()


ggplot() +
  geom_sf(data = year1, fill = "#009E73", color = 'black', alpha = 0.5) +
  geom_sf(data = year2, fill = "#E69F00", color = 'black', alpha = 0.5) +
  geom_sf(data = year3, fill = "#0072B2", color = 'black', alpha = 0.5) +
  theme_classic()

Как бы я мог определить общую площадь, покрытую трижды, общую площадь, покрытую только дважды, а общую площадь, покрытую только один раз?

Если вы не загрузили их в R, как называются ваши шейп-файлы? Если вы загрузили их в R, являются ли они объектами научной фантастики и как вы их назвали?

L Tyrone 26.08.2024 18:06

Вы можете использовать два разных подхода: (A) рекурсивно пересекающиеся полигоны обработки или (B) растрирование полигонов и использование растровых вычислений. (A) обеспечивает «бесконечную» точность, тогда как (B) довольно прост и может работать намного быстрее, в зависимости от формы. Похоже, что большая часть ваших данных в любом случае привязана к сетке. Какой подход лучше решит проблему?

I_O 26.08.2024 18:13

@LTyrone файлы форм загружаются в R, это классы «sf» и «data.frame», с геометрией: «sfc_MULTIPOLYGON. Я не понимаю, почему то, что я решил назвать каждым шейп-файлом в среде R, является ревенантом. Я назвал каждый импортированный файл формы в соответствии с годом, к которому он относится (например, Treatment_1990, Treatment_1991 и т. д.).

Sara 26.08.2024 18:16

@I_O файлы форм большие, а многоугольники сложные. Изображение карты было создано на основе подмножества моих данных, простой график с прямоугольниками был добавлен, поскольку другой пользователь рекомендовал добавить код, чтобы проиллюстрировать мою проблему - для этого мне нужно было упростить ситуацию). Как бы я растрировал полигоны, чтобы определить общую площадь, покрытую три раза, общую площадь, покрытую только дважды, и общую площадь, покрытую только один раз.

Sara 26.08.2024 18:25

Имена ваших файлов имеют значение, поскольку рекурсивный подход может быть подходящим решением. Знание названий помогает дать однозначный ответ.

L Tyrone 26.08.2024 18:54

Если вы заинтересованы в расчете общей покрытой площади, будет полезно знать как CRS, так и общую протяженность всех ваших научно-фантастических объектов. Чтобы получить общие координаты экстента rbind() ваших научно-фантастических объектов, затем запустите st_bbox().

L Tyrone 26.08.2024 19:18
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
58
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

используя растровый подход и данные вашего примера:

  • собрать различные простые функции в один пространственный фрейм данных (конечно, это можно сделать вручную):
library(sf)


    the_polygons <- 
      do.call(rbind,
              lapply(list('year1', 'year2', 'year3'),
                  FUN = \(o_name) data.frame(year = o_name,
                                             geometry = get(o_name)
                                             )
                  )
      ) |>
      st_sf()

## > the_polygons
## Simple feature collection with 3 features and 1 field
## Geometry type: MULTIPOLYGON
## Dimension:     XY
## Bounding box:  xmin: 0 ymin: 0 xmax: 3 ymax: 3
## CRS:           NA
##    year                       geometry
## 1 year1 MULTIPOLYGON (((0 0, 1 0, 1...
## 2 year2 MULTIPOLYGON (((0.5 0.5, 1....
## 3 year3 MULTIPOLYGON (((0.8 0.8, 2....
  • создайте базовый растр с общей протяженностью полигонов (полученной с помощью st_bbox и желаемым разрешением. Этот пустой базовый растр позже заполняется значениями полигонов.
    library(terra) ## the workhorse for raster, as {sf} is for features

    raster_base <- the_polygons |> st_bbox() |> ext() |> rast(resolution = .1)

  • создайте выходной растр, rasterizeвключив полигоны в базовый растр, sumподсчитав количество непустых значений (по одному на полигон, охватывающий конкретную ячейку):

    raster_output <- rasterize(the_polygons, raster_base, fun = sum)

  • извлеките и сведите в таблицу значения ячеек:

    table(values(raster_output))

##   1   2   3 
## 325  78   7

(multiply cell count by cell size to obtain area at your resolution)

визуальный контроль:


    plot(raster_output)

Для векторного метода я бы подошел к этому, объединив данные за все годы в один слой с переменной, идентифицирующей год, а затем выполнил самопересечение. В результате будет создан индекс, который можно будет использовать для обратной ссылки на объединенный годичный слой, чтобы получить подробную информацию о каждом перекрытии, которую вы можете объединить в новое поле, в котором перечислены годы. Затем это можно объединить, суммируя полученный слой научной фантастики по интересующим вас переменным. В случае действительно больших слоев этап пересечения может занять некоторое время.

#assign a value to each year

year1 <- st_multipolygon(list(polygon1, polygon2)) %>%
  st_sfc() %>%
    st_union() %>%
      st_as_sf() %>%
        mutate(Year=2024)%>%
  st_set_geometry("geometry")
  

year2 <- st_multipolygon(list(polygon3, polygon4, polygon5))%>%
  st_sfc() %>%
  st_union() %>%
  st_as_sf() %>%
  mutate(Year=2023)%>%
  st_set_geometry("geometry")

year3 <- st_multipolygon(list(polygon6, polygon7))%>%
  st_sfc() %>%
  st_union() %>%
  st_as_sf() %>%
  mutate(Year=2022) %>%
    st_set_geometry("geometry")




###combine into a single layer
combo_years <- bind_rows(list(year1, year2, year3)) 


#self intersect, give an index of overlaps "origins"
combo_years_intersect <- st_intersection(combo_years)

#use the index nubers to the get the year values from the original data set and concantenate into a list using purr functions, could alos use an apply or loop to do the same
combo_years_intersect_list <- combo_years_intersect %>%
  st_collection_extract("POLYGON") %>%  #this not necessary in simplified example but in messy shapes sometimes linestrings an points created
  mutate(Year_List=map_chr(origins, \(x) paste0(combo_years$Year[x], collapse = "; ") ))



#group and summarise by the nuber of overlaps and the the overalpping years
combo_years_intersect_summarised <- combo_years_intersect_list %>%group_by(n.overlaps, Year_List) %>%
  summarise() %>%
    ungroup() %>%
    mutate(area=st_area(.))




mapview::mapview(combo_years_intersect_summarised, zcol = "n.overlaps")



mapview::mapview(combo_years_intersect_summarised, zcol = "Year_List")



##can be piped together   



combo_years_summary_piped <- combo_years %>%
  st_intersection() %>%
  mutate(Year_List=map_chr(origins, \(x) paste0(combo_years$Year[x], collapse = "; ") ))%>%
  group_by(n.overlaps, Year_List) %>%
  summarise()%>%
  ungroup() %>%
  mutate(area=st_area(.))



##can calc other values at same time


combo_years_summary_many <- combo_years %>%
  st_intersection() %>%
  mutate(Year_List=map_chr(origins, \(x) paste0(combo_years$Year[x], collapse = "; ") ), 
         Year_Min=map_dbl(origins, \(x) min(combo_years$Year[x], na.rm=T) ),
         Year_Max=map_dbl(origins, \(x) min(combo_years$Year[x], na.rm=T) ))%>%
  group_by(n.overlaps, Year_List,Year_Min, Year_Max) %>%
  summarise()%>%
  ungroup() %>%
  mutate(area=st_area(.))




mapview::mapview(combo_years_summary_many, zcol = "n.overlaps")




mapview::mapview(combo_years_summary_many, zcol = "Year_Min")
  


##can now summarise total area by overlapping treatments 



OVERLAP_SUMMARY_AREAS <- combo_years_summary_many %>%
  st_drop_geometry() %>%
    group_by(n.overlaps) %>%
      summarise(total_area=sum(area))

#or

OVERLAP_SUMMARY_AREAS <- combo_years_summary_many %>%
  st_drop_geometry() %>%
  group_by(Year_List) %>%
  summarise(total_area=sum(area))

Другие вопросы по теме