Визначення, чи оточена точка за допомогою растрової обробки


9

Я намагаюся вдосконалити надзвичайно громіздкий процес вектора / пітона для природної моделі небезпеки. На даний момент у нас є тривалий сценарій, який генерує відстані / несучі лінії від заданої точки, щоб визначити:

  1. тип багатокутника, який він перетинає (наприклад, ліс, трава, болото тощо)
  2. відстань до цього багатокутника
  3. скільки цих ліній перетинаються багатокутники, щоб визначити, наскільки він "оточений".

Є багато більше залучених, але в цьому суть. Я намагаюся знайти спосіб покращити це, і зараз я натрапив на частину 3. Ідея полягає в тому, щоб визначити, чи точка повністю оточена багатокутниками, в межах скажімо 200 мPointA оточений, тоді як PointB оточений лише ~ 50%

Тож у моєму зображенні, що додається, я б хотів, щоб точка А була позначена як така, що має більший ризик, ніж точка В, оскільки вона повністю оточена моїми багатокутниками. Це повторюється приблизно на 13 мільйонів балів, тому це не є малим завданням, і я, швидше за все, маю можливість отримувати значення, а не виконувати наш сценарій. Я думаю, що для цього повинні бути різні інструменти гідрології чи кошти, але я не можу зробити це головою.

Як я міг про це піти?


1
Прозорий - це завдання, але йому знадобиться значна допомога при застосуванні до 13 мільйонів балів! Спочатку подумайте, як відрізати точки, оточення яких легко перевірити, наприклад, точки, розташовані в регіонах, що знаходяться за межами багатокутників, діаметром менше (скажімо) 200 м: це може виключати "А", але, можливо, не "В". "B" ніколи не буде виключено, оскільки його оглядовий майданчик (приймаючи ділянки полігону дуже "високими" місцями, що блокують вигляд) простягається на більш ніж 200 м від місця розташування Б.
whuber

Хороший момент @whuber. безумовно, я в змозі зменшити загальну кількість балів, фактично оброблених близькістю, і насправді унікальних довгострокових (також я розмовляю геокодованими адресами, тому блоки квартир можна зменшити з 50 балів до 1), але я все одно буду шукати у кількох мільйонах локацій. Я також можу просто розбити все на блоки, що перекриваються, якщо потрібно. Буде досліджувати огляди. Дякую!
Лоз

Іншим швидким екраном є обчислення фокусної середньої сітки індикаторів 0-1 за допомогою кільцевого сусідства: у будь-яких комірках, де його значення дорівнює 1, ваші багатокутники займають весь периметр на радіусі, звідки вони повинні бути оточені. Це швидкий підрахунок і, можливо, викреслить переважну більшість ваших точок, залежно від того, де вони знаходяться і наскільки ви складені багатокутники. Ви також можете пришвидшити початковий скринінг, попередньо переставляючи сітку на більш грубу роздільну здатність, наприклад, 25-50 м або далі.
whuber

Іншим потенційним етапом обробки, або попереднім етапом, буде передача балів через растерізовану версію вашого набору даних, порівнюючи статистику мікрорайону навколо пунктів. Ви можете або абстрагувати свою вимогу "оточеного" як статистику сусідства точок, або якщо "оточений" необхідний, ви можете знайти "легкі" точки (тобто точку, повністю в зоні ризику) за допомогою растрового сусідства, проаналізуйте «легкі» точки з усіх точок, а потім використовуйте векторний аналіз для решти точок.
DPierce

Нічого, мій запит, безумовно, викликав багато інтересу! Дякую всім, хто надав пропозиції та коментарі. Я буду працювати по-своєму, хоча вони всі і відповідають, але всі вони потребують певного часу, щоб я протестував. Обіцяю, що зреагую в підсумку!
Лоз

Відповіді:


6

Існує рішення про витрати, але вам доведеться його кодувати самостійно. Ось як це може виглядати при застосуванні до кожної точки зображення у запитанні (трохи огрубіло, щоб прискорити обчислення):

Малюнок 0

Чорні клітини є частинами навколишніх багатокутників. Кольори, починаючи від світло-оранжевого (короткий) до синього (довгий), показують максимальну відстань (максимум до 50 комірок), яку можна досягти шляхом обходу прямого огляду, не перехоплюючи комірки полігону. (Будь-яка клітина поза рамками цього зображення розглядається як частина полігонів.)

Давайте обговоримо ефективний спосіб зробити це за допомогою растрового подання даних. У цьому поданні всі "навколишні" полігональні комірки матимуть, скажімо, ненульові значення, і будь-яка комірка, яку можна "побачити крізь", матиме нульове значення.

Крок 1: Попередньо обчисливши структуру даних про сусідство

Спочатку ви повинні вирішити, що означає одна клітинка блокувати іншу. Одне з найсправедливіших правил, яке я можу знайти, це таке: використовуючи інтегральні координати рядків і стовпців (і припускаючи квадратні осередки), давайте розглянемо, які комірки можуть блокувати клітинку (i, j) з виду на початок (0,0). Я призначаю комірку (i ', j'), яка є найближчою до відрізка лінії, що з'єднує (i, j) до (0,0), серед усіх комірок, координати яких відрізняються від i та j максимум 1. Тому що це не завжди отримати унікальне рішення (наприклад, при (i, j) = (1,2) і (0,1), і (1,1) будуть однаково добре працювати), потрібні певні засоби для вирішення зв'язків. Було б непогано, щоб ця резолюція зв'язків дотримувалася симетрії кругових мікрорайонів у сітках: заперечення ні координат, ні перемикання координат зберігає ці мікрорайони. Тому ми можемо вирішити, які клітини блокувати (я,

Ілюструючи це правило, написаний наступний код прототипу R. Цей код повертає структуру даних, яка буде зручною для визначення "оточеності" довільних комірок у сітці.

screen <- function(k=1) {
  #
  # Returns a data structure:
  #   $offset is an array of offsets
  #   $screened is a parallel array of screened offset indexes.
  #   $distance is a parallel array of distances.
  # The first index always corresponds to (0,0).
  #
  screened.by <- function(xy) {
    uv <- abs(xy)
    if (reversed <- uv[2] > uv[1]) {
      uv <- rev(uv)
    }
    i <- which.min(c(uv[1], abs(uv[1]-uv[2]), uv[2]))
    ij <- uv + c(floor((1-i)/3), floor(i/3)-1)
    if (reversed) ij <- rev(ij)
    return(ij * sign(xy))
  }
  #
  # For each lattice point within the circular neighborhood,
  # find the unique lattice point that screens it from the origin.
  #
  xy <- subset(expand.grid(x=(-k:k), y=(-k:k)), 
               subset=(x^2+y^2 <= k^2) & (x != 0 | y != 0))
  g <- t(apply(xy, 1, function(z) c(screened.by(z), z)))
  #
  # Sort by distance from the origin.
  #
  colnames(g) <- c("x", "y", "x.to", "y.to")
  ij <- unique(rbind(g[, 1:2], g[, 3:4]))
  i <- order(abs(ij[,1]), abs(ij[,2])); ij <- ij[i, , drop=FALSE]
  rownames(ij) <- 1:length(i)
  #
  # Invert the "screened by" relation to produce the "screened" relation.
  #
  # (Row, column) offsets.
  ij.df <- data.frame(ij, i=1:length(i))
  #
  # Distances from the origin (in cells).
  distance <- apply(ij, 1, function(u) sqrt(sum(u*u)))
  #
  # "Screens" relation (represented by indexes into ij).
  g <- merge(merge(g, ij.df), ij.df, 
             by.x=c("x.to", "y.to"), by.y=c("x","y"))
  g <- subset(g, select=c(i.x, i.y))
  h <- by(g$i.y, g$i.x, identity)

  return( list(offset=ij, screened=h, distance=distance) )
}

Значення screen(12)було використано для створення цього зображення цього екраніального відношення: стрілки вказують від комірок до тих, які негайно їх екранують. Відтінки пропорційні відстані до походження, яке знаходиться в середині цього мікрорайону:

Фігура 1

Цей обчислення є швидким і його потрібно робити лише один раз для даного мікрорайону. Наприклад, якщо ви дивитесь 200 м на сітку з 5 м осередків, розмір мікрорайону складе 200/5 = 40 одиниць.

Крок 2: Застосування обчислень до вибраних точок

Інше зрозуміло: щоб визначити, чи "комірка", розташована у (x, y) (у координатах рядків і стовпців) "оточена" щодо цієї структури даних про сусідство, виконайте тест рекурсивно, починаючи зі зміщення (i, j) = (0,0) (сусідське походження). Якщо значення в сітці багатокутника при (x, y) + (i, j) ненульове, то видимість там блокується. В іншому випадку нам доведеться розглянути всі компенсації, які могли бути заблоковані при зміщенні (i, j) (які знаходяться в O (1) час, використовуючи повернуту структуру даних screen). Якщо жодної не заблоковано, ми дійшли до периметра і зробимо висновок, що (x, y) не оточений, тому ми зупиняємо обчислення (і не намагаємось оглянути будь-які інші точки в околицях).

Ми можемо зібрати ще більше корисної інформації, відстежуючи найдальшу відстань прямої огляду, досягнуту під час роботи алгоритму. Якщо це менше бажаного радіуса, осередок оточують; інакше це не так.

Ось Rпрототип цього алгоритму. Це довше, ніж здається, оскільки Rне підтримує (просто) структуру стека, необхідну для здійснення рекурсії, тому стек також повинен бути закодований. Фактичний алгоритм починається приблизно на дві третини шляху і потребує лише десятка рядків. (І половина з них просто впорядковує ситуацію по краю сітки, перевіряючи наявність індексів поза зоною дії в околицях. Це можна зробити більш ефективним, просто розширивши сітку полігону kрядками та стовпцями по всьому периметру, усуваючи будь-які необхідність перевірки діапазону індексу вартістю трохи більше оперативної пам’яті для утримання полігонної сітки.)

#
# Test a grid point `ij` for a line-of-sight connection to the perimeter
# of a circular neighborhood.  
#   `xy` is the grid.
#   `counting` determines whether to return max distance or count of stack ops.
#   `perimeter` is the assumed values beyond the extent of `xy`.
#
# Grid values of zero admit light; all others block visibility
# Returns maximum line-of-sight distance found within `nbr`.
#
panvisibility <- function(ij, xy, nbr=screen(), counting=FALSE, perimeter=1) {
  #
  # Implement a stack for the algorithm.
  #
  count <- 0 # Stack count
  stack <- list(ptr=0, s=rep(NA, dim(nbr$offset)[1]))
  push <- function(x) {
    n <- length(x)
    count <<- count+n         # For timing
    stack$s[1:n + stack$ptr] <<- x
    stack$ptr <<- stack$ptr+n
  }
  pop <- function() {
    count <<- count+1         # For timing
    if (stack$ptr <= 0) return(NULL)
    y <- stack$s[stack$ptr]
    #stack$s[stack$ptr] <<- NA # For debugging
    stack$ptr <<- stack$ptr - 1
    return(y)
  }
  #
  # Initialization.
  #
  m <- dim(xy)[1]; n <- dim(xy)[2]
  push(1) # Stack the *indexes* of nbr$offset and nbr$screened.
  dist.max <- -1
  #
  # The algorithm.
  #
  while (!is.null(i <- pop())) {
    cell <- nbr$offset[i, ] + ij
    if (cell[1] <= 0 || cell[1] > m || cell[2] <= 0 || cell[2] > n) {
      value <- perimeter
    } else {  
      value <- xy[cell[1], cell[2]]
    }
    if (value==0) {
      if (nbr$distance[i] > dist.max) dist.max <- nbr$distance[i]
      s <- nbr$screened[[paste(i)]]
      if (is.null(s)) {
        #exited = TRUE
        break
      }
      push(s)
    }
  }
  if (counting) return ( count )
  return(dist.max)
}

Малюнок 2: Приклад

У цьому прикладі полігональні клітини чорні. Кольори дають максимальну відстань прямолінійного огляду (до 50 комірок) для неополігональних комірок, починаючи від світло-оранжевого на короткі відстані до темно-синього на найдовші відстані. (Клітини - одна одиниця завширшки і висока.) Помітно очевидні смуги створюються невеликими багатокутниками «островами» посеред «річки»: кожна блокує довгий рядок інших комірок.

Аналіз алгоритму

Структура стека реалізує перший глибинний графік видимості сусідства для підтвердження того, що клітина не оточена. Там, де клітини знаходяться далеко від будь-якого багатокутника, цей пошук потребує огляду лише осередків O (k) на окружність радіуса-k. Найгірші випадки трапляються, коли всередині мікрорайону є невелика кількість розсіяних полігонів, але навіть межі мікрорайону неможливо досягти: для цього потрібен огляд майже всіх комірок у кожному районі, що є O (k ^ 2) операція.

Наступна поведінка характерна для того, з чим зіткнуться. Для малих значень k, якщо полігони не заповнюють більшу частину сітки, більшість неолігональних комірок буде очевидно незатоплена, а алгоритм масштабується як O (k). Для проміжних значень масштабування починає виглядати як O (k ^ 2). Оскільки k стає дійсно великим, більшість осередків будуть оточені, і цей факт можна визначити задовго до того, як буде перевірено все околиці: таким чином обчислювальні зусилля алгоритму досягають практичної межі. Ця межа досягається, коли радіус сусідства наближається до діаметра найбільших з'єднаних неополігональних областей у сітці.

Як приклад, я використовую countingопцію, закодовану в прототипі, screenщоб повернути кількість операцій стека, використовуваних у кожному виклику. Це вимірює обчислювальні зусилля. Наступний графік зображує середню кількість ops стека як функцію радіусу сусідства. Він демонструє передбачувану поведінку.

Малюнок 3

Ми можемо використати це для оцінки обчислень, необхідних для оцінки 13 мільйонів балів у сітці. Припустимо, що використовується околиця k = 200/5 = 40. Тоді в середньому знадобиться кілька сотень операцій стека (залежно від складності сітки багатокутника та де 13 мільйонів точок розташовані відносно багатокутників), маючи на увазі, що в ефективній компільованій мові максимум кілька тисяч простих числових операцій знадобиться (додавати, множувати, читати, писати, зміщувати тощо). Більшість ПК зможуть оцінити оточеність близько мільйона балів за такою швидкістю. (TheRреалізація набагато, набагато повільніше, ніж це, тому що вона погана при такому алгоритмі, через що її можна вважати лише прототипом. Відповідно, ми можемо сподіватися, що ефективна реалізація на досить ефективній та відповідній мові - C ++ і Python приходять до тями - могли завершити оцінку 13 мільйонів балів за хвилину або менше, якщо припустити, що вся полігонна сітка знаходиться в оперативній пам'яті.

Коли сітка занадто велика, щоб вміститися в оперативну пам’ять, цю процедуру можна застосувати до плиткових частин сітки. Вони повинні лише перекриватися kрядками та стовпцями; приймайте максимуми при перекриттях при мозаїзації результатів.

Інші програми

«Витягнути» з тіла води тісно пов'язана з «surroundedness» його точок. Насправді, якщо ми будемо використовувати радіус сусідства, рівний або більший діаметру водойми, ми створимо сітку (непрямої) плоди у кожній точці водного тіла. Використовуючи менший радіус сусідства, ми принаймні отримаємо нижню межу для вибору у всіх найвищих точках, що в деяких додатках може бути досить хорошим (і може значно зменшити обчислювальні зусилля). Варіант цього алгоритму, який обмежує відношення "екранізовано" на конкретні напрямки, був би одним із способів ефективного обчислення результатів у цих напрямках. Зауважте, що такі варіанти вимагають зміни коду для screen; код для цього panvisibilityвзагалі не змінюється.


2

Я точно можу побачити, як можна зробити це за допомогою растрового рішення, але, враховуючи навіть зменшене число балів, я би очікував дуже великого / високого дозволу і тому важко обробити сітку або набір сіток. Враховуючи це, мені цікаво, чи використовувати топологію в gdb може бути ефективніше. Ви можете знайти всі внутрішні порожнечі з чимось на зразок:

arcpy.env.workspace = 'myGDB'
arcpy.CreateTopology_management('myGDB', 'myTopology', '')    
arcpy.AddFeatureClassToTopology_management('myTopology', 'myFeatures', '1','1')    
arcpy.AddRuleToTopology_management ('myToplogy', 'Must Not Have Gaps (Area)', 'myFeatures', '', '', '')    
arcpy.ValidateTopology_management('myTopology', 'Full_Extent')
arcpy.ExportTopologyErrors_management('myTopology', 'myGDB', 'topoErrors')
arcpy.FeatureToPolygon_management('topoErrors_line','topoErrorsVoidPolys', '0.1')`

потім ви можете працювати з topoErrorsVoidPolysвашим звичайним малюнком Intersect_analysis()або будь-яким іншим. Можливо, вам доведеться возитися з витяганням полісів, що цікавлять topoErrorsVoidPolys. @whuber має ряд чудових дописів щодо подібних речей у інших місцях тут, на gis.stackexchange.com.


Це цікава ідея попередньої обробки. Я думаю, що це може бути легко адаптовано до включення межі 200 м (буферизацією та перетином тощо). Ваша думка щодо отримання сіток досить великою, безумовно, викликає занепокоєння. В ГІС немає правила, яке говорить про те, що рішення проблеми повинно бути суто растровим або чисто векторним (хоча існує принцип, який говорить про те, що у вас має бути досить вагомий привід для перетворення одного представництва в інше в середині аналізу; тут, як ви припускаєте, може бути істотна користь від виконання саме цього).
whuber

0

Якщо ви дійсно хочете перейти на растер ... Я б щось робив у відповідності з цим псевдокодом (не чіпляйте лише тому, що це очевидно, що я повернення AML!: P)

  1. растрові точки ("pts_g") і polys ("polys_g" (
  2. пустоти = регіональна група (con (isnull (polys_g), 1))
  3. може знадобитися зробити щось для вдосконалення порожнеч, щоб усунути небажаний зовнішній полігон / відкриту зону Всесвіту
  4. pts_surrounded = con (пустоти, pts_g)

Просто такий спосіб складати, тому може знадобитися доопрацювання.


Ваше рішення не посилається на обмежувальну відстань (скажімо, 200 м), тому, здається, не відповідає правильно на питання.
whuber

ти правий. Це стосується і моєї іншої відповіді. Я думаю, що можна скористатися Expand(), але в цей момент я б подумав, що відповідь від @radouxju буде функціонально рівнозначною і, ймовірно, швидшою. (нічого проти перегляду, просто не використовуйте його багато).
Роланд

намагався редагувати, коли закінчився час. Хочете розширити те, Expand()щоб сказати, що це робиться, pts_gі просто використовувати Con()для перетину polys_g.
Роланд

0

Якщо ви використовуєте значення порогової відстані (тут ви говорите про 200 м), то найкращим рішенням буде використання векторного аналізу:

1) створити 200 м буфера навколо кожної точки (чорним кольором на ілюстрації)

2) використовувати інструмент перетину (аналіз) між буфером та полігонами (синім кольором на ілюстрації). Це буде виглядати приємніше, якщо ви зробите це між межами навколишніх багатокутників і буфером, але кінцевий результат такий же.

3) використовувати функцію багатокутника (управління) для створення полігонів, де ваші точки повністю оточені (червоним кольором на ілюстрації)

4) виберіть шари за місцем розташування (управління) або просторовим з'єднанням (аналіз) для виявлення оточених точок. Використання просторового з'єднання дозволяє мати інформацію про вбудовуваний багатокутник (площа полігону, зональна статистика ...), яка може бути корисною для подальшої обробки.

Альтернативи 2b) Залежно від ваших потреб, ви можете вибрати місце розташування навколишніх багатокутників на відстані 200 м, потім можна визначити деякі види "огородження", але не так суворо, як у 2).

введіть тут опис зображення

Розглядаючи "випадок лабіринту", це могло б допомогти: оцінити, як довго потрібно "втекти" з місця.

  • Ви вже можете виключити з аналізу пункти, які повністю включені або повністю вільні

  • потім ви перетворюєте свої перешкоди в растр і встановлюєте значення на NoData, де у вас є багатокутник, і на розмір комірки в метрі, де ви цього не робите (це зробить вашу вартість растровою).

  • по-третє, ви можете обчислити відстань витрат, використовуючи новостворений растр витрат

  • нарешті, ви використовуєте зональну статистику в якості таблиці на основі меж буфера, перетвореного в растровий (утворюючи кільцеву кількість). Якщо ви можете втекти в будь-який бік, мінімум повинен бути приблизно 200 (залежно від розміру комірки вашого аналізу). Але якщо ви перебуваєте в лабіринті, максимум буде більшим за 200. Тож максимум зональної статистики мінус 200 буде безперервним значенням, яке вказує на те, як важко "втекти".


Будь ласка, уточніть своє визначення "оточений". Опис у питанні говорить про те, що точку слід вважати "оточеною", коли якась частина багатокутника видно у всіх напрямках навколо цієї точки (на відстань 200 м). Як ви перевірите це саме на кроці (3)? (Використовувати векторний аналіз непросто!)
whuber

Я додав трохи ілюстрацій, простіше пояснити так. Якщо буфер не перетинає багатокутник у всіх напрямках, цикл не закриється. І якщо цикл не буде близьким, це не зробить багатокутник.
radouxju

Я не впевнений, що ви маєте на увазі під "петлею" чи "закритою". Зауважте, що точка може бути "оточена", навіть коли жодне коло радіуса r (менше 200 м) навколо неї повністю не міститься в межах багатокутника. Подумайте про лабіринт: полігон - це все, крім коридорів у лабіринті. Можна втекти від лабіринту, починаючи в будь-якій точці всередині нього, але більшість точок буде "оточене" в тому сенсі, що зовнішність лабіринту не буде видно з них.
whuber

з мого розуміння, оточуючий означає десь не можна втекти. На ілюстрації, ви можете втекти від B, але не від A. З іншого боку, B, здається, буде оточений, якщо ви використовуєте оглядовий шар (ну, може, не на 200 м, оскільки на зображенні немає смуги масштабу, але ви б дивіться межі багатокутника, дивлячись у всі сторони). Я думаю, що нам потрібно більше деталей від @Loz
radouxju

Це взагалі не буде складним питанням, якщо критерієм "неможливо уникнути" був критерій перевірки: просто регіональна група згрупує доповнення полігону, збереже лише унікальний зовнішній компонент і перевірить на включення всередину нього. Думаю, уважне прочитання питання - особливо його посилання на перегляд усіх можливих підшипників - роз'яснює сенс, в якому «оточений» має намір, хоча я згоден, він висловлюється досить невиразно.
whuber
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.