สร้างรูปหลายเหลี่ยมขนาดเท่ากันพร้อมกับ PyQGIS?


42

ฉันต้องการสร้างรูปหลายเหลี่ยมตามเส้นเพื่อใช้สำหรับ AtlasCreator ในขั้นตอนต่อไป

ArcMap มีเครื่องมือที่เรียกว่าคุณสมบัติ Strip ดัชนีแผนที่

ด้วยเครื่องมือนี้ฉันสามารถเลือกความสูงและความกว้างของรูปหลายเหลี่ยมของฉัน (พูด 8km x 4km) และผลิต / หมุนตามเส้นโดยอัตโนมัติ

หนึ่งในคุณลักษณะที่สร้างขึ้นของรูปหลายเหลี่ยมแต่ละอันคือมุมการหมุนที่ฉันต้องหมุนลูกศรทิศเหนือของฉันในตัวสร้าง Atlas ในภายหลัง

ป้อนคำอธิบายรูปภาพที่นี่

ใครบ้างมีความคิดวิธีแก้ปัญหานี้ใน QGIS / กับ pyQGIS? Grass- หรือ SAGA-algorithms หรือ prossessing-toolbox model ซึ่งสามารถใช้ในปลั๊กอินที่กำหนดเองได้เช่นกัน;) Edit1: ฉันไม่เพียง แต่ต้องการขอบเขตการพิมพ์เท่านั้น แต่ยังเป็นรูปหลายเหลี่ยมตามที่ฉันต้องการพิมพ์แผนที่ด้วย รูปหลายเหลี่ยม / ส่วนขยายทั้งหมดเป็นแผนที่ภาพรวมบางประเภท

แก้ไข 2: ฉันเสนอความโปรดปรานเนื่องจากฉันยังคงมองหาPyQGIS - โซลูชันที่สามารถใช้ใน QGIS-Plugin โดยไม่จำเป็นต้องติดตั้งซอฟต์แวร์นอกเหนือจาก QGIS (ไม่มี RDBMS เช่น PostGIS / Oracle)


4
ดูเหมือนว่าเป็นความคิดที่สนุกสำหรับปลั๊กอิน
alphabetasoup

1
เป็นความคิดที่บ้าคลั่งฉันคิดว่าบางสิ่งที่อิงกับการใช้งานทั่วไปของ Peucker-Douglas อาจทำงานได้
plablo09

1
บางที v.split.length แล้วลากเส้นตรงระหว่างจุดเริ่มต้นและจุดสิ้นสุดของเซ็กเมนต์และจากนั้น v.buffer พร้อมตัวเลือก "อย่าทำแคปที่ส่วนปลายของโพลิลีน"
Thomas B

1
ฉันชอบที่จะเริ่มให้รางวัลกับคำถามนี้ แต่ฉันยังมีชื่อเสียงไม่เพียงพอ (
Berlinmapper

2
อาจมีรหัสที่นำกลับมาใช้ใหม่ได้บ้างในการใช้งาน รูปสี่เหลี่ยมผืนผ้าของคุณเป็นเหมือนรอยเท้าของร่ายมนตร์แบบอักษรโมโนเซ็ต
user30184

คำตอบ:


29

คำถามที่น่าสนใจ! มันเป็นสิ่งที่ฉันต้องการลองด้วยตัวเอง

คุณสามารถทำได้ใน PostGRES / POSTGIS ด้วยฟังก์ชันที่สร้างชุดรูปหลายเหลี่ยม

ในกรณีของฉันฉันมีตารางที่มีคุณลักษณะหนึ่งรายการ (MULTILINESTRING) ซึ่งแสดงถึงเส้นทางรถไฟ มันต้องใช้ CRS เป็นเมตรฉันใช้ osgb (27700) ฉันทำ 'หน้า' 4km x 2km แล้ว

ที่นี่คุณสามารถเห็นผลลัพธ์ ... สิ่งที่เป็นสีเขียวคือเครือข่ายถนนตัดกับบัฟเฟอร์ 1 กม. รอบทางรถไฟซึ่งสอดคล้องกับความสูงของรูปหลายเหลี่ยมอย่างสวยงาม

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 ไม่ได้ใช้ SRID ใน EWKT เพื่อตั้งค่าเลเยอร์ 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_Azimuth () ใน ST_Rotate () แต่ดูเหมือนจะไม่


คำตอบของคุณดีมากและสิ่งที่ฉันจะลองแน่นอนข้อ จำกัด อย่างหนึ่งสำหรับฉันคือฉันไม่สามารถใช้ postgres สำหรับโครงการที่ฉันกำลังทำงานอยู่และต้องการบางสิ่งที่ไม่ขึ้นอยู่กับฝั่งเซิร์ฟเวอร์ แต่บางทีฉันอาจใช้ ตรรกะที่ยอดเยี่ยมในการทำซ้ำบางอย่างเช่นนั้นด้วย pyQGIS
Berlinmapper

2
ถ้าเป็นกรณีที่ใช้เวลาดูที่ชั้น QgsGeometry มันมีส่วนย่อยของการดำเนินการทางเรขาคณิตของ PostGIS และจะเป็นจุดเริ่มต้นที่ดีถ้าคุณต้องการไปเส้นทาง pyQGIS อัลกอริทึมควรพกพาไปที่ pyQGIS ..
Steven Kay

3
ฉันคิดว่าวิธีการที่ใช้ST_Simplifyเพื่อสร้างบรรทัดอ้างอิงและแบ่งบรรทัดเป็นเซ็กเมนต์จากนั้นใช้ST_BufferและST_Envelopeจะสั้นลงและมีประสิทธิภาพมากขึ้น
Matthias Kuhn

@ Matias Kuhn: ถ้าฉันแบ่งบรรทัดเป็นเซ็กเมนต์ฉันจะได้ขนาดเส้นเท่ากัน แต่ไม่จำเป็นต้องเป็นรูปหลายเหลี่ยมที่เท่ากัน เช่นถ้าเส้นสวย 'โค้ง' รูปหลายเหลี่ยมน่าจะสั้นลงใช่ไหม?
Berlinmapper

2
ฉันทดสอบวิธีการแก้ปัญหาของคุณและ PyQGIS-Version ของสคริปต์ของคุณคุณมีความคิดอย่างไรในการแก้ปัญหาเล็กน้อยที่เหลือ: bit.ly/1KL7JHn ?
Berlinmapper

12

มีโซลูชั่น differents และสิ่งนี้สามารถทำงานกับโพลีไลน์ที่เรียบง่ายและเอนทิตีที่เลือกได้

บล็อกไดอะแกรม:

  1. พารามิเตอร์

    1. เลือกการวางแนวสำหรับการสร้างและอ่านดัชนี (จากซ้ายไปขวา, เหนือ - ใต้ - ใต้ ... )
    2. กำหนดขนาดวัตถุ

    shape = (4000,8000) # (<width>,<length>)
    1. กำหนด coef superposition (10% โดยค่าเริ่มต้น?)
  2. ในนั้น
    1. การสั่งซื้อโพลีไลน์ (เปรียบเทียบจุดเริ่มต้นและจุดสิ้นสุด) การสั่งซื้อขึ้นอยู่กับตัวเลือกการวางแนวของคุณ> สร้างจุดยอดการสั่งซื้อฟีเจอร์คลาส OrderNodes
  3. วนรอบใน OrderNodes

    1. สร้างคุณจุดแรกเป็นจุดยึด

    2. สำหรับแต่ละจุดสุดยอดเพิ่มใน dict x, y, id และคำนวณเวกเตอร์

    3. สร้างรูปหลายเหลี่ยม (เหนือความยาวและการวางแนวเวกเตอร์) ด้วยการลดการซ้อนทับ (10% / 2)> รูปหลายเหลี่ยมด้านซ้าย 5% รูปหลายเหลี่ยมด้านซ้าย 5% รูปหลายเหลี่ยมด้านขวาที่มีจุดยึดเดียวกัน
    4. หยุดเมื่อจุดยอดก่อนหน้าไม่มีรูปหลายเหลี่ยมหรือถ้าเวกเตอร์เลนเป็น> เพื่อกำหนดความยาวของรูปร่าง
    5. สร้างรูปหลายเหลี่ยมด้วยโซลูชันที่ดีก่อนหน้านี้และตั้งจุดยึดด้วยตำแหน่งที่ดีที่สุด
    6. ดำเนินการลูปใหม่และรีเซ็ต dict x, y, id เพื่อสร้างวัตถุรูปหลายเหลี่ยมถัดไป

คุณสามารถเปลี่ยนข้อเสนอนี้หากไม่ชัดเจนหรือแสดงความคิดเห็น


ฟังดูซับซ้อน แต่ฉันต้องยอมรับว่าฉันยังไม่รู้ว่าจะใช้มันกับ modeler หรือ PyQGIS อย่างไร โดยวิธีการ: ค่าสัมประสิทธิ์การทับซ้อนคืออะไร?
Berlinmapper

@Berlinmapper ที่เป็นส่วนหนึ่งของรูปหลายเหลี่ยมที่มี superpostion 8000 x 10% ในกรณีนี้ คุณสามารถเลือกอื่น ๆ หรือสร้าง supperposition ระยะทาง fixe อยู่ระหว่าง polygone คุณจะเห็นว่าในแผนที่ทั้งหมดเพื่อระบุหน้ากระเบื้องถัดไปในมุม
GeoStoneMarten

โซลูชันของคุณมีวัตถุประสงค์เพื่อใช้กับ pyQGIS หรือกล่องเครื่องมือประมวลผลหรือไม่ มันฟังดูดี แต่ฉันก็ยังไม่รู้วิธีดำเนินการต่อไป
Berlinmapper

1
@Berlinmapper ฉันคิดว่าคุณต้องใช้ pyQGIS เพื่อสร้างสคริปต์กระบวนการและตั้งค่าพารามิเตอร์อินพุตและเอาต์พุตในการประมวลผลกล่องเครื่องมือหรือปลั๊กอิน QGIS เหมือนกับ arcgistoolbox ฉันไม่มีเวลาทำและทดสอบ
GeoStoneMarten

12

Steven Kays ตอบคำถามใน pyqgis เพียงเลือกบรรทัดในเลเยอร์ของคุณก่อนเรียกใช้สคริปต์ สคริปต์ไม่รองรับ linemerging ดังนั้นจึงไม่สามารถทำงานบนเลเยอร์ได้ด้วยมัลติเลเยอร์

#!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

ทรัพยากร thanks.great ที่จะเข้าใจว่าเครื่องมือ ArcMap works.unfortunately ฉัน' ม. ไม่ได้ใช้เพื่อ VB แต่บางทีอาจจะมีคนอื่นสามารถใช้มันเพื่อโพสต์คำตอบ / ความคิดเห็น;)
Berlinmapper

4

คำตอบสองคำตอบ (ตอนที่โพสต์) มีความคิดสร้างสรรค์และอธิบายได้ดี อย่างไรก็ตามยังมีวิธีแก้ปัญหาที่ง่าย แต่มีประสิทธิภาพมากสำหรับสิ่งนี้ (สมมติว่าคุณจะยอมรับแผนที่ทั้งหมดที่อยู่ในแนวเดียวกับทิศเหนือขึ้นไปในแบบดั้งเดิมแทนที่จะเป็นทิศเหนือแบบสุ่มตามแม่น้ำ) ถ้าคุณต้องการผลัดอาจเป็นไปได้ แต่ซับซ้อนกว่าเล็กน้อย (ดูด้านล่าง)

ครั้งแรกที่ได้ดูการโพสต์ของฉันที่นี่ สิ่งนี้จะให้วิธีการในการสร้างแผนที่ครอบคลุมสำหรับ Atlas วิธีที่คุณต้องการคือการปรับเป็น 'เวิร์กโฟลว์ 2' ในวิธีการ แยกคุณสมบัติเชิงเส้นของคุณโดยจุดยอดหรือความยาวและบัฟเฟอร์คุณสมบัติตามจำนวนใด ๆ จำนวนเงินที่คุณบัฟเฟอร์โดยจะกำหนดทับซ้อนบางส่วน (แต่ดูด้านล่าง) แต่ที่สำคัญกว่านั้นจะสร้างคุณสมบัติที่มีพื้นที่ คุณสามารถใช้ปลั๊กอินจำนวนเท่าใดก็ได้เพื่อแยกบรรทัด แต่ GRASS v.split.length และ v.split.vert เป็นตัวเลือกที่ดี (มีอยู่ในเครื่องมือการประมวลผล)

เมื่อเปิดใช้งาน Atlas Generation ใน Map Composer แล้วเลือกเลเยอร์บัฟเฟอร์ของคุณให้สลับกลับไปที่แท็บรายการแล้วเลือกวัตถุแผนที่ของคุณ ทำเครื่องหมายที่ 'ควบคุมโดย Atlas' และในกรณีที่คุณใช้งานฉันจะเลือกใช้คุณลักษณะ Margin รอบ ๆ วิธีนี้จะควบคุมการทับซ้อนของคุณระหว่างแผนที่ (หรือมิฉะนั้นคุณอาจต้องการขนาดที่แน่นอน)

คุณสามารถดูตัวอย่าง Atlas โดยใช้ปุ่มแสดงตัวอย่าง Atlas ในแถบเครื่องมือด้านบนของผู้แต่งและดูว่าจะสร้างหน้ากี่หน้า หมายเหตุคุณสามารถเลือกที่จะส่งออกหน้าทั้งหมดในรูปแบบ PDF เดียวหรือเป็นไฟล์แยกกัน

ในการทำให้แผนที่หมุนตามเส้นนั้นจะมีฟิลด์การหมุนในคุณสมบัติ Map Composer Item คุณจะต้องตั้งค่านิพจน์ (ใช้ปุ่มเล็ก ๆ ทางด้านขวาของกล่องหมุน) เลือกตัวแปรเป็นตัวเลือกของคุณจากนั้นแก้ไข ตัวสร้างนิพจน์จะป๊อปอัปและในนั้นคุณสามารถเข้าถึงรูปทรงเรขาคณิตหรือเขตข้อมูลของคุณสมบัติแผนที่ จากนั้นคุณสามารถสร้างเอ็กซ์เพรสเพื่อหมุนแผนที่ตามการหมุนของฟีเจอร์ (คุณสามารถคำนวณแบริ่งโดยใช้จุดเริ่มต้นและจุดสิ้นสุดของแต่ละบรรทัดเซ็กเมนต์และตรีโกณมิติเล็กน้อย) ทำซ้ำกระบวนการเดียวกันเพื่อหมุนลูกศรทิศเหนือของคุณ (ใช้นิพจน์เดียวกันหรือตัวแปรที่คำนวณล่วงหน้า)


ขอบคุณสำหรับการแก้ปัญหานี้ แต่ฉันคิดว่าวิธีนี้ฉันจะไม่ได้รูปหลายเหลี่ยมของขอบเขตเดียวที่ฉันต้องการพิมพ์ ฉันต้องการให้พวกเขาสร้าง "แผนที่ภาพรวม" ด้วยขอบเขตการพิมพ์ทั้งหมด
Berlinmapper
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.