Генерування полігонів однакового розміру поряд з PyQGIS?


42

Я хотів би створити багатокутники по лінії, щоб використовувати їх для AtlasCreator на наступному кроці.

У ArcMap є інструмент під назвою Особливості індексу Strip Map .

За допомогою цього інструменту я можу вибрати висоту та ширину своїх багатокутників (скажімо, 8 км х 4 км) і автоматично виробляти / повертати їх по лінії.

Одним із створених атрибутів кожного багатокутника є кут повороту, який мені потрібен, щоб згодом обертати свої північні стрілки в Atlas Generator.

введіть тут опис зображення

Хтось має ідею, як вирішити це завдання в QGIS / з pyQGIS? Трасові або SAGA-алгоритми або модель-програмувальник інструментів, які можна використовувати у спеціальному плагіні, також буде добре;) Edit1: Мені потрібні не тільки розширення для друку, але і самі полігони, оскільки я хочу надрукувати карту за допомогою всі полігони / розширення як якась оглядова карта.

Edit2: Я пропоную винагороду, оскільки я все ще шукаю рішення PyQGIS, яке можна використовувати в QGIS-плагіні без необхідності встановлення програмного забезпечення окрім QGIS (ніяких RDBMS, як PostGIS / Oracle)


4
Це виглядає як забавна ідея для плагіна.
alphabetasoup

1
Як дика думка, я думаю, що щось, що базується на узагальненні Пеукера-Дугласа, може спрацювати
plablo09

1
можливо v.split.length, потім провести пряму лінію між початковою і кінцевою точкою сегментів, а потім v.buffer з опцією "Не робити шапки на кінцях поліліній"
Thomas B

1
Я хотів би почати банально з цього питання, але мені ще не вистачає репутації; (
Berlinmapper

2
Можливо, буде якийсь код для багаторазового використання у реалізаціях "label-follow line". Ваші прямокутники схожі на сліди гліфів якогось односхилого шрифту.
користувач30184

Відповіді:


29

Цікаве запитання! Це те, що я хотів спробувати сам, тому подумав.

Це можна зробити в PostGRES / POSTGIS за допомогою функції, яка генерує набір багатокутників.

У моєму випадку у мене є таблиця з однією особливістю (МНОГОПОЛОЖЕННЯ), яка являє собою залізничну лінію. Для цього потрібно використовувати 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' ;

Використання цієї функції

Ось приклад; Сторінок 4 км х 2 км, 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 ..
Стівен Кей

3
Я думаю, що для postgis підхід, що використовує ST_Simplify для генерації опорних ліній і розбиття лінії на сегменти, а потім використання ST_Buffer і ST_Envelope було б коротшим і ефективнішим.
Маттіас Кун

@ Маттіас Кун: якщо я перерву лінію на відрізки, я міг би отримати лінії однакового розміру, але не обов'язково також багатокутники однакового розміру. Наприклад, якщо лінія є досить «кривою», багатокутник, ймовірно, буде коротшим, чи не так?
Berlinmapper

2
Я перевірив ваше рішення та PyQGIS-версію вашого сценарію. Будь-яка ідея, як вирішити деякі незначні проблеми: bit.ly/1KL7JHn ?
Berlinmapper

12

Є різні рішення. І це може працювати з простою полілінією та декількома вибраними утвореннями

блок-схема:

  1. Параметри

    1. виберіть орієнтацію для генерації та зчитування індексу (зліва направо, з півночі на південь ...)
    2. встановити розмір об'єкта

    shape = (4000,8000) # (<width>,<length>)
    1. визначити коефіцієнт суперпозиції (10% за замовчуванням?)
  2. в цьому
    1. Замовлення поліліній (порівняння початкової та кінцевої точки) впорядкування залежно від вибору орієнтації> створити функцію впорядкування вершинкласс OrderNodes
  3. цикл на OrderNodes

    1. створити вам першу точку як якір

    2. для кожної вершини додайте її на d x, y, id та обчисліть вектор

    3. генерувати багатокутник (по довжині та орієнтації вектора) із зменшенням суперпозиції (10% / 2)> 5% лівий многокутник 5% правий багатокутник з такою ж точкою якоря
    4. Зупиніться, коли точка прецедентної вершини виходить з багатокутника або якщо вектор len має> форму довжини
    5. Створіть багатокутник з попереднім хорошим рішенням і встановіть опорну точку з останньою гарною позицією
    6. Виконайте новий цикл і скиньте dict x, y, id для створення наступного об'єкта багатокутника.

Ви можете змінити цю пропозицію, якщо вона не зовсім зрозуміла або коментується.


це звучить витончено, але я повинен визнати, що я ще не знаю, як це використовувати для моделера або PyQGIS. до речі: що таке коефіцієнт суперпозиції ?.
Berlinmapper

@Berlinmapper, що є частиною багатокутника із суперпозицією 8000 х 10% у цьому випадку. Ви можете обрати інший або створити фіксовану відстань між полігоною. Ви можете побачити, що в усіх
атласах

чи ваше рішення призначене для використання з pyQGIS або панеллю інструментів для обробки? це звучить чудово, але я все ще не знаю, як діяти
Berlinmapper

1
@Berlinmapper Я думаю, що вам потрібно використовувати pyQGIS для створення сценарію процесу та встановлення вхідного та вихідного параметрів при обробці панелі інструментів чи плагіну QGIS. Те саме, що arcgistoolbox. Я фактично не маю цього робити і тестувати.
GeoStoneMarten

12

Стівен Кейс відповідає пикгісом. Просто виберіть рядки у вашому шарі перед запуском сценарію. Сценарій не підтримує 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

спасибі.великий ресурс, щоб зрозуміти, як працює інструмент ArcMap. на жаль, я не звик до VB, але, можливо, хтось ще може використати його для публікації відповіді / коментаря;)
Berlinmapper

4

Дві відповіді (на момент публікації) геніальні та добре пояснені. Однак для цього також можливе ДУЖЕ просте, але ефективне рішення (якщо припустити, що ви приймете всі свої карти, вирівняні з Північчю вгору традиційним способом, а не випадковим північним напрямком на річці). Якщо ви хочете обертання, це можливо, але трохи складніше (див. Знизу).

Спочатку подивіться тут на моє повідомлення . Це дає вам інструкцію для створення карт покриття для Atlas. Метод, який ви хочете, є адаптацією "Workflow 2" у практичному описі. Розділіть лінійну функцію по вершинах або довжині та буферні функції на будь-яку кількість. Кількість, яку ви буферуєте, частково диктуватиме перекриття (але див. Нижче), але що ще важливіше, воно створює функцію з областю. Ви можете використовувати будь-яку кількість плагінів для розділення ліній, але GRASS v.split.length і v.split.vert є хорошими варіантами (доступні в панелі інструментів обробки).

Увімкнувши Atlas Generation у Map Composer та вибравши захищений шар, перейдіть назад на вкладку «Елементи» та виберіть об’єкт карти. Поставте прапорець "Контрольовано Атладом", і у вашому випадку використання я б обрав функцію Маржа навколо. Це дозволить контролювати перекриття між картами (ви можете віддати перевагу фіксованому масштабу).

Ви можете попередньо переглянути Атлас за допомогою кнопки «Попередній перегляд атласу» у верхній панелі інструментів композитора і побачити, скільки сторінок він створить. Примітка. Ви можете експортувати всі сторінки в один PDF або як окремі файли.

Щоб карта оберталася вздовж лінії, у властивостях елемента "Композитор карт" є поле обертання. Вам потрібно буде встановити вираз (скористайтесь кнопкою праворуч від поля обертання). Виберіть змінну як варіант, а потім Змініть. Створюється виразник, і там ви зможете отримати доступ до геометрії чи полів атласних функцій. Потім ви можете побудувати експрес для обертання карти відповідно до обертання функцій (ви можете обчислити підшипник, використовуючи початкову та кінцеву точки кожного відрізка рядка та трохи потрійку). Повторіть той же процес, щоб повернути стрілку Північ (використовуючи той самий вираз або попередньо обчислену змінну).


дякую за це рішення. але я думаю, що таким чином я не отримав би багатокутники одиничних розширень, які я хочу надрукувати. Мені потрібно, щоб вони також створили "огляд карти" з усіма розширеннями для друку.
Berlinmapper
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.