Làm cách nào để chụp mạng đường bộ vào lưới lục giác trong QGIS?


13

Tôi đang cố gắng sử dụng QGIS 2.14 để gắn một mạng lưới đường vào lưới hình lục giác, nhưng tôi đang nhận được các tạo tác lạ.

Tôi đã tạo lưới hex với MMQGIS , các ô có kích thước khoảng 20 x 23 m. Tôi đã đệm mạng lưới đường bộ bằng cách 1m và làm tăng độ rắn nó để có một nút cứ vài mét. Bạn có thể thấy những gì tôi đang cố gắng đạt được dưới đây. Như bạn thấy, tôi có thể làm cho nó hoạt động trong một số trường hợp: -

  • màu xanh là đường dày đặc (đường đệm)
  • màu đỏ là phiên bản 'hexified' - đây là những gì tôi muốn tìm
  • màu xám là lưới hex

nhập mô tả hình ảnh ở đây

Sau đó, tôi đã sử dụng tính năng hình học Snap mới để gắn các nút vào góc lục giác gần nhất. Kết quả rất hứa hẹn, nhưng dường như có một số trường hợp cạnh mà đường thẳng mở rộng ra để điền vào hình lục giác (hoặc một phần của nó): -

nhập mô tả hình ảnh ở đây

Lý do cho bộ đệm là hình học Snap không cho phép bạn chụp vào một lớp có hình dạng khác nhau. Ví dụ: bạn không thể chụp các nút trên lớp LINE để trỏ vào lớp POINT). Nó có vẻ là hạnh phúc nhất khi chụp POLYGON cho POLYGON.

Tôi nghi ngờ các con đường mở rộng ra khi một bên của đường bộ đệm nhảy sang một bên của ô hex và phía bên kia nhảy sang phía bên kia của ô hex. Trong ví dụ của tôi, những con đường xuyên qua phía đông-tây ở một góc nhọn dường như là tồi tệ nhất.

Những điều tôi đã thử, không thành công: -

  • đệm mạng lưới đường bằng một lượng rất nhỏ, vì vậy nó vẫn là một đa giác nhưng rất mỏng.
  • tăng mật độ các ô hex (vì vậy có các nút dọc theo các cạnh, không chỉ ở các góc)
  • thay đổi khoảng cách chụp tối đa (điều này có hiệu ứng lớn nhất, nhưng dường như tôi không thể tìm thấy một giá trị lý tưởng)
  • sử dụng các lớp LINE, không phải POLYGON

Tôi thấy rằng nếu tôi thay đổi chỉ sử dụng các lớp LINE, Nó hoạt động được một lúc, sau đó gặp sự cố. Nó dường như để lưu công việc của nó khi nó đi - một số dòng đã được xử lý một phần.

nhập mô tả hình ảnh ở đây

Có ai biết bất kỳ cách nào khác để ghi điểm trên một dòng đến điểm gần nhất trên một lớp đa giác / đa giác khác, lý tưởng mà không cần sử dụng postgres / postgis (mặc dù một giải pháp với postgis cũng được hoan nghênh)?

BIÊN TẬP

Đối với bất kỳ ai muốn đi, tôi đã đặt một dự án QGIS khởi đầu ở đây trên Dropbox . Điều này bao gồm các lớp Hex Grid và mật độ dòng. (Mạng lưới đường bộ là từ OSM, vì vậy có thể được tải xuống bằng QuickOSM, ví dụ: nếu bạn cần lấy bản gốc để làm sáng tỏ các con đường).

Lưu ý rằng đó là OSGB (epsg: 27700), một UTM được bản địa hóa cho Vương quốc Anh, với đơn vị tính bằng mét.


3
Bạn có thể vui lòng chia sẻ một tập dữ liệu mẫu? Tôi muốn dùng thử nhưng không muốn trải qua quá trình tạo dữ liệu mẫu từ đầu.
Germán Carrillo

@ GermánCarrillo - cảm ơn. Tôi đã thêm một liên kết đến một dự án mẫu cho câu hỏi.
Steven Kay

Câu trả lời:


14

Giải pháp của tôi liên quan đến một kịch bản PyQGIS nhanh hơn và hiệu quả hơn một quy trình công việc liên quan đến chụp nhanh (tôi cũng đã thử nó). Sử dụng thuật toán của tôi, tôi đã thu được những kết quả này:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây

Bạn có thể chạy các đoạn mã sau theo trình tự từ trong QGIS (trong bảng điều khiển Python của QGIS). Cuối cùng, bạn nhận được một lớp bộ nhớ với các tuyến được chụp vào QGIS.

Điều kiện tiên quyết duy nhất là tạo Shapefile đường nhiều phần (sử dụng Processing->Singleparts to multipart, tôi đã sử dụng trường fictitiuoslàm Unique ID fieldtham số). Điều này sẽ cung cấp cho chúng tôi một roads_multipart.shptập tin với một tính năng duy nhất.

Đây là thuật toán giải thích:

  1. Lấy các cạnh lục giác gần nhất nơi các tuyến đường giao nhau. Với mỗi hình lục giác, chúng ta tạo ra 6 hình tam giác giữa mỗi cặp đỉnh lân cận và tâm tương ứng. Nếu bất kỳ con đường nào giao nhau với một hình tam giác, đoạn được chia sẻ bởi hình lục giác và hình tam giác sẽ được thêm vào tuyến đường bị gãy cuối cùng. Đây là phần nặng hơn của toàn bộ thuật toán, phải mất 35 giây để chạy trên máy của tôi. Trong hai dòng đầu tiên có 2 đường dẫn Shapefile, bạn nên điều chỉnh chúng để phù hợp với đường dẫn tệp của riêng bạn.

    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. Loại bỏ các phân đoạn bị ngắt kết nối (hoặc 'mở') bằng cách sử dụng danh sách Python, bộ dữ liệu và từ điển . Tại thời điểm này, có một số phân đoạn bị ngắt kết nối, nghĩa là các phân đoạn có một đỉnh bị ngắt kết nối nhưng một phân đoạn khác được kết nối với ít nhất 2 phân đoạn khác (xem các phân đoạn màu đỏ trong hình tiếp theo). Chúng ta cần phải thoát khỏi chúng.

    nhập mô tả hình ảnh ở đây

    # 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. Bây giờ chúng ta có thể tạo một lớp vectơ từ danh sách tọa độ và tải nó vào bản đồ QGIS :

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

Một phần khác của kết quả:

nhập mô tả hình ảnh ở đây

Nếu bạn cần các thuộc tính trong các tuyến bị ngắt, chúng tôi có thể sử dụng Chỉ mục không gian để nhanh chóng đánh giá các giao lộ (chẳng hạn như trong /gis//a/130440/4972 ), nhưng đó là một câu chuyện khác.

Hi vọng điêu nay co ich!


1
cảm ơn bạn, nó hoạt động hoàn hảo Có vấn đề khi dán nó vào bảng điều khiển python ... Tôi đã lưu nó dưới dạng tệp .py trong trình chỉnh sửa python của qgis và nó chạy tốt từ đó. Bước nhiều phần loại bỏ các thuộc tính, nhưng một tham gia bộ đệm / không gian sẽ khắc phục điều đó!
Steven Kay

1
Tuyệt quá! Vui mừng cuối cùng nó đã giải quyết vấn đề bạn đang phải đối mặt. Tôi muốn biết trường hợp sử dụng mà bạn đang giải quyết. Bạn có nghĩ rằng chúng ta có thể tận dụng điều này để trở thành một plugin QGIS hoặc có lẽ là một tập lệnh được đưa vào Xử lý tập lệnh không?
Germán Carrillo

1
trường hợp sử dụng mà tôi có trong đầu là các bản đồ giao thông công cộng như Bản đồ ống, trong đó bạn cần phải chộp các đường vào lưới tàu hoặc đến một tập hợp các góc bị hạn chế. Điều này có thể được thực hiện thủ công bằng cách số hóa, nhưng tôi đã quan tâm xem liệu nó có thể được tự động hóa hay không. Tôi đã sử dụng các hình lục giác vì chúng dễ tạo ra, trực quan thú vị và có các góc không đúng góc. Tôi nghĩ rằng điều này đáng để xem xét chi tiết hơn, đặc biệt là nếu nó có thể được khái quát hóa để làm việc với các tàu khác ...
Steven Kay

1
Ý tưởng đằng sau kịch bản sẽ hoạt động trên các lưới hình tam giác, hình vuông, hình ngũ giác, hình lục giác, v.v.
Germán Carrillo

6

Tôi đã làm điều đó trong ArcGIS, chắc chắn có thể được thực hiện bằng cách sử dụng QGIS hoặc đơn giản là python với gói có khả năng đọc hình học. Đảm bảo rằng các đường đại diện cho mạng, tức là chỉ giao nhau ở hai đầu. Bạn đang làm việc với OSM, tôi cho rằng đó là trường hợp.

  • Chuyển đổi các đa giác lân cận thành các đường và cân bằng chúng, để chúng trở thành một mạng hình học.
  • Đặt điểm ở cuối của chúng - Điểm Voronoi: nhập mô tả hình ảnh ở đây
  • Đặt các điểm trên đường với khoảng cách đều đặn là 5 m, đảm bảo đường mạng có tên duy nhất tốt:

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

  • Đối với mọi Điểm đường tìm tọa độ của Điểm Voronoi gần nhất: nhập mô tả hình ảnh ở đây
  • Tạo đường Road trực tuyến bằng cách kết nối các điểm gần nhất theo cùng thứ tự: nhập mô tả hình ảnh ở đây

Nếu bạn không muốn thấy điều này: nhập mô tả hình ảnh ở đây

Đừng cố sử dụng các điểm chuỗi trên Voronoi Lines. Tôi sợ nó sẽ chỉ làm cho nó tồi tệ hơn. Do đó, tùy chọn duy nhất của bạn là tạo mạng từ các đường Voronoi và tìm các tuyến giữa các điểm cuối đường, đó cũng không phải là vấn đề lớn


Điều này là rất tốt, cảm ơn bạn! Bạn đề cập đến việc sử dụng các dòng voronoi, không quá quen thuộc với điều đó (Voronois từ các điểm, tôi có thể hiểu). Bạn có nghĩa là mỗi dòng được bao quanh bởi một đa giác của tất cả các điểm gần nhất với dòng đó? (Tôi không biết về cách làm điều đó trong QGIS). Hay bạn có nghĩa là các đường biên từ một lưới voronoi bình thường, dựa trên các điểm?
Steven Kay

Ranh giới của đa giác gần. Btw tôi dừng lại quá sớm. Để hoàn thành nhiệm vụ, đủ để phân chia kết quả thứ 1 ở đỉnh, thêm điểm ở giữa và lặp lại quy trình
FelixIP

4

Tôi nhận ra rằng bạn đang yêu cầu một phương pháp QGIS, nhưng hãy đồng ý với tôi về một câu trả lời khó hiểu:

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

nhập mô tả hình ảnh ở đây

Ghi chú:

  • Kịch bản lệnh này chứa nhiều vòng lặp trong các vòng lặp và một con trỏ lồng nhau. Chắc chắn có chỗ để tối ưu hóa. Tôi đã chạy qua bộ dữ liệu của bạn trong một vài phút, nhưng nhiều tính năng hơn sẽ gây ra vấn đề.

Cảm ơn bạn vì điều này, nhiều đánh giá cao. Điều này cho thấy chính xác hiệu ứng tôi đang hình dung. Các ý kiến ​​đa dạng có nghĩa là tôi có thể nhận được ý chính về những gì bạn đang làm ngay cả khi tôi không thể chạy mã. Mặc dù nó rất hấp dẫn, tôi chắc chắn rằng điều này sẽ có thể thực hiện được trong pyqgis. Các ý tưởng thuật toán ở đây rất thú vị (đặc biệt là nhìn cả hai chiều kim đồng hồ và ngược chiều kim đồng hồ trên mỗi hình lục giác và chọn vòng tròn ngắn nhất)
Steven Kay

2

Nếu bạn phân chia đường thành các đoạn trong đó mỗi phân đoạn được chứa hoàn toàn bởi hình lục giác, quyết định của bạn về việc sử dụng phân đoạn đường lục giác nào sẽ là liệu khoảng cách từ tâm của đoạn đường phân chia đến mỗi điểm giữa của hình lục giác có nhỏ hơn một nửa đường kính của hình lục giác (hoặc nhỏ hơn bán kính của hình tròn vừa với hình lục giác).

Do đó, nếu bạn (một phân đoạn tại một thời điểm) chọn các phân đoạn dòng hình lục giác (trong đó mỗi phân đoạn là một cạnh của hình lục giác) nằm trong khoảng cách bán kính của hình lục giác, bạn có thể sao chép các hình dạng dòng đó và hợp nhất chúng trên bất kỳ định danh duy nhất bạn sử dụng cho tập dữ liệu đường bộ của bạn.

Nếu bạn gặp khó khăn khi hợp nhất trên mã định danh duy nhất, bạn có thể áp dụng bộ đệm và chọn theo vị trí chỉ trên các phân đoạn đó để áp dụng các thuộc tính của tập dữ liệu đường bộ của bạn; theo cách đó, bạn sẽ không phải lo lắng về việc tạo sai khớp với bộ đệm quá lớn.

Vấn đề với công cụ snap là nó chộp điểm bừa bãi; thật khó để tìm thấy sự dung nạp hoàn hảo để sử dụng. Với phương pháp này, bạn sẽ xác định chính xác các phân đoạn dòng hình lục giác sẽ sử dụng, sau đó thay thế hình học của dữ liệu đường của bạn (hoặc chèn hình học vào một tập dữ liệu khác).

Ngoài ra, nếu bạn vẫn gặp sự cố với các phân đoạn dòng nhảy từ một bên của hình lục giác sang bên kia, bạn có thể chia dòng thành các phân đoạn theo các đỉnh, tính toán độ dài của mỗi dòng, sau đó xóa bất kỳ phân đoạn dòng nào lớn hơn chiều dài trung bình của một bên của hình lục giác.


1

Snapper hình học trong qgis 3.0 đã được làm lại và bây giờ cho phép chụp giữa các loại hình học khác nhau. Nó cũng có rất nhiều bản sửa lỗi. Bạn có thể thử phiên bản "ảnh chụp nhanh hàng ngày" để có quyền truy cập vào cá hồng được cải tiến trước khi 3.0 được phát hành chính thức.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.