PyQGIS ile birlikte eşit boyutlu çokgenler oluşuyor mu?


42

AtlasCreator için bir sonraki adımda kullanmak üzere bir çizgi boyunca çokgenler oluşturmak istiyorum.

ArcMap, Şerit Harita Dizini Özellikleri adı verilen bir araca sahiptir .

Bu araçla çokgenlerimin yüksekliğini ve genişliğini seçebilirim (8km x 4km) ve bunları çizgi boyunca otomatik olarak üretebilir / döndürebilirim.

Her poligonun oluşturduğu niteliklerden biri, daha sonra Atlas Jeneratör'deki kuzey oklarımı döndürmem gereken döndürme açısı.

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

Bu görevin QGIS / pyQGIS ile nasıl çözüleceği konusunda fikri olan var mı? Özel bir eklentinin içinde kullanılabilecek çim veya SAGA algoritmaları veya bir prossessing-toolbox-model de iyi olabilir; tüm çokgenler / bir genel bakış haritası olarak genişletilir.

Düzenleme2: QGIS'ten başka bir yazılım yüklemeye gerek kalmadan hala bir QGIS-Eklentisinde kullanılabilecek bir PyQGIS - çözünürlüğü aradığım için bir ödül sunuyorum (PostGIS / Oracle gibi RDBMS yok)


4
Bu bir eklenti için eğlenceli bir fikir gibi görünüyor.
alphabetasoup

1
Vahşi bir düşünce olarak, Peucker-Douglas genellemesine dayanan bir şeyin işe yarayabileceğini düşünüyorum
plablo09

1
belki v.split.length, sonra bölümlerin başlangıç ​​ve bitiş noktaları arasında düz bir çizgi çizin ve sonra v.buffer ile "Polimerlerin uçlarında kapak yapmayın"
Thomas B

1
Bu konuda bir ödül
kazanmayı çok isterdim

2
"Etiket takip satırı" uygulamalarında yeniden kullanılabilir bir kod olabilir. Dikdörtgenleriniz, bazı monospaced fontların gliflerinin ayak izleri gibidir.
kullanıcı30184

Yanıtlar:


29

İlginç soru! Kendimi denemek istediğim bir şeydi, o yüzden bir şans verdi.

Bunu PostGRES / POSTGIS'te bir çokgen kümesi oluşturan bir işlevle yapabilirsiniz.

Benim durumumda, bir demiryolu hattını temsil eden tek bir özelliğe (ÇOKLU BİR GİRİŞ) sahip bir masam var. Metre cinsinden bir CRS kullanması gerekiyor, ben osgb kullanıyorum (27700). 4km x 2km 'sayfa' yaptım.

Burada sonucu görebilirsiniz ... yeşil alan, karayolu ağıdır ve demiryolunun etrafındaki 1 kilometreye kadar sıkıştırılmıştır, bu da çokgenlerin yüksekliğine güzel bir şekilde karşılık gelir.

postgis oluşturulan şerit haritası

İşte fonksiyon ...

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' ;

Bu işlevi kullanma

İşte bir örnek; 4km x 2km sayfalar, epsg: 27700 ve% 10 örtüşme

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

Bunu çalıştırdıktan sonra PgAdminIII'den bir csv dosyasına verebilirsiniz. Bunu QGIS'e içe aktarabilirsiniz, ancak CRS'yi katman için manuel olarak ayarlamanız gerekebilir - QGIS, CRS katmanını sizin için ayarlamak için EWKT'de SRID'yi kullanmaz: /

Rulman özelliği ekleme

Bu muhtemelen postgis'te daha kolaydır, QGIS ifadelerinde yapılabilir, ancak bir kod yazmanız gerekir. Bunun gibi bir şey ...

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));

Uyarılar

Bu biraz hack-birlikte ve sadece bir veri seti üzerinde test etmek için bir şansım vardı.

Bu rulman özniteliği güncellemesi için hangi iki köşeyi seçmeniz gerektiğinden% 100 emin değilsiniz query... denemeniz gerekebilir.

İtiraf etmeliyim ki poligonu geçerli çizgi segmentiyle eşleşecek şekilde döndürmek için neden bu kadar kıvrımlı bir formül kullanmam gerektiğini bilmiyorum. ST_Azimuth () 'in çıkışını ST_Rotate ()' de kullanabileceğimi düşündüm, fakat görünüşte değil.


Cevabınız gerçekten harika ve kesinlikle deneyeceğim bir şey var. Benim için bir kısıtlama, üzerinde çalıştığım proje için postgres kullanamıyorum ve bir sunucu tarafına bağlı olmayan bir şeye ihtiyacım var. pyQGIS ile böyle bir şey üretmek için büyük bir mantık.
Berlinmapper

2
eğer öyleyse, QgsGeometry sınıfına bir bakın . PostGIS'in geometri işlemlerinin bir alt kümesine sahiptir ve pyQGIS yoluna gitmek istiyorsanız iyi bir başlangıç ​​noktası olacaktır. Algoritma pyQGIS'e taşınabilir olmalıdır ..
Steven Kay

3
Kullanıyorum PostGIS için bir yaklaşım düşünmek ST_Simplify referans çizgiler üretmek ve segmentlere hattı kırılma kullanarak ve daha sonra ST_Buffer ve ST_Envelope daha kısa ve daha verimli olacaktır.
Matthias Kuhn

@Mattthias Kuhn: Eğer çizgiyi parçalara bölersem eşit büyüklükte çizgiler elde edebilirim, ancak eşit büyüklükte poligonlar da olmayabilir. Mesela, çizgi oldukça "kıvrımlı" ise, çokgen daha kısa olurdu, değil mi?
Berlinmapper,

2
Çözümünüzü ve komut dosyanızın PyQGIS Sürümünü test ettim. Soldaki bazı küçük sorunları nasıl çözeceğimiz hakkında bir fikrim var: bit.ly/1KL7JHn ?
Berlinmapper

12

Değişik çözümler var. Ve bu basit polyline ve çoklu seçilmiş objelerle çalışabilir

blok şeması:

  1. Parametreler

    1. oluşturma ve okuma dizini için yönlendirmeyi seçin (soldan sağa, kuzeyden güneye ...)
    2. nesne boyutunu ayarla

    shape = (4000,8000) # (<width>,<length>)
    1. üst üste koyma değerini (varsayılan olarak% 10?) tanımla
  2. içinde
    1. Çok satırlı sipariş vermek (başlangıç ​​ve bitiş noktalarını karşılaştırmak), yönlendirme tercihinize bağlı olarak sıralamak> bir köşe tasarlamak
  3. OrderNodes'ta döngü

    1. sizi ilk nokta çapa olarak oluşturun

    2. Her köşe için x, y, id dizinine ekleyin ve bir vektör hesaplayın

    3. indirgeyici süperpozisyonu (% 10/2)>% 5 sol poligon ile% 5 sağ poligonla aynı çapa noktasına sahip poligon (uzunluk ve vektör oryantasyonu üzerinde) oluşturmak
    4. Emsal bir köşe noktası çokgenin dışına çıktığında veya vektör len> şekil uzunluğundaysa durma
    5. Önceki iyi çözüm ile Genereate poligon ve son iyi pozisyona sahip bağlantı noktası ayarlayın
    6. Yeni döngü yapın ve bir sonraki çokgen nesnesini oluşturmak için, x, y, id kodlarını sıfırlayın.

Gerçekten açık değilse veya yorum yapmıyorsanız bu teklifi değiştirebilirsiniz.


bu çok karmaşık görünüyor ancak henüz modelleyici ya da PyQGIS için nasıl kullanılacağını bilmediğimi itiraf etmeliyim. bu arada: bir üst üste binme katsayısı nedir?
Berlinmapper,

Bu durumda süperpozisyon 8000 x 10% ile poligonun bir parçası olan @Berlinmapper. Diğerini seçebilir veya poligon arasında bir sabit mesafe varsayımı oluşturabilirsiniz. Köşedeki sonraki fayans sayfasını belirtmek için tüm atlaslarda bunu görebilirsiniz
GeoStoneMarten

çözümünüz pyQGIS veya işlem araç kutusu ile mi kullanılacak? kulağa çok hoş geliyor ama hala nasıl devam
edeceğimi

1
@Berlinmapper Proses betiğini oluşturmak ve işlem araç kutusunda veya QGIS eklentisinde girdi ve çıktı parametresini ayarlamak için pyQGIS kullanmanız gerektiğini düşünüyorum. ArcGistoolbox ile aynı. Aslında bunu yapmak ve test etmek için zamanım yok.
GeoStoneMarten

12

Steven Kays pyqgis'de cevap verir. Komut dosyasını çalıştırmadan önce katmanınızdaki satırları seçin. Komut dizilişini desteklemiyor, bu yüzden çok çizgili katmanla çalışamaz

#!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
Harika. Çözümü test ettim. Bu sorunların nasıl çözüleceğine dair herhangi bir fikir hala çözümde: bit.ly/1KL7JHn ?
Berlinmapper,


Teşekkürler, ArcMap aracının nasıl çalıştığını anlamak için bir kaynak. Maalesef VB'ye alışkınım ama belki başka biri bir cevap / yorum yazmak için kullanabilir;)
Berlinmapper

4

İki cevap (gönderim sırasında) zekicedir ve iyi açıklanmıştır. Bununla birlikte, bunun için mümkün olan ÇOK basit ama etkili bir çözüm de vardır (nehre dayalı rastgele bir Kuzey yönünden ziyade, kuzey ile hizalanmış tüm haritalarınızı geleneksel şekilde kabul edersiniz). Rotasyonlar istiyorsanız, mümkündür ancak biraz daha karmaşıktır (aşağıya bakınız).

Öncelikle buradaki gönderime bak . Bu size Atlas için harita teminatları oluşturmak için nasıl yapılır. İstediğiniz yöntem, nasıl yapıldığına göre 'Workflow 2' uyarlamasıdır. Doğrusal özelliğinizin köşelerini veya uzunluğunu ayırın ve özellikleri istediğiniz miktarda tamponlayın. Tamponladığınız miktar, örtüşmeyi kısmen dikte edecektir (ancak aşağıya bakınız) ancak daha da önemlisi, alanlı bir özellik oluşturur. Satırları bölmek için istediğiniz sayıda eklenti kullanabilirsiniz, ancak GRASS v.split.length ve v.split.vert iyi seçeneklerdir (İşleme Araç Kutusunda bulunur).

Atlas Comperation’u Map Composer’da etkinleştirdikten ve tamponlanmış katmanınızı seçtikten sonra, items sekmesine geri dönün ve harita nesnesini seçin. 'Atlas Tarafından Kontrol Edildi' seçeneğini işaretleyin ve kullanım durumunuzda Marjin özelliğini tercih ederim. Bu, haritalar arasındaki örtüşmenizi kontrol eder (alternatif olarak sabit ölçeği tercih edebilirsiniz).

Besteci'nin üst araç çubuğundaki Atlas Önizleme Düğmesini kullanarak Atlas'ı önizleyebilir ve kaç sayfa üreteceğini görebilirsiniz. Tüm sayfaları tek bir PDF olarak veya ayrı dosyalar olarak dışa aktarabileceğinizi unutmayın.

Haritanın çizgi boyunca dönmesini sağlamak için, Harita Oluşturucu Öğesi özelliklerinde bir döndürme alanı vardır. Bir ifade ayarlamanız gerekecektir (döndürme kutusunun sağındaki küçük düğmeyi kullanın). Seçenek olarak değişkeni seçin ve ardından Düzenle. Bir ifade oluşturucusu açılır ve burada atlas özelliklerinin geometrisine veya alanlarına erişebilirsiniz. Daha sonra haritayı özelliklerin dönüşüne göre döndürmek için bir ifade oluşturabilirsiniz (yatağı, her bir çizgi parçasının başlangıç ​​ve bitiş noktalarını ve biraz tetiği kullanarak hesaplayabilirsiniz). Kuzey okunu döndürmek için aynı işlemi tekrarlayın (aynı ifadeyi veya önceden hesaplanmış değişkeni kullanarak).


Bu çözüm için teşekkürler. ancak bu şekilde yazdırmak istediğim tek uzantıların çokgenlerini alamayacağımı düşünüyorum. ayrıca tüm baskı içerikleriyle birlikte bir "genel bakış haritası" üretmelerini istiyorum.
Berlinmapper
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.