PyQGIS와 함께 동일한 크기의 다각형을 생성합니까?


42

다음 단계에서 AtlasCreator에 사용할 다각형을 선을 따라 만들고 싶습니다.

ArcMap에는 Strip Map Index Features 라는 도구가 있습니다 .

이 도구를 사용하여 다각형의 높이와 너비 (예 : 8km x 4km)를 선택하고 선을 따라 자동으로 생성 / 회전 할 수 있습니다.

각 다각형의 생성 된 속성 중 하나는 나중에 Atlas Generator에서 북쪽 화살표를 회전해야하는 회전 각도입니다.

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

누구나 QGIS에서 / pyQGIS 로이 작업을 해결하는 방법을 알고 있습니까? 사용자 정의 플러그인 내에서 사용할 수있는 잔디 또는 SAGA 알고리즘 또는 prossinging-toolbox 모델도 좋습니다.) Edit1 : 인쇄 범위뿐만 아니라 다각형을 사용해야하는 일종의 개요 맵으로 모든 다각형 / 익스텐트.

Edit2 : QGIS와 별도로 소프트웨어를 설치할 필요없이 QGIS 플러그인에서 사용할 수 있는 PyQGIS 솔루션을 찾고 있는데 현상금을 제공하고 있습니다 (PostGIS / Oracle과 같은 RDBMS 없음)


4
이것은 플러그인에 대한 재미있는 아이디어처럼 보입니다.
— alphabetasoup

1
야생의 생각으로, 나는 Peucker-Douglas 일반화에 기반한 어떤 것이 효과가 있다고 생각합니다
— plablo09

1
아마도 v.split.length, 세그먼트의 시작과 끝점 사이에 직선을 그리고 "폴리 라인의 끝에 뚜껑을 만들지 마십시오"옵션을 가진 v.buffer
— Thomas B

1
나는이 질문에 현상금을 시작하고 싶지만 아직 충분한 평판을 가지고 있지 않습니다; (
— Berlinmapper

2
"label-follow line"구현에서 재사용 가능한 코드가있을 수 있습니다. 직사각형은 일부 고정 폭 글꼴의 글리프의 발자국과 같습니다.
— user30184

답변:


29

재미있는 질문! 그것은 내가 직접 시도하고 싶었던 일이므로 갈 수 있습니다.

PostGRES / POSTGIS에서 다각형 세트를 생성하는 함수를 사용하여이 작업을 수행 할 수 있습니다.

필자의 경우 철도를 나타내는 하나의 기능 (MULTILINESTRING)이있는 테이블이 있습니다. 미터 단위로 CRS를 사용해야하며 osgb (27700)를 사용하고 있습니다. 4km x 2km '페이지'를 수행했습니다.

여기에서 결과를 볼 수 있습니다 ... 녹색 물건은 도로 네트워크이며, 철도 주변의 1km 버퍼에 클리핑되어 다각형의 높이에 잘 맞습니다.

postgis 생성 스트립 맵

기능은 다음과 같습니다.

CREATE OR REPLACE FUNCTION getAllPages(wid float, hite float, srid integer, overlap float) RETURNS SETOF geometry AS
$BODY$
DECLARE
    page geometry; -- holds each page as it is generated
    myline geometry; -- holds the line geometry
    startpoint geometry;
    endpoint geometry;
    azimuth float; -- angle of rotation
    curs float := 0.0 ; -- how far along line left edge is
    step float;
    stepnudge float;
    currpoly geometry; -- used to make pages
    currline geometry;
    currangle float;
    numpages float;
BEGIN
    -- drop ST_LineMerge call if using LineString 
    -- replace this with your table.
    SELECT ST_LineMerge(geom) INTO myline from traced_osgb; 
    numpages := ST_Length(myline)/wid;

    step := 1.0/numpages;
    stepnudge := (1.0-overlap) * step; 
    FOR r in 1..cast (numpages as integer)
    LOOP
        -- work out current line segment

        startpoint :=  ST_SetSRID(ST_Line_Interpolate_Point(myline,curs),srid);
        endpoint :=  ST_SetSRID(ST_Line_Interpolate_Point(myline,curs+step),srid);
        currline := ST_SetSRID(ST_MakeLine(startpoint,endpoint),srid);

        -- make a polygon of appropriate size at origin of CRS
        currpoly := ST_SetSRID(ST_Extent(ST_MakeLine(ST_MakePoint(0.0,0.0),ST_MakePoint(wid,hite))),srid);

        -- then nudge downwards so the midline matches the current line segment
        currpoly := ST_Translate(currpoly,0.0,-hite/2.0);

        -- Rotate to match angle
        -- I have absolutely no idea how this bit works. 
        currangle := -ST_Azimuth(startpoint,endpoint) - (PI()/2.0) + PI();
        currpoly := ST_Rotate(currpoly, currangle);

        -- then move to start of current segment
        currpoly := ST_Translate(currpoly,ST_X(startpoint),ST_Y(startpoint));

        page := currpoly;

        RETURN NEXT page as geom; -- yield next result
        curs := curs + stepnudge;
    END LOOP;
    RETURN;
END
$BODY$
LANGUAGE 'plpgsql' ;

이 기능 사용

다음은 예입니다. 4km x 2km 페이지, epsg : 27700 및 10 % 겹침

select st_asEwkt(getallpages) from getAllPages(4000.0, 2000.0, 27700, 0.1);

이것을 실행 한 후 PgAdminIII에서 csv 파일로 내보낼 수 있습니다. 이것을 QGIS로 가져올 수 있지만 레이어에 대해 CRS를 수동으로 설정해야 할 수도 있습니다. QGIS는 EWKT의 SRID를 사용하여 레이어 CRS를 설정하지 않습니다.

베어링 속성 추가

이것은 postgis에서 더 쉬울 것입니다. QGIS 표현식으로 수행 할 수 있지만 코드를 작성해야합니다. 이 같은...

create table pages as (
    select getallpages from getAllPages(4000.0, 2000.0, 27700, 0.1)
);

alter table pages add column bearing float;

update pages set bearing=ST_Azimuth(ST_PointN(getallpages,1),ST_PointN(getallpages,2));

경고

그것은 약간 해킹되었으며 하나의 데이터 세트에서만 테스트 할 수있는 기회를 가졌습니다.

베어링 속성 업데이트에서 어떤 두 정점을 선택해야하는지 100 % 확실하지 않습니다 query. 실험해야 할 수도 있습니다.

왜 현재 선분과 일치하도록 다각형을 회전시키기 위해 그러한 복잡한 공식을 수행해야하는지 모르겠다. ST_Rotate ()에서 ST_Azimuth ()의 출력을 사용할 수 있다고 생각했지만 그렇지 않은 것 같습니다.


귀하의 답변은 정말 훌륭하고 확실히 시도 할 것입니다. 하나의 제한 사항은 내가 작업중 인 프로젝트에 postgres를 사용할 수 없으며 서버 측에 의존하지 않는 것이 필요하다는 것입니다. pyQGIS로 그런 것을 재현하는 훌륭한 논리.
— Berlinmapper

2
이 경우 QgsGeometry 클래스를 살펴보십시오 . PostGIS의 지오메트리 작업의 하위 집합이 있으며 pyQGIS 경로를 원한다면 좋은 출발점이 될 것입니다. 이 알고리즘은 pyQGIS에 이식 가능해야합니다.
— Steven Kay

3
postgis의 경우 ST_Simplify 를 사용하여 참조 라인을 생성 하고 라인을 세그먼트 로 분리 한 다음 ST_Buffer 및 ST_Envelope 를 사용하는 것이 더 짧고 효율적이라고 생각합니다.
— Matthias Kuhn

@ Matthias Kuhn : 선을 세그먼트로 나누면 같은 크기의 선을 얻을 수 있지만 반드시 같은 크기의 다각형은 아닙니다. 예를 들어 선이 꽤 '곡선'이라면 다각형이 더 짧을 것입니다.
— Berlinmapper

2
귀하의 솔루션과 스크립트의 PyQGIS-Version을 테스트했습니다. 작은 사소한 문제를 해결하는 방법에 대한 아이디어 : bit.ly/1KL7JHn ?
— Berlinmapper

12

다른 솔루션이 있습니다. 그리고 이것은 간단한 폴리 라인과 선택된 여러 엔티티와 함께 ​​작동 할 수 있습니다

블록 다이어그램 :

  1. 매개 변수

    1. 생성 방향을 선택하고 인덱스를 읽습니다 (왼쪽에서 오른쪽으로, 북쪽에서 남쪽으로 ...)
    2. 객체 크기 설정

    shape = (4000,8000) # (<width>,<length>)
    1. 중첩 계수 정의 (기본적으로 10 %?)
  2. 초기화
    1. 폴리선 정렬 (시작점과 끝점 비교) 순서는 방향 선택에 따라 달라집니다.> 정점 순서 지정 피처 클래스
  3. OrderNode에서 루프

    1. 앵커로 첫 번째 포인트를 만들

    2. 각 정점에 대해 dict x, y, id에 추가하고 벡터를 계산하십시오.

    3. 중첩을 줄임으로써 (길이 및 벡터 방향에 따라) 다각형 생성 (10 % / 2)> 5 % 왼쪽 다각형
    4. 선행 정점이 다각형을 벗어나거나 벡터 길이가 모양보다 길 경우 중지
    5. 이전의 좋은 솔루션으로 다각형을 생성하고 마지막으로 좋은 위치로 앵커 포인트 설정
    6. 새로운 루프를 수행하고 dict x, y, id를 재설정하여 다음 다각형 객체를 생성하십시오.

이 제안이 실제로 명확하지 않거나 언급되지 않은 경우 변경할 수 있습니다.


이것은 정교하게 들리지만 아직 모델러 나 PyQGIS에 이것을 사용하는 방법을 모른다는 것을 인정해야합니다. 그건 그렇고 : 중첩 계수는 무엇입니까?.
— Berlinmapper

이 경우 superpostion이 8000 x 10 % 인 다각형의 일부인 @Berlinmapper. 다른 다각형을 선택하거나 다각형 사이에 고정 거리 상 정치를 만들 수 있습니다. 모든 아틀라스에서 다음 타일 페이지가 코너에 있음을 알 수 있습니다.
— GeoStoneMarten

귀하의 솔루션이 pyQGIS 또는 처리 도구 상자와 함께 사용됩니까? 그것은 훌륭하게 들리지만 나는 여전히 진행하는 방법을 모른다
— Berlinmapper

1
@Berlinmapper 프로세스 툴박스 또는 QGIS 플러그인에서 프로세스 스크립트를 생성하고 입력 및 출력 매개 변수를 설정하려면 pyQGIS를 사용해야한다고 생각합니다. arcgistoolbox와 동일합니다. 나는 실제로 그것을 할 시간이 없다.
— GeoStoneMarten

12

Steven Kays는 pyqgis로 대답합니다. 스크립트를 실행하기 전에 레이어에서 선을 선택하십시오. 스크립트는 라인 병합을 지원하지 않으므로 multilinestring을 가진 레이어에서는 작동하지 않습니다.

#!python
# coding: utf-8

# https://gis.stackexchange.com/questions/173127/generating-equal-sized-polygons-along-line-with-pyqgis
from qgis.core import QgsMapLayerRegistry, QgsGeometry, QgsField, QgsFeature, QgsPoint
from PyQt4.QtCore import QVariant


def getAllPages(layer, width, height, srid, overlap):
    for feature in layer.selectedFeatures():
        geom = feature.geometry()
        if geom.type() <> QGis.Line:
            print "Geometry type should be a LineString"
            return 2
        pages = QgsVectorLayer("Polygon?crs=epsg:"+str(srid), 
                      layer.name()+'_id_'+str(feature.id())+'_pages', 
                      "memory")
        fid = QgsField("fid", QVariant.Int, "int")
        angle = QgsField("angle", QVariant.Double, "double")
        attributes = [fid, angle]
        pages.startEditing()
        pagesProvider = pages.dataProvider()
        pagesProvider.addAttributes(attributes)
        curs = 0
        numpages = geom.length()/(width)
        step = 1.0/numpages
        stepnudge = (1.0-overlap) * step
        pageFeatures = []
        r = 1
        currangle = 0
        while curs <= 1:
            # print 'r =' + str(r)
            # print 'curs = ' + str(curs)
            startpoint =  geom.interpolate(curs*geom.length())
            endpoint = geom.interpolate((curs+step)*geom.length())
            x_start = startpoint.asPoint().x()
            y_start = startpoint.asPoint().y()
            x_end = endpoint.asPoint().x()
            y_end = endpoint.asPoint().y()
            # print 'x_start :' + str(x_start)
            # print 'y_start :' + str(y_start)
            currline = QgsGeometry().fromWkt('LINESTRING({} {}, {} {})'.format(x_start, y_start, x_end, y_end))
            currpoly = QgsGeometry().fromWkt(
                'POLYGON((0 0, 0 {height},{width} {height}, {width} 0, 0 0))'.format(height=height, width=width))
            currpoly.translate(0,-height/2)
            azimuth = startpoint.asPoint().azimuth(endpoint.asPoint())
            currangle = (startpoint.asPoint().azimuth(endpoint.asPoint())+270)%360
            # print 'azimuth :' + str(azimuth)
            # print 'currangle : ' +  str(currangle)

            currpoly.rotate(currangle, QgsPoint(0,0))
            currpoly.translate(x_start, y_start)
            currpoly.asPolygon()
            page = currpoly
            curs = curs + stepnudge
            feat = QgsFeature()
            feat.setAttributes([r, currangle])
            feat.setGeometry(page)
            pageFeatures.append(feat)
            r = r + 1

        pagesProvider.addFeatures(pageFeatures)
        pages.commitChanges()
        QgsMapLayerRegistry.instance().addMapLayer(pages)
    return 0

layer = iface.activeLayer()
getAllPages(layer, 500, 200, 2154, 0.4)

1
큰. 솔루션을 테스트했습니다. 솔루션이 여전히 가지고있는 이러한 문제를 해결하는 방법에 대한 아이디어가 있습니까 : bit.ly/1KL7JHn ?
— Berlinmapper

아마도 여기에 "영감"이있을 것입니다 : github.com/maphew/arcmapbook/blob/master/Visual_Basic/…
— Thomas B

고맙게도 ArcMap 도구의 작동 방식을 이해하는 데 도움이되는 훌륭한 자료입니다. 불행히도 저는 VB에 익숙하지 않지만 다른 누군가가 그것을 사용하여 답변 / 댓글을 게시 할 수 있습니다.)
— Berlinmapper

4

두 개의 답변 (게시 시점)은 독창적이고 잘 설명되어 있습니다. 그러나 이것에 대해 매우 간단하지만 효과적인 해결책이 있습니다 (강을 기반으로 한 임의의 북쪽 방향이 아닌 전통적인 방식으로 모든지도를 북쪽으로 정렬한다고 가정). 회전을 원한다면 가능하지만 약간 더 복잡합니다 (아래 참조).

먼저 여기 내 게시물을 살펴보십시오 . 이를 통해 Atlas에 대한지도 적용 범위를 만드는 방법을 알 수 있습니다. 원하는 방법은 사용 방법의 '워크 플로 2'입니다. 정점 또는 길이로 선형 형상을 분할하고 원하는만큼 형상을 버퍼링합니다. 버퍼링하는 양은 부분적으로 오버랩을 지시하지만 (아래 참조) 더 중요한 것은 영역이있는 피처를 만듭니다. 여러 플러그인을 사용하여 선을 분할 할 수 있지만 GRASS v.split.length 및 v.split.vert가 좋은 옵션입니다 (Processing Toolbox에서 사용 가능).

Map Composer에서 Atlas Generation을 활성화하고 버퍼링 된 레이어를 선택한 후 항목 탭으로 다시 전환하고 맵 객체를 선택하십시오. 'Controlled by Atlas'를 확인하고 사용 사례에서 Margin around 기능을 선택합니다. 이렇게하면지도 간의 겹침을 제어 할 수 있습니다 (또는 고정 배율을 선호 할 수도 있습니다).

작곡가의 상단 툴바에있는 미리보기 아틀라스 버튼을 사용하여 아틀라스를 미리 볼 수 있으며 얼마나 많은 페이지가 생성되는지 확인할 수 있습니다. 모든 페이지를 단일 PDF 또는 별도의 파일로 내보내도록 선택할 수 있습니다.

선을 따라 맵을 회전 시키려면 맵 작성기 항목 속성에 회전 필드가 있습니다. 표현식을 설정해야합니다 (회전 상자 오른쪽에있는 작은 버튼 사용). 변수를 옵션으로 선택한 다음 편집을 선택하십시오. 표현식 빌더가 팝업되고 아틀라스 피쳐의 지오메트리 또는 필드에 액세스 할 수 있습니다. 그런 다음 피쳐의 회전에 따라 맵을 회전하는 급행을 구축 할 수 있습니다 (각 선분의 시작점과 끝점과 약간의 삼각점을 사용하여 베어링을 계산할 수 있음). 동일한 프로세스 또는 반복 계산 된 변수를 사용하여 동일한 프로세스를 반복하여 북쪽 화살표를 회전시킵니다.


이 솔루션에 감사드립니다. 하지만이 방법으로 인쇄하려는 단일 범위의 다각형을 얻지 못할 것이라고 생각합니다. 나는 모든 인쇄 범위와 함께 "개요지도"를 생성해야합니다.
— Berlinmapper
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.