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_BufferST_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.