Tạo đa giác có kích thước bằng nhau cùng với PyQGIS?


42

Tôi muốn tạo đa giác dọc theo một dòng để sử dụng chúng cho AtlasCreator trong bước tiếp theo.

ArcMap có một công cụ gọi là Tính năng Chỉ mục Bản đồ Dải .

Với công cụ này, tôi có thể chọn chiều cao và chiều rộng của đa giác của mình (giả sử 8km x 4km) và tự động tạo / xoay chúng dọc theo đường.

Một trong những thuộc tính được tạo của mỗi đa giác là góc xoay mà tôi cần để xoay mũi tên phía bắc của mình trong Atlas Generator sau đó.

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

Có ai có ý tưởng làm thế nào để giải quyết nhiệm vụ này trong QGIS / với pyQGIS không? Các thuật toán Grass hoặc SAGA hoặc mô hình hộp công cụ prossessing có thể được sử dụng bên trong một plugin tùy chỉnh cũng sẽ ổn thôi;) Edit1: Tôi không chỉ cần các bản in mà còn cả các đa giác khi tôi muốn in bản đồ tất cả các đa giác / phạm vi như một số loại bản đồ tổng quan.

Edit2: Tôi đang cung cấp một tiền thưởng vì tôi vẫn đang tìm kiếm một giải pháp PyQGIS có thể được sử dụng trong Plugin -ISIS mà không cần cài đặt phần mềm ngoài QGIS (không có RDBMS như PostGIS / Oracle)


4
Điều này trông giống như một ý tưởng thú vị cho một plugin.
alphasoup

1
Là một suy nghĩ hoang dã, tôi nghĩ rằng một cái gì đó dựa trên sự khái quát hóa Peucker-Douglas có thể hoạt động
plablo09

1
có lẽ v.split.length, sau đó vẽ một đường thẳng giữa điểm bắt đầu và điểm cuối của các phân đoạn và sau đó v.buffer với tùy chọn "Không tạo mũ ở đầu của polylines"
Thomas B

1
Tôi rất thích bắt đầu một tiền thưởng cho câu hỏi này nhưng tôi chưa có đủ danh tiếng; (
Berlinmapper

2
Có thể có một số mã có thể tái sử dụng trong các triển khai "dòng theo nhãn". Hình chữ nhật của bạn giống như dấu chân của glyphs của một số phông chữ đơn cách.
dùng49584

Câu trả lời:


29

Câu hỏi thú vị! Đó là điều mà tôi muốn thử bản thân mình, vì vậy hãy thử xem.

Bạn có thể làm điều này trong PostGRES / POSTGIS với chức năng tạo ra một tập hợp đa giác.

Trong trường hợp của tôi, tôi có một bảng với một tính năng (MULTILINESTRING) đại diện cho một tuyến đường sắt. Nó cần sử dụng CRS tính bằng mét, tôi đang sử dụng osgb (27700). Tôi đã thực hiện 4km x 2km 'trang'.

Tại đây, bạn có thể thấy kết quả ... thứ màu xanh lá cây là mạng lưới đường, được cắt vào vùng đệm 1km quanh đường sắt, tương ứng với chiều cao của đa giác độc đáo.

postgis tạo bản đồ dải

Đây là chức năng ...

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

Sử dụng chức năng này

Đây là một ví dụ; 4km x 2km trang, epsg: 27700 và 10% trùng lặp

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

Sau khi chạy, bạn có thể xuất từ ​​PGAdminIII sang tệp csv. Bạn có thể nhập cái này vào QGIS, nhưng bạn có thể cần phải đặt CRS theo cách thủ công cho lớp - QGIS không sử dụng SRID trong EWKT để đặt CRS lớp cho bạn: /

Thêm thuộc tính mang

Điều này có thể được thực hiện dễ dàng hơn trong postgis, nó có thể được thực hiện trong các biểu thức của QGIS nhưng bạn sẽ cần phải viết một số mã. Một cái gì đó như thế này ...

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

Hãy cẩn thận

Đó là một chút hack-together và chỉ có cơ hội để thử nghiệm trên một tập dữ liệu.

Không chắc chắn 100% hai đỉnh bạn sẽ cần chọn trong bản cập nhật thuộc tính mang đó query.. có thể cần thử nghiệm.

Tôi phải thú nhận rằng tôi không biết tại sao tôi cần phải thực hiện một công thức phức tạp như vậy để xoay đa giác để phù hợp với phân khúc dòng hiện tại. Tôi nghĩ rằng tôi có thể sử dụng đầu ra từ ST_Azimuth () trong ST_Rotate (), nhưng dường như không.


Câu trả lời của bạn thực sự rất hay và chắc chắn tôi sẽ cố gắng. Một hạn chế đối với tôi là tôi không thể sử dụng postgres cho dự án tôi đang làm và cần một cái gì đó không phụ thuộc vào phía máy chủ. Nhưng có lẽ tôi có thể sử dụng logic tuyệt vời để tái tạo một cái gì đó như thế với pyQGIS.
Berlinmapper

2
nếu đó là trường hợp, hãy xem lớp QssGeometry . Nó có một tập hợp con các hoạt động hình học của PostGIS và sẽ là điểm khởi đầu tốt nếu bạn muốn đi theo lộ trình pyQGIS. Thuật toán sẽ được chuyển sang pyQGIS ..
Steven Kay

3
Tôi nghĩ đối với postgis một cách tiếp cận sử dụng ST_Simplify để tạo các đường tham chiếu và ngắt dòng thành các phân đoạn và sau đó sử dụng ST_BufferST_En phong bì sẽ ngắn hơn và hiệu quả hơn.
Matthias Kuhn

@Matthias Kuhn: nếu tôi chia dòng thành các phân đoạn, tôi có thể nhận được các dòng có kích thước bằng nhau nhưng không nhất thiết phải là đa giác có kích thước bằng nhau. ví dụ: nếu đường thẳng khá 'cong' thì đa giác có thể sẽ ngắn hơn, phải không?
Berlinmapper

2
Tôi đã thử nghiệm giải pháp của bạn và PyQGIS-Phiên bản tập lệnh của bạn. Mọi ý tưởng của bạn làm thế nào để giải quyết một số vấn đề nhỏ còn lại: bit.ly/1KL7JHn ?
Berlinmapper

12

Có giải pháp khác biệt. Và điều này có thể làm việc với đa tuyến đơn giản và nhiều thực thể được chọn

sơ đồ khối:

  1. Thông số

    1. chọn hướng để tạo và đọc chỉ mục (từ trái sang phải, từ bắc sang nam ...)
    2. đặt kích thước đối tượng

    shape = (4000,8000) # (<width>,<length>)
    1. xác định chồng chất (10% theo mặc định?)
  2. trong đó
    1. Đặt hàng polyline (so sánh điểm bắt đầu và điểm kết thúc) thứ tự tùy thuộc vào lựa chọn định hướng của bạn> tạo một đỉnh thứ tự featureclass OrderNodes
  3. vòng lặp trên OrderNodes

    1. tạo cho bạn điểm đầu tiên là mỏ neo

    2. cho mỗi đỉnh thêm nó vào dict x, y, id và tính một vectơ

    3. tạo đa giác (theo chiều dài và hướng vectơ) với việc giảm chồng chất (10% / 2)> 5% đa giác trái 5% đa giác phải với cùng một điểm neo
    4. Dừng khi điểm đỉnh trước đó nằm ngoài đa giác hoặc nếu vectơ len> để định hình chiều dài
    5. Tạo đa giác với giải pháp tốt trước đó và đặt điểm neo với vị trí tốt cuối cùng
    6. Thực hiện vòng lặp mới và đặt lại dict x, y, id để tạo đối tượng đa giác tiếp theo.

Bạn có thể thay đổi đề xuất này nếu nó không thực sự rõ ràng hoặc bình luận.


Điều này nghe có vẻ phức tạp nhưng tôi phải thừa nhận rằng tôi chưa biết cách sử dụng nó cho người lập mô hình hoặc PyQGIS. Nhân tiện: hệ số chồng chất là gì ?.
Berlinmapper

@Berlinmapper là một phần của đa giác với superpostion 8000 x 10% trong trường hợp này. Bạn có thể chọn một cái khác hoặc tạo một sự thay thế khoảng cách giữa các polygone. Bạn có thể thấy rằng trong tất cả các tập bản đồ để chỉ ra trang gạch tiếp theo ở góc
GeoStoneMarten

giải pháp của bạn có nghĩa là được sử dụng với pyQGIS hoặc hộp công cụ xử lý? Nghe có vẻ hay nhưng tôi vẫn không biết cách tiến hành
Berlinmapper

1
@Berlinmapper Tôi nghĩ rằng bạn cần sử dụng pyQGIS để tạo tập lệnh quy trình và đặt tham số đầu vào và đầu ra trong hộp công cụ xử lý hoặc plugin QGIS. Tương tự như arcgistoolbox. Tôi không có thời gian thực sự để làm điều đó và kiểm tra nó.
GeoStoneMarten

12

Steven Kays trả lời bằng pyqgis. Chỉ cần chọn các dòng trong lớp của bạn trước khi chạy tập lệnh. Kịch bản không hỗ trợ linemerging vì vậy nó không thể hoạt động trên lớp với đa tuyến

#!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
Tuyệt quá. Tôi đã thử nghiệm giải pháp. Bất kỳ ý tưởng làm thế nào để giải quyết những vấn đề này giải pháp vẫn có: bit.ly/1KL7JHn ?
Berlinmapper

có lẽ có một số "cảm hứng" ở đây: github.com/maphew/arcmapbook/blob/master/Visual_Basic/ mẹo
Thomas B

tài nguyên cảm ơn để hiểu cách thức hoạt động của công cụ ArcMap. Thật không may tôi ', tôi không sử dụng VB nhưng có lẽ ai đó có thể sử dụng nó để đăng câu trả lời / nhận xét;)
Berlinmapper

4

Hai câu trả lời (tại thời điểm đăng) là khéo léo và giải thích tốt. Tuy nhiên, cũng có một giải pháp RẤT đơn giản nhưng hiệu quả có thể cho việc này (giả sử bạn sẽ chấp nhận tất cả các bản đồ của mình được căn chỉnh theo hướng Bắc lên theo cách truyền thống, thay vì hướng Bắc ngẫu nhiên dựa trên sông). Nếu bạn muốn xoay, có thể nhưng phức tạp hơn một chút (xem phía dưới).

Đầu tiên hãy xem bài viết của tôi ở đây . Điều này cung cấp cho bạn cách hướng dẫn để tạo các bản đồ cho Atlas. Phương pháp bạn muốn là một sự thích ứng là 'Quy trình làm việc 2' theo cách thực hiện. Chia tính năng tuyến tính của bạn theo các đỉnh hoặc độ dài và đệm các tính năng theo số lượng bất kỳ. Số tiền bạn đệm theo sẽ quyết định một phần sự chồng chéo (nhưng xem bên dưới) nhưng quan trọng hơn, nó tạo ra một tính năng với một khu vực. Bạn có thể sử dụng bất kỳ số lượng bổ trợ nào để phân chia các dòng nhưng GRASS v.split.length và v.split.vert là các tùy chọn tốt (có sẵn trong Hộp công cụ xử lý).

Khi đã bật Atlas Generation trong Trình soạn thảo bản đồ và chọn lớp đệm của bạn, hãy quay lại tab vật phẩm và chọn đối tượng bản đồ của bạn. Kiểm tra 'Được điều khiển bởi Atlas' và trong trường hợp sử dụng của bạn, tôi sẽ chọn tính năng Margin xung quanh. Điều này sẽ kiểm soát sự chồng chéo của bạn giữa các bản đồ (thay vào đó bạn có thể thích tỷ lệ cố định).

Bạn có thể xem trước Atlas của mình bằng Nút Atlas Xem trước trong thanh công cụ hàng đầu của nhà soạn nhạc và xem nó sẽ tạo ra bao nhiêu trang. Lưu ý bạn có thể chọn xuất tất cả các trang trong một tệp PDF hoặc dưới dạng các tệp riêng biệt.

Để có được bản đồ xoay dọc theo dòng, có một trường xoay trong thuộc tính Mục Trình soạn đồ. Bạn sẽ cần đặt một biểu thức (sử dụng nút nhỏ ở bên phải hộp xoay). Chọn biến là tùy chọn của bạn và sau đó chỉnh sửa. Trình xây dựng biểu thức sẽ bật lên và trong đó bạn có thể truy cập vào hình học hoặc các trường của các tính năng bản đồ. Sau đó, bạn có thể xây dựng một biểu thức để xoay bản đồ theo vòng xoay của các tính năng (bạn có thể tính toán ổ trục bằng cách sử dụng điểm bắt đầu và điểm kết thúc của từng đoạn đường và một chút trig). Lặp lại quy trình tương tự để xoay mũi tên Bắc của bạn (sử dụng cùng một biểu thức hoặc biến được tính toán trước).


cảm ơn vì giải pháp này nhưng tôi nghĩ theo cách này tôi sẽ không nhận được đa giác của các phạm vi duy nhất tôi muốn in. tôi cần chúng để tạo ra một "bản đồ tổng quan" với tất cả các phạm vi in.
Berlinmapper
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.