2d çokgenin alanını nasıl hesaplarım?


81

2 boyutlu uzayda kendisiyle kesişmeyen bir dizi nokta varsayarsak, ortaya çıkan çokgenin alanını belirlemenin etkili bir yöntemi nedir?

Bir yan not olarak, bu bir ev ödevi değil ve kod aramıyorum. Kendi yöntemimi uygulamak için kullanabileceğim bir açıklama arıyorum. Noktalar listesinden bir dizi üçgen çekmeye dair fikirlerim var, ancak dışbükey ve içbükey çokgenlerle ilgili muhtemelen yakalayamayacağım bir dizi uç durum olduğunu biliyorum.


6
"Yüzey alanı" terimi biraz yanıltıcıdır. İstediğiniz gibi görünen şey sadece (normal) alandır. 3B'de yüzey alanı, dış yüzeyin alanıdır, bu nedenle bu kavramın doğal 2B genellemesi, çokgenin çevresinin uzunluğu olacaktır, ki bu açıkça aradığınız şey değildir.
batty

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

Yanıtlar:


111

İşte standart yöntem , AFAIK. Temel olarak her köşe etrafındaki çapraz çarpımları toplayın. Üçgenlemeden çok daha basit.

Python kodu, (x, y) köşe koordinatlarının bir listesi olarak temsil edilen bir çokgen verildiğinde, örtük olarak son tepe noktasından ilkine sarılır:

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]])

David Lehavi şöyle yorumluyor: Bu algoritmanın neden çalıştığından bahsetmeye değer: Bu, Green teoreminin −y ve x fonksiyonları için bir uygulamasıdır ; tam olarak bir planimetrenin çalıştığı şekilde. Daha spesifik olarak:

Yukarıdaki formül =
integral_over_perimeter(-y dx + x dy) =
integral_over_area((-(-dy)/dy+dx/dx) dy dx) =
2 Area


7
Bu algoritmanın neden çalıştığını belirtmek gerekir: -y ve x fonksiyonları için Green teoreminin bir uygulamasıdır; tam olarak bir planimetrenin çalıştığı şekilde. Daha spesifik olarak: Yukarıdaki formül = integral_permieter (-y dx + x dy) = integral_area ((- (- dy) / dy + dx / dx) dydyx = 2 Area
David Lehavi

6
Gönderideki bağlantı kesildi, başkası olan var mı?
Yakov

1
Compgeom-discuss@research.bell-labs.com posta listesindeki bağlantılı tartışma benim için uygun değil. İletiyi Google Cache'den kopyaladım: gist.github.com/1200393
Andrew Андрей Листочкин

2
@ perfectionm1ng değiştirme yönleri, toplamdaki işareti ters çevirir, ancak abs()oturumu kapatır.
Darius Bacon

3
Sınırlamalar: Bu yöntem, sağda gösterildiği gibi, bir tarafın diğerinin üzerine geçtiği kendisiyle kesişen çokgenler için yanlış yanıt üretecektir. Bununla birlikte, üçgenler, düzenli ve düzensiz çokgenler, dışbükey veya içbükey çokgenler için doğru şekilde çalışacaktır. ( mathopenref.com/coordpolygonarea.html )
OneWorld

14

Çapraz çarpım bir klasiktir.

Yapmanız gereken zilyonlarca hesaplama varsa, yarı daha az çarpma gerektiren aşağıdaki optimize edilmiş sürümü deneyin:

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;

Netlik için dizi alt simge kullanıyorum. İşaretçileri kullanmak daha etkilidir. İyi derleyiciler sizin için yapacak olsa da.

Çokgenin "kapalı" olduğu varsayılır, bu da ilk noktayı alt simge N ile nokta olarak kopyaladığınız anlamına gelir. Ayrıca çokgenin çift sayıda noktaya sahip olduğunu varsayar. N çift değilse ilk noktanın ek bir kopyasını ekleyin.

Algoritma, klasik çapraz çarpım algoritmasının iki ardışık yinelemesini açıp birleştirerek elde edilir.

İki algoritmanın sayısal kesinlik açısından nasıl karşılaştırıldığından pek emin değilim. Benim izlenimim, yukarıdaki algoritmanın klasik olandan daha iyi olduğu, çünkü çarpma işleminin, çıkarma hassasiyetindeki kaybı geri yükleme eğiliminde olduğu yönünde. GPU'da olduğu gibi kayan nokta kullanmakla sınırlandırıldığında, bu önemli bir fark yaratabilir.

DÜZENLEME: "Üçgenler ve Çokgenler 2B ve 3B Alanı" daha da verimli bir yöntemi anlatıyor

// "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
İkinci kod parçacığının çalışacağını hayal edemiyorum. Çokgenin X ekseninde ne kadar uzaksa, alanının o kadar büyük olacağı açıktır.
Cygon

1
Yukarıda açıklanan algoritmanın doğru bir matematiksel yeniden düzenlemesidir ve bazı çarpımlardan tasarruf sağlar. Haklısın, ancak diğer köşelerin tanımladığı alanlar çıkarılacak. Ancak bu gerçekten hassas bir bozulmaya yol açabilir.
chmike

2
Göz ardı ettiğiniz şey, toplamanın y çıkarımı nedeniyle her zaman bazı olumsuz terimlere sahip olduğudur. Herhangi bir 2b poligonal şekli düşünün ve ardışık tepe noktalarının y değerlerini karşılaştırın. Bazı çıkarmaların negatif bir değer ve bazılarının da pozitif vereceğini göreceksiniz.
chmike

2
Doğrusu, bu son paragraf aklımı başıma toplayamadığım şeydi! İ <= N ile çalışır. Sabrınız için teşekkürler, her şeyi geri
alıyorum

1
Bir yan notta, algoritmanın döndürdüğü alan "işaretlidir" (noktaların sırasına göre negatif veya pozitif), bu nedenle her zaman pozitif alan istiyorsanız sadece mutlak değeri kullanın.
NightElfik

11

Bu sayfa , formülün

görüntü açıklamasını buraya girin

basitleştirilebilir:

görüntü açıklamasını buraya girin

Birkaç terim yazıp bunları ortak faktörlere göre gruplandırırsanız xi, eşitliği görmek zor değildir.

Bunun nyerine sadece çarpma gerektirdiğinden, nihai toplama daha etkilidir 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

Bu sadeleştirmeyi burada Joe Kington'dan öğrendim .


NumPy'niz varsa, bu sürüm daha hızlıdır (çok küçük diziler hariç tümü için):

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 sürümü için teşekkürler.
physicsmichael


4

Üçgen ve toplam üçgen alanlarını genişletmek için, dışbükey bir çokgeniniz varsa VEYA çokgeni kesişen diğer tüm noktalara çizgiler oluşturmayan bir nokta seçerseniz bunlar çalışır.

Kesişmeyen genel bir çokgen için, a ve b'nin "yan yana" olduğu vektörlerin (referans noktası, nokta a), (referans noktası, b noktası) çapraz çarpımını toplamanız gerekir.

Poligonu sırayla tanımlayan bir nokta listesine sahip olduğunuzu varsayarsak (i ​​ve i + 1 noktaları çokgenin bir çizgisini oluşturur):

Toplam (çapraz çarpım ((nokta 0, nokta i), (nokta 0, nokta i + 1)) için i = 1 ila n - 1.

Bu çapraz çarpımın büyüklüğünü alın ve yüzey alanına sahipsiniz.

Bu, iyi bir referans noktası seçme konusunda endişelenmenize gerek kalmadan içbükey çokgenleri işleyecektir; Çokgenin içinde olmayan bir üçgen oluşturan herhangi üç noktanın, çokgenin içindeki herhangi bir üçgenin ters yönünü gösteren bir çarpı çarpımı olacaktır, böylece alanlar doğru bir şekilde toplanır.


3

Poligon alanını hesaplamak için

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);
}    

Bu, kabul edilen cevaba 34 olumlu oy veren 3 yıllık bir sorudur. Cevabınızın daha önce gönderilmiş olan diğer cevaplardan nasıl daha iyi olduğunu bize söyleyin.
Mark Taylor

3
Python değil, c'de bir örnektir. Farklı dillerde olması daha iyi değil ama güzel
Underdoeg

2

Veya bir kontur integrali yapın. Stokes Teoremi, bir alan integralini kontur integrali olarak ifade etmenize izin verir. Biraz Gauss karesi ve Bob senin amcan.


2

dilden bağımsız çözüm:

VERİLEN: Bir çokgen HER ZAMAN üst üste binmeyen n-2 üçgenlerden oluşabilir (n = nokta sayısı VEYA kenar). 1 üçgen = 3 kenarlı çokgen = 1 üçgen; 1 kare = 4 kenarlı çokgen = 2 üçgen; vb reklam mide bulantısı QED

bu nedenle, bir çokgen, üçgenler "kesilerek" küçültülebilir ve toplam alan, bu üçgenlerin alanlarının toplamı olacaktır. bir parça kağıt ve makasla deneyin, en iyisi süreci takip etmeden önce görselleştirmenizdir.

Bir çokgen yolundaki 3 ardışık noktayı alır ve bu noktalarla bir üçgen oluşturursanız, üç olası senaryodan birine ve yalnızca birine sahip olursunuz:

  1. ortaya çıkan üçgen tamamen orijinal çokgenin içinde
  2. ortaya çıkan üçgen tamamen orijinal poligonun dışında
  3. ortaya çıkan üçgen kısmen orijinal çokgende yer alır

biz sadece birinci seçeneğe (tamamen kapsanan) giren durumlarla ilgileniyoruz.

bunlardan birini her bulduğumuzda, onu keseriz, alanını hesaplarız (kolay peasy, burada formülü açıklamayız) ve bir kenarı eksik olan yeni bir çokgen (bu üçgen kesik çokgene eşdeğer) yaparız. tek bir üçgenimiz kalana kadar.

bu programatik olarak nasıl uygulanır:

çokgenin ETRAFINDAKİ yolu temsil eden bir dizi (ardışık) nokta oluşturun. 0. noktasından başlayın. x, x + 1 ve x + 2 noktalarından üçgenler oluşturan diziyi (birer birer) çalıştırın. her üçgeni bir şekilden bir alana dönüştürün ve onu çokgenden oluşturulan alanla kesiştirin. Ortaya çıkan kesişme orijinal üçgenle aynıysa, bu durumda söz konusu üçgen tamamen çokgende yer alır ve kesilebilir. x + 1'i diziden çıkarın ve x = 0'dan yeniden başlayın. aksi takdirde (üçgen [kısmen veya tamamen] çokgenin dışındaysa), dizide bir sonraki x + 1 noktasına gidin.

ek olarak haritalama ile entegre etmek istiyorsanız ve coğrafi noktalardan başlıyorsanız, ÖNCE coğrafi noktalardan ekran noktalarına geçiş yapmalısınız. bu, yeryüzünün şekli için bir modelleme ve formül belirlemeyi gerektirir (dünyayı bir küre olarak düşünme eğiliminde olsak da, aslında bu, girintili düzensiz bir ovaldir (yumurta şekli)). Daha fazla bilgi wiki için birçok model var. Önemli bir konu, alanı bir düzlem olarak mı yoksa kavisli olarak mı değerlendireceğinizdir. genel olarak, noktaların birbirinden birkaç km uzakta olduğu "küçük" alanlar, dışbükey değil düzlemsel olarak kabul edilirse önemli bir hata oluşturmayacaktır.



1
  1. Bir taban noktası (en dışbükey nokta) ayarlayın. Bu, üçgenlerin eksen noktası olacak.
  2. Taban noktanızın dışındaki en soldaki noktayı (keyfi) hesaplayın.
  3. Üçgeninizi tamamlamak için en soldaki 2. noktayı hesaplayın.
  4. Bu üçgenleştirilmiş alanı kaydedin.
  5. Her yinelemede sağa doğru bir nokta kaydırın.
  6. Üçgen alanların toplanması

Sonraki nokta "geriye doğru" hareket ediyorsa, üçgen alanını yadsdığınızdan emin olun.
yinelemeli

1

Üçgenleri toplamaktan daha iyisi, Kartezyen uzayda yamukların toplamıdır:

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

Ayakkabı bağı formülünün uygulanması Numpy'de yapılabilir. Bu köşeleri varsayarsak:

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

Alan bulmak için aşağıdaki işlevi tanımlayabiliriz:

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

Ve sonuçlar alıyorum:

print PolyArea(x,y)
# 0.26353377782163534

Döngüden kaçınmak, bu işlevi şunlardan ~ 50 kat daha hızlı hale getirir 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

Not: Bu cevabı başka bir soru için yazdım , tam bir çözüm listesine sahip olmak için burada söz ettim.


0

Eğilim, basitçe üçgenleri kesmeye başlamak olurdu. Başka herhangi bir şeyin aşırı derecede kıllı olmaktan nasıl kaçabileceğini anlamıyorum.

Çokgeni oluşturan üç ardışık noktayı alın. Açının 180'den küçük olduğundan emin olun. Artık hesaplamakta sorun olmayacak yeni bir üçgene sahipsiniz, orta noktayı çokgen nokta listesinden silin. Sadece üç puanınız kalana kadar tekrarlayın.


Bununla ilgili tüylü kısım, ardışık üç noktanız çokgenin dışında veya kısmen dışında bir üçgeni tanımlıyorsa, o zaman bir probleminiz olmasıdır.
Richard

@Richard: Bu yüzden 180 derece hakkında yeterlilik. Çokgenin dışındaki bir üçgeni bölerseniz, çok fazla derece elde edersiniz.
Loren Pechtel

açıyı nasıl bulduğunuzu daha iyi tanımlamanız gerekebilir. Düzlem geometrisinde bir üçgenin parçası olarak 3 noktaya sahip olmanın ve herhangi bir açı veya açı kombinasyonunun 180 dereceyi aşmasının bir yolu yoktur - kontrol anlamsız görünecektir.
Richard

@Richard: Poligonunuzda her kavşağın açısına sahipsiniz. İlgili üçgen poligonun dışında kalıyorsa, iki segment arasındaki açı 180 dereceden büyük olacaktır.
Loren Pechtel

İki bitişik kenar parçasının iç açısının 180 dereceden daha büyük olacağını söylüyorsunuz.
Richard

0

Bunu yapmanın C yolu:

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 kodu

Burada açıklandığı gibi: http://www.wikihow.com/Calculate-the-Area-of-a-Polygon

Pandalarla

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 çokgenin alanını hesaplamak için birkaç basit fonksiyon vereceğim. Bu, hem dışbükey hem de içbükey çokgenler için işe yarar. çokgeni basitçe birçok alt üçgene böleriz.

//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;
}

cpiki argüman alır, yine de biriyle çağırıyorsunuz.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.