QGIS'te altıgen bir ızgaraya bir yol ağı nasıl bağlanır?


13

Altıgen bir ızgaraya bir yol ağı eklemek için QGIS 2.14 kullanmaya çalışıyorum, ama garip eserler alıyorum.

MMQGIS ile altıgen bir ızgara oluşturdum , hücreler yaklaşık 20 x 23 m. Yol ağını 1m kadar tamponladım ve yoğunlaştırdım , böylece birkaç metrede bir düğüm var. Neyi başarmaya çalıştığımı aşağıda görebilirsiniz. Gördüğünüz gibi, bazı durumlarda işe alabilirim: -

  • mavi yoğunlaştırılmış yoldur (tamponlu çizgi)
  • kırmızı 'hexified' versiyonudur - bulmak istediğim şey bu
  • gri altıgen ızgaradır

resim açıklamasını buraya girin

Daha sonra düğümleri en yakın altıgen köşeye oturtmak için yeni Yapış geometrileri özelliğini kullandım. Sonuçlar umut vericidir, ancak çizginin altıgeni (veya bir kısmını) doldurmak için genişlediği bazı kenar durumlar vardır: -

resim açıklamasını buraya girin

Tamponun nedeni, Yapış geometrileri , geometrisi farklı olan bir katmana yapışmanıza izin vermemesidir. Örneğin, LINE katmanındaki düğümleri POINT katmanındaki noktalara yapıştıramazsınız). POLYGON'u POLYGON'a bağlamanın en mutlu olduğu görülüyor.

Tamponlu yol çizgisinin bir tarafı altıgen hücrenin bir tarafına atladığında ve diğer taraf altıgen hücrenin diğer tarafına atladığında yolların genişlediğinden şüpheleniyorum. Örneğimde, batı-doğuyu dar bir açıda geçen yollar en kötü gibi görünüyor.

Başarısız denediğim şeyler: -

  • yol ağını küçük bir miktarda tamponlar, bu yüzden bir çokgen kalır ancak çok incedir.
  • altıgen hücreleri yoğunlaştırmak (böylece sadece köşelerde değil, kenarlarda düğümler vardır)
  • maksimum yakalama mesafesini değiştirme (bu en büyük etkiye sahiptir, ancak ideal bir değer bulamıyorum)
  • LINE katmanları kullanma, POLYGON'lar değil

Ben sadece LINE katmanları kullanarak değiştirmek, bir süre çalışır, sonra çöküyor bulmak. Çalışmalarını ilerledikçe kurtarıyor gibi görünüyor - bazı çizgiler kısmen işlendi.

resim açıklamasını buraya girin

Herkes bir çizgideki noktaları başka bir çizgi / çokgen katmanındaki en yakın noktaya tutturmanın başka bir yolunu biliyor mu, ideal olarak postgres / postgis kullanmaya gerek kalmadan (postgis ile bir çözüm de hoş karşılanacaktır)?

DÜZENLE

Gitmek isteyen herkes için buraya Dropbox'a bir başlangıç ​​QGIS projesi koydum . Buna Hex Grid ve Densified lines katmanları dahildir. (Yol ağı OSM'dendir, bu nedenle QuickOSM kullanılarak indirilebilir, örneğin yolları yoğunlaştırmak için orijinali almanız gerekiyorsa).

İngiltere için yerelleştirilmiş bir UTM'nin metre cinsinden birimleriyle OSGB'de (epsg: 27700) olduğunu unutmayın.


3
Örnek bir veri kümesini paylaşabilir misiniz? Denemek istiyorum ama sıfırdan örnek veri oluşturma sürecini gözden geçirmek istemiyorum.
Germán Carrillo

@ GermánCarrillo - teşekkürler. Soruya örnek bir projeye link ekledim.
Steven Kay

Yanıtlar:


14

Benim çözüm yapışmayı içeren bir iş akışından daha hızlı ve daha etkili bir PyQGIS betiği içerir (Ben de denedim). Algoritmamı kullanarak şu sonuçları elde ettim:

resim açıklamasını buraya girin

resim açıklamasını buraya girin

Aşağıdaki kod parçacıklarını QGIS içinden (QGIS Python konsolunda) sırayla çalıştırabilirsiniz. Sonunda, kıvrımlı yolların QGIS'e yüklendiği bir bellek katmanı elde edersiniz.

Tek önkoşul, çok parçalı bir yol Shapefile oluşturmaktır (kullanın Processing->Singleparts to multipart, alanı parametre fictitiuosolarak kullandım Unique ID field). Bu bize roads_multipart.shptek bir özelliğe sahip bir dosya verecektir .

İşte açıklanan algoritma:

  1. Güzergahların kesiştiği en yakın altıgen kenarları alın. Her altıgen için, her bir komşu köşe çifti ve karşılık gelen centroid arasında 6 üçgen oluştururuz. Herhangi bir yol bir üçgenle kesişirse, altıgen ve üçgen tarafından paylaşılan segment son kopuk yola eklenir. Bu, tüm algoritmanın daha ağır kısmı, makinemde 35 saniye sürüyor. İlk iki satırda 2 Shapefile yolu vardır, bunları kendi dosya yollarınıza uyacak şekilde ayarlamanız gerekir.

    hexgrid = QgsVectorLayer("/docs/borrar/hex_grid_question/layers/normal-hexgrid.shp", "hexgrid", "ogr")
    roads = QgsVectorLayer("/docs/borrar/hex_grid_question/layers/roads_multipart.shp", "roads", "ogr")  # Must be multipart!
    
    roadFeat = roads.getFeatures().next() # We just have 1 geometry
    road = roadFeat.geometry() 
    indicesHexSides = ((0,1), (1,2), (2,3), (3,4), (4,5), (5,0))
    
    epsilon = 0.01
    # Function to compare whether 2 segments are equal (even if inverted)
    def isSegmentAlreadySaved(v1, v2):
        for segment in listSegments:        
            p1 = QgsPoint(segment[0][0], segment[0][1])
            p2 = QgsPoint(segment[1][0], segment[1][1])
            if v1.compare(p1, epsilon) and v2.compare(p2, epsilon) \
                or v1.compare(p2, epsilon) and v2.compare(p1, epsilon):
                return True
        return False
    
    # Let's find the nearest sides of hexagons where routes cross
    listSegments = []
    for hexFeat in hexgrid.getFeatures():
        hex = hexFeat.geometry()
        if hex.intersects( road ):
            for side in indicesHexSides:
                triangle = QgsGeometry.fromPolyline([hex.centroid().asPoint(), hex.vertexAt(side[0]), hex.vertexAt(side[1])])
                if triangle.intersects( road ):
                    # Only append new lines, we don't want duplicates!!!
                    if not isSegmentAlreadySaved(hex.vertexAt(side[0]), hex.vertexAt(side[1])): 
                        listSegments.append( [[hex.vertexAt(side[0]).x(), hex.vertexAt(side[0]).y()], [hex.vertexAt(side[1]).x(),hex.vertexAt(side[1]).y()]] )  
  2. Python listelerini, tuples ve sözlükleri kullanarak bağlantısız (veya 'açık') segmentlerden kurtulun . Bu noktada, bazı bağlantısız bölümler kalmıştır, yani bir tepe noktası bağlantısı kesilmiş ancak diğeri en az diğer 2 bölüme bağlanmış bölümler vardır (bir sonraki şekilde kırmızı bölümlere bakınız). Onlardan kurtulmamız gerekiyor.

    resim açıklamasını buraya girin

    # Let's remove disconnected/open segments
    lstVertices = [tuple(point) for segment in listSegments for point in segment]
    dictConnectionsPerVertex = dict((tuple(x),lstVertices.count(x)-1) for x in set(lstVertices))
    
    # A vertex is not connected and the other one is connected to 2 segments
    def segmentIsOpen(segment):
        return dictConnectionsPerVertex[tuple(segment[0])] == 0 and dictConnectionsPerVertex[tuple(segment[1])] >= 2 \
            or dictConnectionsPerVertex[tuple(segment[1])] == 0 and dictConnectionsPerVertex[tuple(segment[0])] >= 2
    
    # Remove open segments
    segmentsToDelete = [segment for segment in listSegments if segmentIsOpen(segment)]        
    for toBeDeleted in segmentsToDelete:
        listSegments.remove( toBeDeleted )
  3. Şimdi koordinatlar listesinden bir vektör katmanı oluşturabilir ve bunu QGIS haritasına yükleyebiliriz :

    # Create a memory layer and load it to QGIS map canvas
    vl = QgsVectorLayer("LineString", "Snapped Routes", "memory")
    pr = vl.dataProvider()
    features = []
    for segment in listSegments:
        fet = QgsFeature()
        fet.setGeometry( QgsGeometry.fromPolyline( [QgsPoint(segment[0][0], segment[0][1]), QgsPoint(segment[1][0], segment[1][1])] ) )
        features.append(fet)
    
    pr.addFeatures( features )
    vl.updateExtents()
    QgsMapLayerRegistry.instance().addMapLayer(vl)

Sonucun başka bir kısmı:

resim açıklamasını buraya girin

Yakalanan rotalarda niteliklere ihtiyacınız varsa, kavşakları hızlı bir şekilde değerlendirmek için bir Mekansal Dizin kullanabiliriz ( /gis//a/130440/4972 gibi ), ancak bu başka bir hikaye.

Bu yardımcı olur umarım!


1
teşekkür ederim, mükemmel çalışıyor! Python konsoluna yapıştırma sorunları vardı ... Ben qgis python editörü .py dosyası olarak kaydetti ve oradan iyi koştu. Çok parçalı adım öznitelikleri kaldırır, ancak bir arabellek / uzamsal birleşim bunu düzeltir!
Steven Kay

1
Harika! Sevindim sonunda karşılaştığınız sorunu çözdü. Karşılaştığınız kullanım durumunun ne olduğunu bilmek istiyorum. Bunu bir QGIS eklentisi veya İşleme komut dosyalarına dahil edilmiş bir komut dosyası haline getirmek için kullanabilir miyiz?
Germán Carrillo

1
aklımdaki kullanım örneği, tüplü bir ızgaraya veya sınırlı bir açı kümesine çizgiler eklemeniz gereken Tüp Haritası gibi toplu taşıma haritalarıydı . Bu, dijitalleştirerek manuel olarak yapılabilir, ancak otomatik olup olamayacağını görmek ilgimi çekti. Oluşturmaları kolay, görsel açıdan ilginç ve dik açı olmayan açıları olan altıgenleri kullandım. Bence bu daha ayrıntılı olarak incelenmeye değer, özellikle de diğer tesselasyonlarla çalışmak için genelleştirilebiliyorsa ...
Steven Kay

1
Senaryonun arkasındaki fikir üçgenler, kareler, beşgenler, altıgenler, vb.
Germán Carrillo

6

ArcGIS'de yaptım, kesinlikle QGIS kullanılarak veya sadece geometrileri okuyabilen bir pakete sahip python ile uygulanabilir. Yolların ağı temsil ettiğinden emin olun, yani yalnızca uçlarda birbiriyle kesişin. OSM ile uğraşıyorsunuz, sanırım durum böyle.

  • Yakınlık çokgenlerini çizgilere dönüştürün ve onları düzlemselleştirin, böylece onlar da geometrik bir ağ haline gelir.
  • Puanları sonuna kadar yerleştir - Voronoi Puanları: resim açıklamasını buraya girin
  • Yolları 5 m'lik düzenli aralıklarla yola koyun, ağ yollarının benzersiz bir ada sahip olduğundan emin olun:

resim açıklamasını buraya girin resim açıklamasını buraya girin

  • Her Yol Noktası için en yakın Voronoi Noktası koordinatlarını bulun: resim açıklamasını buraya girin
  • En yakın noktaları aynı sırayla bağlayarak “Yollar” oluşturun: resim açıklamasını buraya girin

Bunu görmek istemiyorsanız: resim açıklamasını buraya girin

Voronoi Lines'da zincirleme noktaları kullanmaya çalışmayın. Korkarım sadece daha da kötüleştirecek. Böylece tek seçeneğiniz Voronoi hatlarından ağ oluşturmak ve yol bitiş noktaları arasındaki yolları bulmaktır, bu da önemli değildir.


bu harika, teşekkürler! Voronoi çizgilerini kullandığınızdan bahsediyorsunuz, buna çok aşina değilsiniz (Voronois, noktalardan anlayabiliyorum). Her bir çizginin, o çizgiye en yakın tüm noktaların çokgeniyle çevrili olduğunu mu kastediyorsunuz? (Bunu QGIS'de yapmanın bir yolunun farkında değilim). Yoksa normal bir voronoi örgüsünün sınır çizgilerini, noktalara göre mi kastediyorsunuz?
Steven Kay

Yakınlık çokgenlerinin sınır çizgileri. Ama çok erken durdum. Görevi tamamlamak için ilk sonucu köşede bölmek, orta noktaya nokta eklemek ve işlemi tekrarlamak
yeterlidir

4

Bir QGIS yöntemi istediğini anlıyorum, ancak bir cevap sormak için benimle:

roads = 'clipped roads' # roads layer
hexgrid = 'normal-hexgrid' # hex grid layer
sr = arcpy.Describe('roads').spatialReference # spatial reference
outlines = [] # final output lines
points = [] # participating grid vertices
vert_dict = {} # vertex dictionary
hex_dict = {} # grid dictionary
with arcpy.da.SearchCursor(roads,["SHAPE@","OID@"], spatial_reference=sr) as r_cursor: # loop through roads
    for r_row in r_cursor:
        with arcpy.da.SearchCursor(hexgrid,["SHAPE@","OID@"], spatial_reference=sr) as h_cursor: # loop through hex grid
            for h_row in h_cursor:
                if not r_row[0].disjoint(h_row[0]): # check if the shapes overlap
                    hex_verts = []
                    for part in h_row[0]:
                        for pnt in part:
                            hex_verts.append(pnt) # add grid vertices to list
                    int_pts = r_row[0].intersect(h_row[0],1) # find all intersection points between road and grid
                    hex_bnd = h_row[0].boundary() # convert grid to line
                    hex_dict[h_row[1]] = hex_bnd # add grid geometry to dictionary
                    for int_pt in int_pts: # loop through intersection points
                        near_dist = 1000 # arbitrary large number
                        int_pt = arcpy.PointGeometry(int_pt,sr)
                        for hex_vert in hex_verts: # loop through hex vertices
                            if int_pt.distanceTo(hex_vert) < near_dist: # find shortest distance between intersection point and grid vertex
                                near_vert = hex_vert # remember geometry
                                near_dist = int_pt.distanceTo(hex_vert) # remember distance
                        vert_dict.setdefault(h_row[1],[]).append(arcpy.PointGeometry(near_vert,sr)) # store geometry in dictionary
                        points.append(arcpy.PointGeometry(near_vert,sr)) # add to points list
for k,v in vert_dict.iteritems(): # loop through participating vertices
    if len(v) < 2: # skip if there was only one vertex
        continue
    hex = hex_dict[k] # get hex grid geometry
    best_path = hex # longest line possible is hex grid boundary
    for part in hex:
        for int_vert in v: # loop through participating vertices
            for i,pnt in enumerate(part): # loop through hex grid vertices
                if pnt.equals(int_vert): # find vertex index on hex grid corresponding to current point
                    start_i = i
                    if start_i == 6:
                        start_i = 0
                    for dir in [[0,6,1],[5,-1,-1]]: # going to loop once clockwise, once counter-clockwise
                        past_pts = 0 # keep track of number of passed participating vertices
                        cur_line_arr = arcpy.Array() # polyline coordinate holder
                        cur_line_arr.add(part[start_i]) # add starting vertex to growing polyline
                        for j in range(dir[0],dir[1],dir[2]): # loop through hex grid vertices
                            if past_pts < len(v): # only make polyline until all participating vertices have been visited
                                if dir[2] == 1: # hex grid vertex index bookkeeping
                                    if start_i + j < 6:
                                        index = start_i + j
                                    else:
                                        index = (start_i - 6) + j
                                else:
                                    index = j - (5 - start_i)
                                    if index < 0:
                                        index += 6
                                cur_line_arr.add(part[index]) # add current vertex to growing polyline
                                for cur_pnt in v:
                                    if part[index].equals(cur_pnt): # check if the current vertex is a participating vertex
                                        past_pts += 1 # add to counter
                        if cur_line_arr.count > 1:
                            cur_line = arcpy.Polyline(cur_line_arr,sr)
                            if cur_line.length < best_path.length: # see if current polyline is shorter than any previous candidate
                                best_path = cur_line # if so, store polyline
    outlines.append(best_path) # add best polyline to list
arcpy.CopyFeatures_management(outlines, r'in_memory\outlines') # write list
arcpy.CopyFeatures_management(points, r'in_memory\mypoints') # write points, if you want

resim açıklamasını buraya girin

Notlar:

  • Bu komut dosyası döngüler içinde birçok döngü ve iç içe imleç içeriyor. Optimizasyon için kesinlikle yer var. Veri kümelerinizi birkaç dakika içinde geçirdim, ancak daha fazla özellik sorunu artıracak.

Bunun için teşekkür ederim, çok takdir ediyorum. Bu tam olarak görselleştirdiğim etkiyi gösteriyor . Bol yorumlar, kodu çalıştıramasam bile ne yaptığının özünü alabileceğim anlamına gelir. Arcpy olmasına rağmen, bunun pyqgis'te yapılabileceğinden eminim. Buradaki algoritma fikirleri ilginç (özellikle her altıgeni saat yönünde ve saat yönünün tersine bakmak ve en kısa yolu seçmek)
Steven Kay

2

Yol çizgisini, her bir segmentin altıgen tarafından tamamen kaplandığı segmentlere böldüyseniz, hangi altıgen çizgi segmentlerinin kullanılacağına dair kararınız, bölünmüş yol segmentinin centroidinden her altıgen tarafın orta noktasına olan mesafenin, altıgenin çapı (veya altıgenin içine uyan bir dairenin yarıçapından daha az).

Böylece, (her seferinde bir segment) altıgenin yarıçapına yakın mesafede bulunan altıgen çizgi parçalarını (her bir parçanın altıgenin bir tarafı olduğu) seçecekseniz, bu çizgi geometrilerini kopyalayabilir ve yol veri kümeniz için hangi benzersiz tanımlayıcıyı kullanırsanız kullanın.

Benzersiz tanımlayıcıda birleştirme konusunda sorun yaşıyorsanız, arabellek uygulayabilir ve yol veri kümenizin özelliklerini uygulamak için yalnızca bu segmentlerde konuma göre seçim yapabilirsiniz; bu şekilde çok büyük bir arabellekle yanlış eşleşme yapma konusunda endişelenmenize gerek kalmaz.

Çıtçıt aracıyla ilgili sorun, noktaları rastgele ayırmasıdır; o mükemmel toleransı bulmak zor. Bu metodoloji ile hangi altıgen çizgi segmentlerinin kullanılacağını doğru bir şekilde belirleyecek, ardından yol verilerinizin geometrisini değiştirecek (veya geometrileri farklı bir veri kümesine ekleyeceksiniz).

Ayrıca, altıgenin bir tarafından diğer tarafına atlayan çizgi parçalarıyla ilgili hala sorun yaşıyorsanız, çizgiyi köşelere göre segmentlere bölebilir, her bir çizginin uzunluğunu hesaplayabilir, ardından altıgenin bir tarafının ortalama uzunluğu.


1

Qgis 3.0'daki geometri kırıcı yeniden işlenmiştir ve artık farklı geometri türleri arasında yapışmaya izin vermektedir. Ayrıca birçok düzeltme var. 3.0 resmi olarak yayınlanmadan önce geliştirilmiş snapper'a erişmek için "günlük anlık görüntü" sürümünü deneyebilirsiniz.

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.