Як обчислити площу 2d-многокутника?


81

Якщо припустити ряд точок у 2d-просторі, які не самоперетинаються, що є ефективним методом визначення площі результуючого многокутника?

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


6
Термін "площа поверхні" дещо вводить в оману. Вам здається, що вам потрібна лише (звичайна) область. У 3D площа поверхні - це площа зовнішньої поверхні, тому природним двовимірним узагальненням цього поняття буде довжина периметра багатокутника, що явно не те, що ви шукаєте.
batty

def area (polygon): return abs (numpy.cross (polygon, numpy.roll (polygon, -1, 0)). sum () / 2)
iouvxz

Відповіді:


111

Ось стандартний метод , AFAIK. В основному підсумовують перехресні добутки навколо кожної вершини. Набагато простіше, ніж тріангуляція.

Код Python, заданий багатокутником, представленим у вигляді списку (x, y) координат вершин, що неявно переносяться з останньої вершини на першу:

def area(p):
    return 0.5 * abs(sum(x0*y1 - x1*y0
                         for ((x0, y0), (x1, y1)) in segments(p)))

def segments(p):
    return zip(p, p[1:] + [p[0]])

Девід Лехаві коментує: Варто згадати, чому працює цей алгоритм: Це застосування теореми Гріна для функцій −y та x; точно так, як працює планіметр . Більш конкретно:

Формула вище =
integral_over_perimeter(-y dx + x dy) =
integral_over_area((-(-dy)/dy+dx/dx) dy dx) =
2 Area


7
Варто згадати, чому працює цей алгоритм: Це застосування теореми Гріна для функцій -y та x; точно так, як працює планіметр. Більш конкретно: Формула вище = інтегральний_пермітр (-y dx + x dy) = integral_area ((- (- dy) / dy + dx / dx) dydyx = 2 Площа
David Lehavi

6
Посилання в дописі мертве. Чи є у когось інше?
Яков

1
Обговорене у списку розсилки compgeom-discuss@research.bell-labs.com мені недоступне. Я скопіював повідомлення з кешу Google: gist.github.com/1200393
Ендрю Андрей Листочкин

2
@ perfectionm1ng перемикання напрямків переверне знак у сумі, але abs()позбавить знак.
Дарій Бекон,

3
Обмеження: Цей метод дасть неправильну відповідь для самопересічних багатокутників, де одна сторона перетинається з іншою, як показано праворуч. Однак він буде працювати правильно для трикутників, правильних і неправильних багатокутників, опуклих або увігнутих багатокутників. ( mathopenref.com/coordpolygonarea.html )
OneWorld

14

Крос-продукт - це класика.

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

area = 0;
for( i = 0; i < N; i += 2 )
   area += x[i+1]*(y[i+2]-y[i]) + y[i+1]*(x[i]-x[i+2]);
area /= 2;

Для наочності використовую індекс масиву. Більш ефективно використовувати покажчики. Хоча хороші компілятори зроблять це за вас.

Передбачається, що багатокутник "закритий", що означає, що ви копіюєте першу точку як точку з індексом N. Він також передбачає, що багатокутник має парну кількість точок. Додайте додаткову копію першого пункту, якщо N не парне.

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

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

EDIT: "Площа трикутників і багатокутників 2D & 3D" описує ще більш ефективний метод

// "close" polygon
x[N] = x[0];
x[N+1] = x[1];
y[N] = y[0];
y[N+1] = y[1];

// compute area
area = 0;
for( size_t i = 1; i <= N; ++i )
  area += x[i]*( y[i+1] - y[i-1] );
area /= 2;

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

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

2
Ви пропустили те, що додавання завжди має деякі від’ємні доданки через віднімання y. Розглянемо будь-яку 2d полігональну фігуру та порівняємо значення y послідовних вершин. Ви побачите, що деяке віднімання дасть негативне значення, а деяке позитивне.
chmike

2
Дійсно, цей останній абзац - це те, чого я не міг обговорити! При i <= N це працює. Дякую за терпіння, я беру все назад :)
Cygon

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

11

Ця сторінка показує, що формула

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

можна спростити до:

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

Якщо виписати кілька термінів і згрупувати їх за загальними факторами xi, то рівність не важко побачити.

Остаточне підсумовування є більш ефективним, оскільки воно вимагає лише nмноження замість 2n.

def area(x, y):
    return abs(sum(x[i] * (y[i + 1] - y[i - 1]) for i in xrange(-1, len(x) - 1))) / 2.0

Я дізнався про це спрощення від Джо Кінгтона тут .


Якщо у вас є NumPy, ця версія швидша (для всіх, крім дуже малих масивів):

def area_np(x, y):        
    x = np.asanyarray(x)
    y = np.asanyarray(y)
    n = len(x)
    shift_up = np.arange(-n+1, 1)
    shift_down = np.arange(-1, n-1)    
    return (x * (y.take(shift_up) - y.take(shift_down))).sum() / 2.0

1
Дякуємо за версію NumPy.
physicsmichael

4

Набір точок без будь-яких інших обмежень не обов'язково однозначно визначає багатокутник.

Отже, спочатку ви повинні вирішити, який багатокутник будувати з цих точок - можливо, опуклий корпус? http://en.wikipedia.org/wiki/Convex_hull

Потім триангулюйте і обчисліть площу. http://www.mathopenref.com/polygonirregulararea.html


4

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

Для загального багатокутника, що не перетинається, потрібно підсумувати перехресний добуток векторів (опорна точка, точка a), (опорна точка, точка b), де a і b знаходяться "поруч" один з одним.

Припускаючи, що у вас є список точок, які визначають багатокутник у порядку (порядок - точки i і i + 1 утворюють лінію багатокутника):

Сума (перехресний добуток ((точка 0, точка i), (точка 0, точка i + 1)) для i = 1 до n - 1.

Візьміть величину цього поперечного продукту, і у вас буде площа поверхні.

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


3

Для обчислення площі багатокутника

http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=geometry1#polygon_area

int cross(vct a,vct b,vct c)
{
    vct ab,bc;
    ab=b-a;
    bc=c-b;
    return ab.x*bc.y-ab.y*bc.x;
}    
double area(vct p[],int n)
{ 
    int ar=0;
    for(i=1;i+1<n;i++)
    {
        vct a=p[i]-p[0];
        vct b=p[i+1]-p[0];
        area+=cross(a,b);
    }
    return abs(area/2.0);
}    

Це 3-річне питання з 34 голосами за прийняту відповідь. Розкажіть, наскільки ваша відповідь краща за будь-яку іншу вже опубліковану відповідь.
Марк Тейлор,

3
Це приклад на c, а не python. Не краще, але приємно мати його на різних мовах
underdoeg

2

Або зробіть контурний інтеграл. Теорема Стокса дозволяє виразити інтеграл площі як інтегральний контур. Трохи квадратури Гауса, а Боб твій дядько.


2

мовне незалежне рішення:

ДАНО: багатокутник ЗАВЖДИ може складатися з n-2 трикутників, які не перекриваються (n = кількість точок АБО сторін). 1 трикутник = 3-сторонній многокутник = 1 трикутник; 1 квадрат = 4-сторонній многокутник = 2 трикутники; і т.д. оголошення нудота QED

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

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

  1. Отриманий трикутник знаходиться всередині оригінального багатокутника
  2. Отриманий трикутник повністю знаходиться поза вихідним багатокутником
  3. Отриманий трикутник частково міститься у вихідному багатокутнику

нас цікавлять лише випадки, які потрапляють у перший варіант (повністю міститься).

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

як реалізувати це програмно:

створити масив (послідовних) точок, що представляють шлях НАВКОЛО багатокутника. почати з точки 0. запустити масив, складаючи трикутники (по одному) з точок x, x + 1 та x + 2. перетворити кожен трикутник із фігури в область і перетинати його з площею, створеною з багатокутника. ЯКЩО отримане перетин ідентичне вихідному трикутнику, тоді згаданий трикутник повністю міститься в багатокутнику і може бути відрубаний. видалити x + 1 з масиву і почати знову з x = 0. в іншому випадку (якщо трикутник знаходиться поза [частково або повністю] багатокутником), перейдіть до наступної точки x + 1 у масиві.

крім того, якщо ви хочете інтегруватись із картографуванням і починаєте з геоточок, ви повинні ПЕРШИМ перейти з геоточок на екранні точки. для цього потрібно прийняти рішення про моделювання та формулу форми землі (хоча ми схильні думати про землю як про кулю, вона насправді є неправильною яйцевидною (яєчна форма), із вм'ятинами). Є багато моделей, для подальшої інформації wiki. важливим питанням є те, чи будете ви вважати область площиною чи криволінійною. загалом, "малі" ділянки, де точки знаходяться на відстані до декількох км, не призведуть до значних помилок, якщо вважати їх площинними та не опуклими.



1
  1. Встановіть базову точку (найбільш опуклу точку). Це буде ваша точка повороту трикутників.
  2. Обчисліть крайню ліву точку (довільну), крім базової точки.
  3. Обчисліть другу крайню ліву точку, щоб завершити свій трикутник.
  4. Збережіть цю трикутну область.
  5. Кожну ітерацію зміщуйте на одну точку вправо.
  6. Підсумуйте триангульовані площі

Переконайтеся, що ви заперечуєте область трикутника, якщо наступна точка рухається «назад».
рекурсивно

1

Краще, ніж підсумовувати трикутники, - це підсумовування трапецій у декартовому просторі:

area = 0;
for (i = 0; i < n; i++) {
  i1 = (i + 1) % n;
  area += (vertex[i].y + vertex[i1].y) * (vertex[i1].x - vertex[i].x) / 2.0;
}

1

Впровадження формули шнурка можна зробити в Numpy. Припускаючи ці вершини:

import numpy as np
x = np.arange(0,1,0.001)
y = np.sqrt(1-x**2)

Ми можемо визначити таку функцію для пошуку області:

def PolyArea(x,y):
    return 0.5*np.abs(np.dot(x,np.roll(y,1))-np.dot(y,np.roll(x,1)))

І отримання результатів:

print PolyArea(x,y)
# 0.26353377782163534

Цикл уникнення робить цю функцію в 50 разів швидшою, ніж PolygonArea:

%timeit PolyArea(x,y)
# 10000 loops, best of 3: 42 µs per loop
%timeit PolygonArea(zip(x,y))
# 100 loops, best of 3: 2.09 ms per loop

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


0

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

Візьмемо три послідовні точки, що складають багатокутник. Переконайтеся, що кут менше 180. Тепер у вас новий трикутник, який не складе труднощів обчислити, видаліть середню точку зі списку точок багатокутника. Повторюйте, поки у вас не залишиться лише три очки.


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

@Richard: Ось чому кваліфікація близько 180 градусів. Якщо відрізати трикутник поза багатокутником, у вас вийде занадто багато градусів.
Loren Pechtel

можливо, вам доведеться краще описати, як ви знаходите кут. У геометрії площини немає можливості мати 3 точки як частину трикутника і мати будь-який кут або комбінацію кутів, що перевищують 180 градусів - перевірка здається безглуздою.
Річард

@Richard: На вашому багатокутнику у вас кут кожного переходу. Якщо відповідний трикутник лежатиме поза багатокутником, кут між двома відрізками буде більшим за 180 градусів.
Loren Pechtel

Ви маєте на увазі, що внутрішній кут двох сусідніх сегментів краю буде більше 180 градусів.
Річард

0

C спосіб зробити це:

float areaForPoly(const int numVerts, const Point *verts)
{
    Point v2;
    float area = 0.0f;

    for (int i = 0; i<numVerts; i++){
        v2 = verts[(i + 1) % numVerts];
        area += verts[i].x*v2.y - verts[i].y*v2.x;
    }

    return area / 2.0f;
}

0

Код Python

Як описано тут: http://www.wikihow.com/Calculate-the-Area-of-a-Polygon

З пандами

import pandas as pd

df = pd.DataFrame({'x': [10, 20, 20, 30, 20, 10, 0], 'y': [-10, -10, -10, 0, 10, 30, 20]})
df = df.append(df.loc[0])

first_product = (df['x'].shift(1) * df['y']).fillna(0).sum()
second_product = (df['y'].shift(1) * df['x']).fillna(0).sum()

(first_product - second_product) / 2
600

0

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

//don't forget to include cmath for abs function
struct Point{
  double x;
  double y;
}
// cross_product
double cp(Point a, Point b){ //returns cross product
  return a.x*b.y-a.y*b.x;
}

double area(Point * vertices, int n){  //n is number of sides
  double sum=0.0;
  for(i=0; i<n; i++){
    sum+=cp(vertices[i], vertices[(i+1)%n]); //%n is for last triangle
  }
  return abs(sum)/2.0;
}

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