다각형의 평균 너비를 계산합니까? [닫은]


40

도로 표면을 나타내는 다각형의 평균 너비를 검사하고 싶습니다. 나는 또한 도로 중심선을 벡터 (때로는 정확하게 중심에 있지 않음)로 가지고 있습니다. 이 예에서 도로 중심선은 빨간색이고 다각형은 파란색입니다.

여기에 이미지 설명을 입력하십시오

내가 생각한 하나의 무차별 접근 방식은 작은 증분으로 라인을 버퍼링하고, fishnet 그리드와 버퍼를 교차시키고, fishpoly 그리드와 도로 폴리곤을 교차시키고, 교차 측정에 대한 교차 영역을 계산 하고이 작업을 계속 할 때까지 계속하는 것입니다 오류가 작습니다. 이것은 조잡한 접근 방식이며 더 우아한 솔루션이 있는지 궁금합니다. 또한 큰 도로와 작은 도로의 너비를 숨길 수 있습니다.

ArcGIS 10, PostGIS 2.0 또는 QGIS 소프트웨어를 사용하는 솔루션에 관심이 있습니다. 나는이 질문 을 보았고 Dan Patterson의 ArcGIS10 도구 를 다운로드 했지만 원하는 것을 계산할 수 없었습니다.

방금 다음과 같은 녹색 다각형을 생성 할 수있는 ArcGIS 10 의 Minimum Bounding Geometry 도구를 발견했습니다 .

여기에 이미지 설명을 입력하십시오

이것은 그리드를 따르는 도로에는 좋은 솔루션처럼 보이지만 달리 작동하지 않으므로 다른 제안에 여전히 관심이 있습니다.


사이드 바에서 가능한 해결책을 배제 했습니까? 즉 gis.stackexchange.com/questions/2880/… 잠재적으로 중복 된 게시물에 대한 사소한 답변으로 표시되었습니다

@ DanPatterson 나는 이런 질문을 보지 못했습니다 (물론 관련이 있습니다). 내 질문이 신고되었다는 의미입니까? 나는 당신의 두 번째 줄을 이해하지 못했습니다.
djq

@Dan과 관련된 질문은 "너비"에 대한 다른 해석과 관련이 있습니다 (실제로 해석이 완벽하지는 않습니다). 응답 은 평균 너비가 아닌 가장 넓은 지점에서 너비 찾는 데 중점을 둔 것으로 보입니다 .
whuber

@whuber 여기에 중앙 집중화 원하는대로 토론은 다른 질문을 폐쇄, 나는 질문 사람들에게 편집 "이해할 것을 제안 직사각형 스트립의 평균 폭의 estimative을 "
피터 크라우스

@Peter : 직사각형 스트립은 다각형으로 만들어 졌기 때문에보다 일반적인 타이틀이 있어야합니다.
whuber

답변:


40

문제의 일부는 "평균 폭"의 적절한 정의를 찾는 것입니다. 몇몇은 자연 스럽지만 적어도 약간 다를 수 있습니다. 간단히하기 위해 계산하기 쉬운 속성을 기반으로 정의를 고려하십시오 (예 : 중간 축 변환 또는 버퍼 시퀀스를 기반으로 정의는 제외).

예를 들어, 명확한 "폭"을 가진 다각형의 전형적인 직관은 길고 상당히 직선적 인 폴리 라인 (길이 L ) 주위의 작은 버퍼 (반지름 이 제곱 된 끝의 r )입니다. 우리는 2r = w 를 너비로 생각 합니다. 그러므로:

  • 그것의 주변 P 는 대략 2L + 2w와 동일하다;

  • 면적 A 는 대략 w L과 같습니다.

w 및 길이 L 은 2 차 x ^ 2-(P / 2) x + A의 근으로 복구 할 수 있습니다. 특히, 우리는 추정 할 수 있습니다

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

다각형이 실제로 길고 마른 것이 확실하다면, 더 가까운 근사치로 2L + 2w를 사용하여 2L과 같을 수 있습니다.

  • w (조악하게) = 2A / P.

이 근사에서의 상대 오차는 w / L에 비례합니다. 다각형이 얇을수록 w / L이 0에 가까워지고 근사가 더 좋아집니다.

이 접근법은 매우 간단 할뿐만 아니라 (영역을 둘레로 나누고 2를 곱하면됩니다), 어느 공식이든 다각형의 방향이나 위치에 관계가 없습니다 (유클리드 모션이 영역이나 둘레).

세그먼트 중 하나를 사용하여 거리 세그먼트를 나타내는 다각형의 평균 너비를 추정 할 수 있습니다 . 영역 A 에는 원래 폴리 라인의 각 굽힘에 작은 쐐기가 포함되어 있기 때문에 원래 추정치 w (2 차 공식 사용) 에서 발생하는 오류가 발생합니다 . 굽힘 각도의 합이 t 라디안 인 경우 (이는 폴리 라인의 총 절대 곡률입니다)

  • P = 2L + 2w + 2 파이트와

  • A = L w + Pi tw ^ 2.

이것을 이전 (이차 공식) 솔루션에 연결하고 단순화하십시오. 연기 가 사라지면 곡률 항 t 의 기여 가 사라졌습니다! 원래는 근사치처럼 보이는 것이 자기 교차하지 않는 폴리 라인 버퍼 (제곱 된 끝 부분)에 완벽하게 정확합니다. 가변 폭 다각형의 경우 이는 평균 폭 의 합리적인 정의 입니다.


@whuber에게 감사드립니다. 대답이며 더 명확하게 생각하는 데 도움이되었습니다.
djq

@ whuber : 나는 종이를 쓰고 있는데 여기에 설명하는 방법에 대한 적절한 ( '학문적') 참조를 제공해야합니다. 그런 참조가 있습니까? 이 측정 값의 이름이 있습니까? 그렇지 않다면, 나는 당신의 이름을 따서 명명 할 수 있습니다! "허버의 폭 측정"은 어떻습니까?
Julien

@ julien 참조가 없습니다. MISC {20279, TITLE = {다각형 평균 너비 계산?}, AUTHOR = {whuber ( gis.stackexchange.com/users/664/whuber )}, HOWPUBLISHED = {GIS}, NOTE = {URL : gis.stackexchange.com/q/20279/664 (버전 : 2013-08-13)}, EPRINT = { gis.stackexchange.com/q/20279 }, URL = { gis.stackexchange.com/q/20279 }}
whuber

18

여기에 @whuber 솔루션에 대한 최적화가 거의 없으며 더 일반적인 문제의 솔루션을 통합하는 데 유용하기 때문에 "버퍼 너비"라는 용어를 사용 하고 있습니다. 너비 추정값을 반환하는 st_buffer 역함수가 있습니까?

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;

이 문제에 대한, 대한 @celenius 질문 거리 폭은 , sw이 솔루션은

 sw = buffer_width(ST_Length(g1), g2)

sw은 "평균 폭",이다 g1의 중심 라인 g2, 그리고 거리 g2A는 다각형 . PostGIS로 테스트 한 OGC 표준 라이브러리 만 사용 했으며 동일한 buffer_width 함수로 다른 심각한 실제 응용 프로그램을 해결했습니다.

데모

A2영역이며 g2, L1(중심선의 길이 g1)의 g2.

우리가 생성 할 수 치죠 g2에 의해 g2=ST_Buffer(g1,w), 그리고 그 g1직선, 그래서 g2아이폰에있는 직사각형 L1와 폭 2*w, 그리고

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

w사각형 ( g2) 너비 의 절반 이므로 @whuber와 같은 공식이 아닙니다 . 그것은 좋은 추정기이지만, 우리가 테스트 (아래)에서 볼 수 있듯이 정확하지 않으며 함수는 그것을 단서로 사용하여 g2면적 을 줄이고 최종 추정기로 사용합니다.

여기서는 "endcap = square"또는 "endcap = round"인 버퍼를 평가하지 않으며 A2 , 같은 포인트 버퍼 영역의 합이 필요합니다 w.

참조 : 2005 년 비슷한 포럼 에서 W. Huber는 이와 같은 솔루션 및 기타 솔루션에 대해 설명합니다.

테스트 및 이유

직선의 경우 예상대로 결과가 정확합니다. 그러나 다른 형상의 경우 결과가 실망 스러울 수 있습니다. 주된 이유는 아마도 모든 모델이 정확한 사각형이거나 "스트립 사각형"에 근접 할 수있는 도형 때문일 것입니다. 이 근사치의 한계를 확인하기위한 "테스트 키트"( wfactor위 결과 참조).

 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;

결과 :

WITH RECTANGLES (중앙선은 직선)입니다.

         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

다른 지오메트리 (중심 접힘) :

         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.

btype참조 에는 ST_Buffer 가이드를 좋은 ilustratins 여기에 사용되는 선 스트링으로.

결론 :

  • 추정기 w_estim는 항상보다 낫다 w_near.
  • "직사각형 근처" g2지오메트리의 경우wfactor
  • 다른 형상 ( "직사각형 스트립"근처)의 경우 wfactor=~0.01에 1 %의 오차 한계 를 사용하십시오 w_estim. 이 wfactor까지 다른 추정기를 사용하십시오.

주의 및 예방

추정 오차가 발생하는 이유는 무엇입니까? 당신이 사용하는 경우 ST_Buffer(g,w), 당신은 폭 버퍼에 의해 추가 된 새로운 영역이 있음은 "직사각형 스트립 모델"으로 기대 w에 관한 w*ST_Length(g)또는 w*ST_Perimeter(g)... 보통 오버레이 (접힌 선 참조) 또는으로 "스타일링"하지 않을 때, 때이다 평균 w결함 추정 . 이것이 테스트의 주요 메시지입니다.

모든 king of buffer 에서이 문제를 감지하려면 버퍼 생성 동작을 확인하십시오.

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;

결과 :

         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%

        경보


13

다각형 데이터를 공간적 또는 테이블 방식으로 중심선 데이터에 결합 할 수 있으면 각 중심선 정렬에 대한 다각형 영역을 합산하고 중심선 길이로 나눕니다.


사실이야! 이 경우 중심선의 길이는 같지 않지만 항상 하나로 결합하고 다각형별로 분할 할 수 있습니다.
djq

데이터가 postgreSQL / postGIS에 있고 중심선과 다각형에 대한 거리 ID 필드가있는 경우 병합 / 분할이 필요하지 않으며 집계 함수를 사용하면 답을 얻을 수 있습니다. SQL이 느리거나 예제를 게시 할 것입니다. 이것이 당신이 어떻게 해결하려고하는지 알려 주시면, 필요한 경우
정리해 드리겠습니다

감사합니다 Scro, 현재 PostGIS에 없지만로드하기가 매우 빠릅니다. @ whuber의 접근 방식을 먼저 시도하지만 PostGIS의 결과와 비교할 것입니다 (SQL 도움말 제공에 감사드립니다.하지만 관리 할 수 ​​있어야합니다). 주로 내 머리에 접근을 명확하게하려고합니다.
djq

+1 사용 가능한 상황에 적합한 훌륭한 간단한 솔루션입니다.
whuber

9

다각형의 평균 너비에 대한 수식을 개발하여 Python / ArcPy 함수에 넣었습니다. 내 공식은 다른 곳에서 논의한 평균 너비의 가장 간단한 개념에서 파생 된 것이지만 실질적으로 확장됩니다. 즉, 다각형과 면적이 같은 원의 지름입니다. 그러나 위의 질문과 프로젝트에서 가장 좁은 축의 너비에 더 관심이있었습니다. 또한 잠재적으로 복잡한 볼록하지 않은 모양의 평균 너비에 관심이있었습니다.

내 해결책은 다음과 같습니다.

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

그건:

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

기능은 다음과 같습니다

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

다음은 위의 함수를 사용하여 다양한 모양에서 평균 너비 (및 참조 용으로 다른 지오메트리 속성)가있는 내 보낸 맵입니다.

여기에 이미지 설명을 입력하십시오


4
식을 단순화하면 그냥 area / perimeter * 4됩니다.
culebrón

감사합니다, @ culebrón. 나는 공식의 단순성에 대한 개념의 명확성을 위해 가고 있었고, 심지어 방정식을 단순화하는 것에 대해 생각조차하지 않았습니다. 처리 시간이 절약됩니다.
Tom

0

대략적인 중간 축을 가진 또 다른 솔루션 :

  1. 다각형의 대략적인 중간 축을 계산합니다.
  2. 대략적인 내측 축의 길이를 구하십시오.
  3. 축의 양쪽 끝에서 다각형 경계까지의 거리를 얻습니다.
  4. 3 단계에서 축 길이와 거리를 합하면 대략 다각형의 길이입니다.
  5. 이제 다각형의 영역을이 길이로 나누고 다각형의 평균 너비를 얻을 수 있습니다.

대략적인 중간 축이 단일 연속 선이 아닌 다각형의 경우 결과가 반드시 잘못되므로 1 단계 전에이를 확인하고 돌아올 NULL수 있습니다.

예

다음은 PostgreSQL 함수의 예입니다 (참고 : postgispostgis_sfcgal 확장자 를 설치해야 함 ).

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;

불리:

이 솔루션은 다각형이 거의 직사각형이고 사람이 길이를 직관적으로 정의 할 수 있지만 대략적인 중간 축이 가장자리 근처에 가지가 거의 없으므로 알고리즘이 None을 반환하는 경우에는 작동하지 않습니다.

예:

깨진 예

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.