Poligonun ortalama genişliğinin hesaplanması? [kapalı]


40

Yol yüzeyini temsil eden bir poligonun ortalama genişliğini incelemekle ilgileniyorum. Ayrıca, yol merkez çizgisini bir vektör olarak görüyorum (bu bazen tam olarak merkezde değil). Bu örnekte, yol merkez çizgisi kırmızıdır ve çokgen mavidir:

görüntü tanımını buraya girin

Düşündüğüm kaba kuvvet yaklaşımı, çizgiyi küçük artışlarla tamponlamak, tamponu bir file ızgarayla kesişmek, yol çokgenini bir file ızgarayla kesişmek, kesişen alanı hem kesişim önlemleri için hesaplamak hem de bunu yapmaya devam etmektir. hata küçük. Bu kaba bir yaklaşım olsa da, daha zarif bir çözüm olup olmadığını merak ediyorum. Ayrıca, bu, büyük bir yolun ve küçük bir yolun genişliğini de gizler.

ArcGIS 10, PostGIS 2.0 veya QGIS yazılımı kullanan bir çözümle ilgileniyorum. Bu soruyu gördüm ve Dan Patterson’un ArcGIS10’a yönelik aracını indirdim ancak ne istediğimi hesaplayamadım.

Aşağıdaki yeşil poligonları üretmemi sağlayan ArcGIS 10'da Minimum Sınırlama Geometrisi aracını yeni keşfettim :

görüntü tanımını buraya girin

Bu, şebekeyi izleyen yollar için iyi bir çözüm gibi görünüyor, ancak başka türlü işe yaramayacaktı, bu yüzden diğer önerilerle de ilgileniyorum.


Kenar çubuğundaki olası çözümleri eklediniz mi? yani, gis.stackexchange.com/questions/2880/… görünüşte potansiyel olarak yinelenen bir gönderiye önemsiz bir cevap olarak işaretlendi

@DanPatterson Böyle bir soru görmedim (elbette çoğu ilişkili). Sorumun işaretlendiğini mi demek istediniz? İkinci sıranı anlamadım.
djq

Bu ilgili soru olan @Dan, "genişliğin" farklı bir yorumuyla ilgilidir (aslında, yorum tamamen net değildir). Cevaplar ortalama genişlikten ziyade en geniş noktada genişliği bulmaya odaklanmış gibi görünüyor .
whuber

@Whuber burada merkezileştirme istediğimizden tartışmalar, başka soruları kapanış, ben soru insanlara düzenlenmiş "anlamaya olduğunu düşündürmektedir dikdörtgen şeridinin ortalama genişliği kestirimini ile "
Peter Krauss

@Peter: dikdörtgen şeklinde bir şerittir olarak daha ziyade bir çokgen, daha genel başlıktaki beklemeye gerek.
whuber

Yanıtlar:


40

Sorunun bir kısmı “ortalama genişlik” için uygun bir tanım bulmak. Birçoğu doğaldır ancak en azından biraz farklı olacaktır. Basit olması için, hesaplanması kolay özelliklere dayalı tanımları göz önünde bulundurun (örneğin, medial eksen dönüşümü veya arabellek dizilerine dayalı olanları ekarte edecek).

Bir örnek olarak, belirli bir "genişliğe" sahip bir poligonun arketipsel sezgisinin, uzun, oldukça düz bir polyline ( L uzunluğu için) etrafında küçük bir tampon ( kare uçlu r yarıçapı sayılır) olduğunu düşünün . 2r = w'nin genişliği olduğunu düşünüyoruz . Böylece:

  • Çevresi P yaklaşık olarak 2L + 2w'ye eşittir;

  • A alanı yaklaşık olarak L'ye eşittir.

Genişlik w ve uzunluk L , ikinci dereceden x ^ 2 - (P / 2) x + A'nın kökleri olarak elde edilebilir; özellikle tahmin edebiliriz

  • w = (P - Sqrt (P ^ 2 - 16A)) / 4 .

Çokgenin gerçekten uzun ve sıska olduğundan emin olduğunuzda, bir başka yaklaşım olarak 2L + 2w'yi 2L'ye eşit olarak alabilirsiniz.

  • w (kabaca) = 2A / P.

Bu yaklaşımdaki nispi hata, w / L ile orantılıdır: poligon ne kadar ince olursa, w / L sıfıra yaklaşır ve yaklaşım ne kadar iyi olursa o kadar iyidir.

Bu yaklaşım sadece son derece basit olmakla kalmaz (sadece alanı çevre ile bölün ve 2 ile çarpın), her iki formülle de çokgenin nasıl yönlendirildiği veya nereye yerleştirildiği önemli değildir (çünkü bu Öklid hareketleri bölgeyi ve bölgeyi değiştirmez. çevre uzunluğu).

Sokak kesimlerini temsil eden tüm çokgenler için ortalama genişliği tahmin etmek üzere bu formüllerden birini kullanmayı düşünebilirsiniz . Eğer orijinal tahmini yapmak hata w (kuadratik formülü ile) alan nedeniyle yaklaşık geliyor bir de orijinal Çoklu çizginin her viraj minik takozlar içerir. Bükülme açılarının toplamı t radyan ise (bu, polinin toplam mutlak eğriliğidir), o zaman gerçekten

  • P = 2L + 2w + 2 Pi tw ve

  • A = L w + Pi tw ^ 2.

Bunları önceki (ikinci dereceden formül) çözümüne takın ve basitleştirin. Duman temizlendiğinde, eğrilik teriminin t katkısı kayboldu! Aslen bir yaklaşım gibi görünen şey, kendinden kesişmeyen polineri tamponları (kare uçlu) için kusursuz bir şekilde doğrudur. Değişken genişlikteki çokgenler için bu, ortalama genişliğin makul bir tanımıdır .


Teşekkürler @whuber bu harika bir cevap ve bu konuda çok daha net düşünmeme yardımcı oldu.
djq

@whuber: Bir makale yazıyorum ve burada tanımladığınız yönteme uygun ('akademik') bir referans vermem gerekiyor. Böyle bir referansın var mı? Bu önlemin bir adı var mı? Eğer değilse, sizden sonra adlandırabilirim! Peki ya "Huber'in genişlik ölçüsü"?
julien

@julien Herhangi bir referansım yok. Bu biçim işe yarayabilir: MISC {20279, TITLE = {Ortalama çokgen genişliğinin hesaplanması?}, AUTHOR = {whuber ( gis.stackexchange.com/users/664/whuber )}, HOWPUBLISHED = {GIS}, NOTE = {URL: gis.stackexchange.com/q/20279/664 (sürüm: 2013-08-13)}, EPRINT = { gis.stackexchange.com/q/20279 }, URL = { gis.stackexchange.com/q/20279 }}
whuber

18

Burada @whuber çözümü hakkında çok az optimizasyon gösteriyorum ve "tampon genişliği" terimini kullanıyorum, çünkü daha genel bir sorunun çözümünü bütünleştirmek için kullanışlıdır: genişlik tahmini veren bir st_buffer ters fonksiyonu var mı?

CREATE FUNCTION buffer_width(
        -- rectangular strip mean width estimator
    p_len float,   -- len of the central line of g
    p_geom geometry, -- g
    p_btype varchar DEFAULT 'endcap=flat' -- st_buffer() parameter
) RETURNS float AS $f$
  DECLARE
    w_half float;
    w float;    
  BEGIN
         w_half := 0.25*ST_Area(p_geom)/p_len;
         w      := 0.50*ST_Area( ST_Buffer(p_geom,-w_half,p_btype) )/(p_len-2.0*w_half);
     RETURN w_half+w;
  END
$f$ LANGUAGE plpgsql IMMUTABLE;

Bu sorun için, yaklaşık @celenius soru sokak genişliği , sw, çözüm

 sw = buffer_width(ST_Length(g1), g2)

Burada sw"ortalama genişlik", g1orta hat g2ve cadde g2bir POLYGON'dur . Yalnızca PostGIS ile test edilen OGC standart kütüphanesini kullandım ve aynı tampon_width işleviyle diğer ciddi pratik uygulamaları çözdüm.

GÖSTERİ

A2alanıdır g2, L1(merkezi çizgisinin uzunluğu g1) arasında g2.

Biz üretebilir olduğu düşünüldüğünde g2ile g2=ST_Buffer(g1,w), ve bu g1bir düz, yani g2uzunluğu olan bir dikdörtgendir L1ve genişliği 2*w, ve

    A2 = L1*(2*w)   -->  w = 0.5*A2/L1

@Whuber ile aynı formül değil, çünkü burada wrectangle ( g2) genişliğinin yarısı var . İyi bir tahmincidir, ancak testlerle görebileceğimiz gibi (aşağıda) kesin değildir ve işlev g2alanı azaltmak için ve nihai bir tahminci olarak bunu bir ipucu olarak kullanır .

Burada "endcap = square" veya "endcap = round" olan tamponları değerlendirmiyoruz, bununla A2 aynı olan bir nokta tamponu alanının toplamına ihtiyaç var w.

KAYNAKLAR: 2005 yılının benzer bir forumunda , W. Huber, bunun gibi ve diğer çözümleri açıklar.

TESTLER VE NEDENLER

Düz çizgiler için sonuçlar beklendiği gibi kesindir. Ancak diğer geometriler için sonuçlar hayal kırıklığı yaratabilir. Asıl sebep, belki de tüm model tam dikdörtgenler veya "şerit dikdörtgene" yaklaşılabilen geometriler içindir. İşte bu yaklaşımın sınırlarını kontrol etmek için bir "test kiti" ( wfactoryukarıdaki sonuçlara bakınız).

 SELECT *, round(100.0*(w_estim-w)/w,1) as estim_perc_error
    FROM (
        SELECT btype, round(len,1) AS len, w, round(w/len,3) AS wfactor,
               round(  buffer_width(len, gbase, btype)  ,2) as w_estim ,
               round(  0.5*ST_Area(gbase)/len       ,2) as w_near
        FROM (
         SELECT
            *, st_length(g) AS len, ST_Buffer(g, w, btype) AS gbase
         FROM (
               -- SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g, -- straight
               SELECT ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g,
            unnest(array[1.0,10.0,20.0,50.0]) AS w
              ) AS t, 
             (SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
             ) AS t2
        ) as t3
    ) as t4;

SONUÇLAR:

Dikdörtgenler (merkez hattı düz bir çizgidir):

         btype          |  len  |  w   | wfactor | w_estim | w_near | estim_perc_error 
------------------------+-------+------+---------+---------+--------+------------------
 endcap=flat            | 141.4 |  1.0 |   0.007 |       1 |      1 |                0
 endcap=flat join=bevel | 141.4 |  1.0 |   0.007 |       1 |      1 |                0
 endcap=flat            | 141.4 | 10.0 |   0.071 |      10 |     10 |                0
 endcap=flat join=bevel | 141.4 | 10.0 |   0.071 |      10 |     10 |                0
 endcap=flat            | 141.4 | 20.0 |   0.141 |      20 |     20 |                0
 endcap=flat join=bevel | 141.4 | 20.0 |   0.141 |      20 |     20 |                0
 endcap=flat            | 141.4 | 50.0 |   0.354 |      50 |     50 |                0
 endcap=flat join=bevel | 141.4 | 50.0 |   0.354 |      50 |     50 |                0

DİĞER GEOMETRİLERLE (merkez hattı katlanmış):

         btype          | len |  w   | wfactor | w_estim | w_near | estim_perc_error 
 -----------------------+-----+------+---------+---------+--------+------------------
 endcap=flat            | 465 |  1.0 |   0.002 |       1 |      1 |                0
 endcap=flat join=bevel | 465 |  1.0 |   0.002 |       1 |   0.99 |                0
 endcap=flat            | 465 | 10.0 |   0.022 |    9.98 |   9.55 |             -0.2
 endcap=flat join=bevel | 465 | 10.0 |   0.022 |    9.88 |   9.35 |             -1.2
 endcap=flat            | 465 | 20.0 |   0.043 |   19.83 |  18.22 |             -0.9
 endcap=flat join=bevel | 465 | 20.0 |   0.043 |   19.33 |  17.39 |             -3.4
 endcap=flat            | 465 | 50.0 |   0.108 |   46.29 |  40.47 |             -7.4
 endcap=flat join=bevel | 465 | 50.0 |   0.108 |   41.76 |  36.65 |            -16.5

 wfactor= w/len
 w_near = 0.5*area/len
 w_estim is the proposed estimator, the buffer_width function.

Hakkında iyi bir ilustratinler ve burada kullanılan LINESTRING'ler içeren ST_Buffer rehberinebtype bakınız .

SONUÇLAR :

  • tahmincisi w_estimdaima daha iyidir w_near;
  • "dikdörtgene yakın" g2geometriler için, tamamwfactor
  • Başka bir geometri için ("dikdörtgen şeritlerin" yakınında), wfactor=~0.01hatanın% 1'i için sınırı kullanın w_estim. Bu wfactor'a kadar başka bir tahmin cihazı kullanın.

Dikkat ve önleme

Tahmin hatası neden oluşur? Kullandığınızda ST_Buffer(g,w), "dikdörtgen şerit modeli" ile genişlik tamponu tarafından eklenen yeni alanın wyaklaşık w*ST_Length(g)veya w*ST_Perimeter(g)... olacağını beklersiniz . ortalama whatanın tahmini . Bu, testlerin ana mesajıdır.

Bu sorunu herhangi bir tampon kralı için tespit etmek için, tampon üretim davranışını kontrol edin:

SELECT btype, w, round(100.0*(a1-len1*2.0*w)/a1)::varchar||'%' AS straight_error,  
                 round(100.0*(a2-len2*2.0*w)/a2)::varchar||'%' AS curve2_error,
                 round(100.0*(a3-len3*2.0*w)/a3)::varchar||'%' AS curve3_error
FROM (
 SELECT
    *, st_length(g1) AS len1, ST_Area(ST_Buffer(g1, w, btype)) AS a1,
    st_length(g2) AS len2, ST_Area(ST_Buffer(g2, w, btype)) AS a2,
    st_length(g3) AS len3, ST_Area(ST_Buffer(g3, w, btype)) AS a3
 FROM (
       SELECT ST_GeomFromText('LINESTRING(50 50,150 150)') AS g1, -- straight
              ST_GeomFromText('LINESTRING(50 50,150 150,150 50)') AS g2,
              ST_GeomFromText('LINESTRING(50 50,150 150,150 50,250 250)') AS g3,
              unnest(array[1.0,20.0,50.0]) AS w
      ) AS t, 
     (SELECT unnest(array['endcap=flat','endcap=flat join=bevel']) AS btype
     ) AS t2
) as t3;

SONUÇLAR:

         btype          |  w   | straight_error | curve2_error | curve3_error 
------------------------+------+----------------+--------------+--------------
 endcap=flat            |  1.0 | 0%             | -0%          | -0%
 endcap=flat join=bevel |  1.0 | 0%             | -0%          | -1%
 endcap=flat            | 20.0 | 0%             | -5%          | -10%
 endcap=flat join=bevel | 20.0 | 0%             | -9%          | -15%
 endcap=flat            | 50.0 | 0%             | -14%         | -24%
 endcap=flat join=bevel | 50.0 | 0%             | -26%         | -36%

        Alarm


13

Çokgen verilerinizi merkez çizgisi verilerinize ekleyebiliyorsanız (uzamsal veya sekmeli yollarla), her merkez çizgisi hizalaması için çokgen alanlarını toplayın ve merkez çizgisinin uzunluğuna bölün.


bu doğru! Bu durumda, merkez çizgilerim aynı uzunlukta değil, ancak her zaman bir olarak birleştirebilirim ve çokgen başına bölerdim.
djq

Verileriniz postgreSQL / postGIS içindeyse ve merkez çizgileri ve çokgenler için bir sokak kimliği alanınız varsa, birleştirme / bölmeye gerek yoktur ve toplama işlevlerini kullanarak yanıtınız yalnızca bir sorgulamadır. SQL'de yavaşım, yoksa bir örnek gönderirdim. Nasıl çözeceğinize dair bana haber verin, ve çözmenize yardım edeceğim (gerekirse).
Scro

Teşekkürler Scro, şu anda PostGIS’de değil, ancak yüklenmesi çok hızlı. Sanırım ilk önce @ whuber’un yaklaşımını deneyeceğim, ancak bunu PostGIS’in sonuçlarıyla karşılaştıracağım (ve SQL yardım teklifi için teşekkür ederim, ama ben yönetebilmeli). Temelde ilk önce kafamdaki yaklaşımı netleştirmeye çalışıyorum.
djq

+1 Bu mevcut durumlar için güzel ve basit bir çözüm.
whuber

9

Bir poligonun ortalama genişliği için bir formül geliştirdim ve bir Python / ArcPy fonksiyonuna koydum. Formülüm, başka bir yerde tartıştığım en geniş ortalama genişlik kavramından; yani, çokgeninizle aynı alana sahip olan bir dairenin çapı. Ancak, yukarıdaki soruda ve projemde, en dar eksen genişliğine daha çok ilgi duydum. Ayrıca, potansiyel olarak karmaşık, dışbükey olmayan şekiller için ortalama genişliğe ilgi duydum.

Benim çözümüm:

(perimeter / pi) * area / (perimeter**2 / (4*pi))
= 4 * area / perimeter

Yani:

(Diameter of a circle with the same perimeter as the polygon) * Area / (Area of a circle with the same perimeter as the polygon)

İşlev:

def add_average_width(featureClass, averageWidthField='Width'):
    '''
    (str, [str]) -> str

    Calculate the average width of each feature in the feature class. The width
        is reported in units of the feature class' projected coordinate systems'
        linear unit.

    Returns the name of the field that is populated with the feature widths.
    '''
    import arcpy
    from math import pi

    # Add the width field, if necessary
    fns = [i.name.lower() for i in arcpy.ListFields(featureClass)]
    if averageWidthField.lower() not in fns:
        arcpy.AddField_management(featureClass, averageWidthField, 'DOUBLE')

    fnsCur = ['SHAPE@LENGTH', 'SHAPE@AREA', averageWidthField]
    with arcpy.da.UpdateCursor(featureClass, fnsCur) as cur:
        for row in cur:
            perim, area, width = row
            row[-1] = ((perim/pi) * area) / (perim**2 / (4 * pi))
            cur.updateRow(row)

    return averageWidthField

Aşağıda, yukarıdaki işlevi kullanarak çeşitli şekillerde ortalama genişliğe (ve referans için bazı diğer geometri niteliklerine) sahip dışa aktarılmış bir harita verilmiştir:

görüntü tanımını buraya girin


4
İfadeyi basitleştirirseniz, tam olacaktır area / perimeter * 4.
culebrón

Teşekkürler, @ culebrón. Formülün sadeliği üzerine kavramın açıklığına gidecektim ve denklemi basitleştirmeyi hiç düşünmedim. Bu bana biraz işlem süresi kazandırmalı.
Tom,

0

Yaklaşık medial eksenli başka bir çözüm:

  1. Çokgenin yaklaşık medial eksenini hesaplayın;
  2. Yaklaşık medial eksenin uzunluğunu alın;
  3. Eksenin iki ucundan çokgenin sınırına kadar mesafe alın;
  4. Eksen uzunluğu ve 3. adımdaki mesafeleri toplayın - yaklaşık poligonun uzunluğu;
  5. Şimdi çokgen alanını bu uzunluğa bölebilir ve ortalama çokgen genişliğini elde edebilirsiniz.

Sonuç, yaklaşık medial eksenin tek bir sürekli çizgi olmadığı çokgenler için kesinlikle yanlıştır, bu yüzden adım 1'den önce kontrol edip geri dönebilirsiniz NULL.

örnekler

İşte PostgreSQL işlevi örneği (not: postgis ve postgis_sfcgal uzantılarını yüklemeniz gerekir ):

CREATE FUNCTION ApproximatePolygonLength(geom geometry)
RETURNS float AS $$
    SELECT
        CASE
            /* in case when approximate medial axis is empty or simple line
             * return axis length
             */
            WHEN (ST_GeometryType(axis.axis) = 'ST_LineString' OR ST_IsEmpty(axis.axis))
                THEN axis_length.axis_length
                    + start_point_distance.start_point_distance
                    + end_point_distance.end_point_distance
            /* else geometry is too complex to define length */
            ELSE NULL
        END AS length
    FROM
        LATERAL (
            SELECT
                ST_MakeValid(geom) AS valid_geom
        ) AS valid_geom,
        LATERAL (
            SELECT
                /* `ST_LineMerge` returns:
                 *  - `GEOMETRYCOLLECTION EMPTY`, if `ST_ApproximateMedialAxis` is an empty line (i.e. for square);
                 *  - `LINESTRING ...`, if `ST_ApproximateMedialAxis` is a simple line;
                 *  - `MULTILINESTRING ...`, if `ST_ApproximateMedialAxis` is a complex line
                 *     that can not be merged to simple line. In this case we should return `NULL`.
                 */
                ST_LineMerge(
                    ST_ApproximateMedialAxis(
                        valid_geom.valid_geom
                    )
                ) AS axis
        ) AS axis,
        LATERAL (
            SELECT
                ST_Boundary(valid_geom.valid_geom) AS border
        ) AS border,
        LATERAL (
            SELECT
                ST_Length(axis.axis) AS axis_length
        ) AS axis_length,
        LATERAL (
            SELECT
                ST_IsClosed(axis.axis) AS axis_is_closed
        ) AS axis_is_closed,
        LATERAL (
            SELECT
                CASE WHEN axis_is_closed.axis_is_closed THEN 0
                ELSE
                    ST_Distance(
                        border.border,
                        CASE ST_GeometryType(axis.axis)
                            WHEN 'ST_LineString' THEN ST_StartPoint(axis.axis)
                            /* if approximate medial axis is empty (i.e. for square),
                             * get centroid of geometry
                             */
                            ELSE ST_Centroid(valid_geom.valid_geom)
                        END
                    )
                END AS start_point_distance
        ) AS start_point_distance,
        LATERAL (
            SELECT
                CASE WHEN axis_is_closed.axis_is_closed THEN 0
                ELSE
                    ST_Distance(
                        border.border,
                        CASE ST_GeometryType(axis.axis)
                            WHEN 'ST_LineString' THEN ST_EndPoint(axis.axis)
                            /* if approximate medial axis is empty (i.e. for square),
                             * get centroid of geometry
                             */
                            ELSE ST_Centroid(valid_geom.valid_geom)
                        END
                    )
                END AS end_point_distance
        ) AS end_point_distance;
$$ LANGUAGE SQL;

CREATE FUNCTION ApproximatePolygonWidth(geom geometry)
RETURNS float AS $$
    SELECT
        CASE
            WHEN length IS NULL THEN NULL
            ELSE area.area / length.length
        END AS width
    FROM
        (
            SELECT ApproximatePolygonLength(geom) AS length
        ) AS length,
        (
            SELECT
                ST_Area(
                    ST_MakeValid(geom)
                ) AS area
        ) AS area;
$$ LANGUAGE SQL;

dezavantajı:

Bu çözüm, çokgen neredeyse dikdörtgen olduğu ve insanın uzunluğunu sezgisel olarak tanımlayabildiği ancak yaklaşık medial eksen kenarına yakın küçük dallara sahip olduğu ve bu nedenle algoritmanın döndürdüğü durumlarla çalışmaz.

Örnek:

kırık örnek

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.