Згладжування багатокутників у контурній карті?


52

Ось контурна карта, для якої доступні всі багатокутники рівнів.

Дозвольте запитати, як згладити багатокутники, зберігаючи всі вершини, що зберігаються у їх точних місцях?

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

Насправді я шукаю фрагмент коду (бажано, в Python ), який може згладжувати 2D багатокутники (будь-якого типу: опуклі, увігнуті, самопересічні тощо) досить безболісно (забути сторінки кодів) та точним.

FYI, в ArcGIS є функція, яка робить це ідеально, але використання сторонніх комерційних додатків не є моїм вибором для цього питання.

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


1)

Scipy.interpolate:

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

Як ви бачите, отримані сплайни (червоні) є незадовільними!

2)

Ось результат за допомогою коду, наведеного тут . Це не добре працює!

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

3)

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

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

Задоволення умови, що сплайн передає точки:

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

4)

Ось моя реалізація "ідеї шубера", рядок за рядком в Python за його даними. Можливо, є деякі помилки, оскільки результати не гарні.

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

K = 2 - це катастрофа, і тому k> = 4.

5)

Я усунув одну точку в проблемному місці, і отриманий сплайнер тепер ідентичний похоротному. Але все ж питання, чому метод працює не у всіх випадках?

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

6)

Гарне вирівнювання даних whuber може бути таким (малюється програмним забезпеченням векторної графіки), в якому плавно додано додаткову точку (порівняйте з оновленням

4):

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

7)

Перегляньте результат від Python-версії коду whuber для деяких знакових фігур:

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

8) [мій останній]

Ось остаточне рішення (не ідеальне!):

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

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


як ви генеруєте контури "багатокутника"? Чи не завжди вони будуть лініями, оскільки контур, що перетинає край DEM, ніколи не закриється на собі?
фісташкувати

Я використав функцію v.generalize в GRASS, щоб зробити згладжування контурних ліній з гідними результатами, хоча це може зайняти деякий час для карт з дуже щільними контурами.
фісташкувати

@pistachionut Ви можете вважати, що рівні контуру є багатолінійними. Я шукаю чистий код на першому етапі. Якщо немає, тоді легкий пакет для Python.
Розробник

Можливо, подивіться на scipy.org/Cookbook/Інтерполяцію, бо це звучить так, як ви хочете
спланувати

1
Крива @ Пабло Безьє у вашому посиланні добре працює для поліліній. whuber працює майже добре для багатокутників. Тож вони разом могли вирішити питання. Дуже дякую, що безкоштовно поділилися своїми знаннями.
Розробник

Відповіді:


37

Більшість методів сплайнування послідовностей чисел будуть сплайгони багатокутників. Хитрість полягає в тому, щоб сплайни плавно «закрилися» на кінцевих точках. Для цього «оберніть» вершини навколо кінців. Потім сплайнуйте координати x- і y окремо.

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

#
# Splining a polygon.
#
#   The rows of 'xy' give coordinates of the boundary vertices, in order.
#   'vertices' is the number of spline vertices to create.
#              (Not all are used: some are clipped from the ends.)
#   'k' is the number of points to wrap around the ends to obtain
#       a smooth periodic spline.
#
#   Returns an array of points. 
# 
spline.poly <- function(xy, vertices, k=3, ...) {
    # Assert: xy is an n by 2 matrix with n >= k.

    # Wrap k vertices around each end.
    n <- dim(xy)[1]
    if (k >= 1) {
        data <- rbind(xy[(n-k+1):n,], xy, xy[1:k, ])
    } else {
        data <- xy
    }

    # Spline the x and y coordinates.
    data.spline <- spline(1:(n+2*k), data[,1], n=vertices, ...)
    x <- data.spline$x
    x1 <- data.spline$y
    x2 <- spline(1:(n+2*k), data[,2], n=vertices, ...)$y

    # Retain only the middle part.
    cbind(x1, x2)[k < x & x <= n+k, ]
}

Для ілюстрації його використання давайте створимо невеликий (але складний) багатокутник.

#
# Example polygon, randomly generated.
#
set.seed(17)
n.vertices <- 10
theta <- (runif(n.vertices) + 1:n.vertices - 1) * 2 * pi / n.vertices
r <- rgamma(n.vertices, shape=3)
xy <- cbind(cos(theta) * r, sin(theta) * r)

Розкладіть його за допомогою попереднього коду. Щоб сплайн був більш плавним, збільште кількість вершин з 100; щоб зробити його менш рівним, зменшіть кількість вершин.

s <- spline.poly(xy, 100, k=3)

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

plot(s, type="l", lwd=2, col="Gray")
lines(xy, col="Red", lty=2, lwd=2)
points(xy, col="Red", pch=19)
points(s, cex=0.8)
points(s[c(1,dim(s)[1]),], col="Blue", pch=19)

Спланований багатокутник


5
Гарна відповідь. Чи є спосіб гарантувати, що контури не закінчуються схрещуванням в результаті згладжування?
Кірк Куйкендалл

Це гарне запитання, @Kirk. Мені невідомий жоден спосіб гарантувати неперехід від цієї форми згладжування. (Насправді я навіть не бачу, як гарантувати, що згладжена полілінія не є самопересічною. Хоча це не є великою проблемою для більшості контурів.) Для цього вам потрібно повернутися до оригіналу DEM і замість цього використовувати кращий метод для обчислення контурів. (Там є кращі методи - вони були відомі в протягом тривалого часу - але AFAIK деякі з найбільш популярних GISes не використовувати їх.)
whuber

По-перше, я все ще працюю над тим, щоб реалізувати вашу відповідь у Python, але це не вдало. По-друге, що буде результатом, якщо застосувати свій метод на квадраті? Ви можете звернутися до тих, кого я намалював у питанні.
Розробник

1
Я прийняв це як відповідь, оскільки це дає хороше рішення. Незважаючи на те, що він не є ідеальним, але він дав мені кілька ідей, що працюють навколо, я сподіваюся, що я знайду рішення, яке задовольняє моменти, про які я згадував вище, у своєму питанні та коментарях. Ви також можете розглянути коментарі Ваубера до питання [QC], там є хороші хитрощі. Нарешті, я повинен сказати, що переклад на python майже очевидний із встановленням чудового пакету Scipy. Також розгляньте коментар Пабло в КК як можливе рішення для поліліній, тобто кривих Безьє. Успіхів усім.
Розробник

1
побачивши ваші відповіді, я шкодую, що не піклуюся про мою математику !!!
vinayan

2

Я знаю, що це стара публікація, але вона з’явилася в Google на щось, що я шукав, тому я подумав, що опублікую своє рішення.

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

Наступний екстракт iPython використовує кубічну інтерполяцію, надану SciPy. Зверніть увагу, що значення z, які я побудував, не важливі, якщо всі контури рівновіддалені по висоті.

In [1]: %pylab inline
        pylab.rcParams['figure.figsize'] = (10, 10)
        Populating the interactive namespace from numpy and matplotlib

In [2]: import scipy.interpolate as si

        xs = np.array([0.0, 0.0, 4.5, 4.5,
                       0.3, 1.5, 2.3, 3.8, 3.7, 2.3,
                       1.5, 2.2, 2.8, 2.2,
                       2.1, 2.2, 2.3])
        ys = np.array([0.0, 3.0, 3.0, 0.0,
                       1.1, 2.3, 2.5, 2.3, 1.1, 0.5,
                       1.1, 2.1, 1.1, 0.8,
                       1.1, 1.3, 1.1])
        zs = np.array([0,   0,   0,   0,
                       1,   1,   1,   1,   1,   1,
                       2,   2,   2,   2,
                       3,   3,   3])
        pts = np.array([xs, ys]).transpose()

        # set up a grid for us to resample onto
        nx, ny = (100, 100)
        xrange = np.linspace(np.min(xs[zs!=0])-0.1, np.max(xs[zs!=0])+0.1, nx)
        yrange = np.linspace(np.min(ys[zs!=0])-0.1, np.max(ys[zs!=0])+0.1, ny)
        xv, yv = np.meshgrid(xrange, yrange)
        ptv = np.array([xv, yv]).transpose()

        # interpolate over the grid
        out = si.griddata(pts, zs, ptv, method='cubic').transpose()

        def close(vals):
            return np.concatenate((vals, [vals[0]]))

        # plot the results
        levels = [1, 2, 3]
        plt.plot(close(xs[zs==1]), close(ys[zs==1]))
        plt.plot(close(xs[zs==2]), close(ys[zs==2]))
        plt.plot(close(xs[zs==3]), close(ys[zs==3]))
        plt.contour(xrange, yrange, out, levels)
        plt.show()

Кубічний інтерпольований результат

Результати тут виглядають не найкраще, але, маючи так мало контрольних точок, вони все ще цілком справедливі. Зверніть увагу, як витягнута зелена лінія виводиться на ширший синій контур.


Встановлені плавні криві повинні бути максимально наближеними до оригінального багатокутника / полілінії.
Розробник

1

Я майже точно написав той пакунок, який ви шукаєте ... але це було в Перлі, і було більше десяти років тому: GD :: Polyline . Він використовував 2D кубічні криві Безьє і "згладжував" довільний багатокутник або "Поліліній" (моє ім'я тоді для того, що зараз зазвичай називають "LineString").

Алгоритм був два кроки: задавши точки в Полігоні, додайте два контрольних точки Безьє між кожною точкою; потім зателефонуйте на простий алгоритм, щоб здійснити кусочне наближення сплайна.

Друга частина проста; перша частина була трохи мистецтва. Тут було прозріння: розглянути «сегмент управління» вершинного N: vN. Сегмент управління було три колінеарних точки: [cNa, vN, cNb]. Центральною точкою була вершина. Нахил цього контрольного сегмента дорівнював схилу від вершини N-1 до вершини N + 1. Довжина лівої частини цього відрізка становила 1/3 довжини від вершини N-1 до вершини N, а довжина правої частини цього відрізка становила 1/3 довжини від вершини N до вершини N + 1.

Якщо вихідна крива була чотири вершини: [v1, v2, v3, v4]то кожна вершина тепер отримати сегмент управління у вигляді: [c2a, v2, c2b]. Складіть їх разом так: [v1, c1b, c2a, v2, c2b, c3a, v3, c3b, c4a, v4]і зігніть їх чотири одночасно, як чотири точки Безьє:, [v1, c1b, c2a, v2]потім [v2, c2b, c3a, v3], і так далі. Оскільки [c2a, v2, c2b]були лінійними, то отримана крива буде гладкою в кожній вершині.

Таким чином, це також відповідає вашій вимозі параметризувати "герметичність" кривої: використовуйте меншу величину, ніж 1/3 для "більш жорсткої" кривої, більшу - для "петлі". У будь-якому випадку отримана крива завжди проходить через вихідні задані точки.

Це призвело до плавної кривої, яка "обписала" початковий Полігон. У мене також був якийсь спосіб "вписати" плавну криву ... але я цього не бачу в коді CPAN.

У будь-якому разі, у мене немає версії на Python, а також цифр не маю. Але ... якщо / коли я портую це на Python, я обов'язково опублікую тут.


Неможливо оцінити код Perl, додати графіку, щоб продемонструвати, як він працював, якщо це можливо.
Розробник
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.