Малюємо хвилясті, химерні лінії в QGIS?


21

Чи є функція або плагін QGIS для того, щоб намалювати химерну лінію?

Я використовував Spline Tool, щоб вручну намалювати деякі хвилі, але це забирає багато часу. Якщо можливо, я хотів би намалювати щось на кшталт:

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

Inkscape Function Plotter ( sin(x)крива).


Дійсно цікаве питання! Ви думаєте про інструмент для миттєвого малювання ліній (наприклад, коли ви малюєте просту функцію лінії за допомогою миші), або, можливо, спосіб отримати цей результат, починаючи з координат як вхідних даних (зрештою з точок, ліній чи багатокутників)?
mgri

1
@mgri Дякую за ваш коментар Я розраховую використовувати цю лінію для позначення кордону з деякими невизначеностями (як коливальна берегова лінія), тому первинною ідеєю було перетворення полілінії (заздалегідь визначеними координатами) у хитання. Але ідея миттєвого малювання такого типу ліній теж здається привабливою.
Казухіто

Будь ласка, подивіться, чи допоможе моє рішення. Я не перевіряв це широко, тому, будь ласка, дайте мені знати, якщо щось піде не так (я зараз не можу це зробити, але я, мабуть, відредагую свою відповідь з додатковою інформацією).
mgri

Відповіді:


18

Я пропоную рішення за допомогою PyQGIS. Він повинен працювати як для шарів Linestring, так і для MultiLineString.

Це рішення засноване на створенні напівкруглих кілець, тому потрібно встановити значення діаметра (тобто stepзмінну в коді нижче). Обраний вами крок не буде реальним кроком, який застосовується, оскільки він коригується на основі довжини рядка (але це буде дійсно схоже на початково встановлене значення). Вам потрібно зробити кілька спроб, перш ніж знайти найкраще значення для stepзмінної.

Код також вимагає другого (необов’язкового) параметра (називається crv_angle), який допомагає зменшити або збільшити кривизну для кілець (я провів кілька тестів для цього, тому пропоную залишити 45 градусів як кут за замовчуванням, оскільки це призведе до справжнього кругообігу кільця).

Вам потрібно запустити цей код лише з консолі Python:

from math import sin, cos, radians

step = 3 # choose the proper value (e.g. meters or degrees) with reference to the CRS used
crv_angle = 45 # degrees

def segment(polyline):
    for x in range(0, len(polyline) - 1):
        first_point = polyline[x]
        second_point = polyline[x +1]
        seg = QgsGeometry.fromPolyline([first_point, second_point])
        tmp_azim = first_point.azimuth(second_point)
        len_feat = seg.length()
        parts = int(len_feat/step)
        real_step = len_feat/parts # this is the real step applied

        points = []
        current = 0
        up = True

        while current < len_feat:
            if up:
                round_angle = radians(90 - (tmp_azim - crv_angle))
                up = False
            else:
                round_angle = radians(90 - (tmp_azim + crv_angle))
                up = True
            first = seg.interpolate(current)
            coord_x, coord_y = (first.asPoint().x(), first.asPoint().y())
            p1=QgsPointV2(coord_x, coord_y)
            dist_x, dist_y = ((real_step*sin(rad_crv_angle))* cos(round_angle), (real_step*sin(rad_crv_angle)) * sin(round_angle))
            p2 = QgsPointV2(coord_x + dist_x, coord_y + dist_y)
            points.extend([p1, p2])
            current += real_step

        second = seg.interpolate(current + real_step)
        p3=QgsPointV2(second.asPoint().x(), second.asPoint().y())
        points.append(p3)

        circularRing = QgsCircularStringV2()
        circularRing.setPoints(points) # set points for circular rings
        fet = QgsFeature()
        fet.setGeometry(QgsGeometry(circularRing))
        prov.addFeatures([fet])

layer = iface.activeLayer() # load the input layer as you want
crs = layer.crs().toWkt()
rad_crv_angle = radians(crv_angle)

# Create the output layer
outLayer = QgsVectorLayer('Linestring?crs='+ crs, 'wiggly_line' , 'memory')
prov = outLayer.dataProvider()
fields = layer.pendingFields()
prov.addAttributes(fields)
outLayer.updateFields()

for feat in layer.getFeatures():
    geom = feat.geometry()
    polyline = geom.asPolyline()
    segment(polyline)

# Add the layer to the Layers panel
QgsMapLayerRegistry.instance().addMapLayer(outLayer)

і це створить новий шар пам'яті рядків із очікуваним результатом:

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


Оце Так! Я не можу перестати грати з цим, таким чудовим. Зверху сам вихід може використовуватися для подальших операцій, таких як буфер. Дякую @mgri І останнє, що інколи я бачу, як маленькі кола з’являються біля вершин або біля них. Чи можна цього уникнути? Я можу їх видалити, видаливши центральний вузол такого кола інструментом Node (так що це не велика справа).
Казухіто

1
@Kazuhito, будь ласка, дивіться мій відредагований код. Здається, що щось було не так з дискретизацією, і я сподіваюся, що це вже виправлено. Я провів кілька тестів і, здається, працює добре (код також читабельніше).
mgri

1
Велике спасибі @mgri. Є дуже незначні кола, і, на жаль, не можу чітко пояснити, як вони з'являються. Більшість вершин зараз "без кола". Єдине розрізнення, яке я помітив, що такий вузол (з колом) показав менше "r-значення" у таблиці редактора вершин інструмента вузла. Це легко керується і набагато краще, ніж я передбачав. Ще раз дякую вам. Дозвольте прийняти вашу відповідь як рішення.
Казухіто

1
Дякую, @Kazuhito. Мені шкода, що не створили ідеального рішення. Однак я сподіваюся, що вам сподобається (інакше ви можете також надіслати мені зразок форм-файлу, і я спробую виправити проблему). Якщо я знайду більш ефективний спосіб, опублікую його!
mgri

1
Велике спасибі @mgri. Якщо я знайду якийсь відмінний зразок у появі кіл, я оновлю вас відтворюваним прикладом. Це дуже приємно (і його вихід прекрасний)!
Казухіто

20

Коротка відповідь: ви можете отримати його за допомогою спеціального SVG. Дивіться нижню частину цієї публікації для одного.

Довга відповідь:

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

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

Для цього слід стилізувати лінію з двома лініями Marker. Кожна лінія Маркера складається з простого маркера, півкола. Перший обертається на 180. Обидва встановлені на прозорий.

У рядку Маркер ви вказуєте змістити один з них, щоб два символи не були намальовані один перед одним, а поруч. Якщо ви використовуєте розмір інтервалу відступника = 1/2 *, вихідним сигналом буде синусоїдальна крива. Я пропоную вам пограти з розміром інтервалу, зміщенням та розмірами символів.

Основним обмеженням при такому підході є діаметр лінії півколів, який підсумовується до початкової лінії. Якщо ваш фон білий (або будь-якого простого кольору), ви можете додати 3-й простий рядок, використовуючи колір тла.

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

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

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

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

** редагувати **

Ще один варіант позбавлення від центральної лінії - створити новий символ SVG. Я модифікував напівкриву, живучи лише округлою частиною. Це працює, хоча 1/2 еліпса може бути більш привабливим. Знімок екрана робився з використанням символу розміром 10, інтервалу 4, зміщення 2.

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

збережіть код нижче у файлі half_circle_line.svg і переконайтеся, що шлях до svg встановлений у QGIS // Settings / Options / System / SVG Paths

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="11.2889mm" height="11.2889mm"
 viewBox="0 0 32 32"
 xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  version="1.2" baseProfile="tiny">
<title>Qt Svg Document</title>
<desc>Generated with Qt</desc>
<defs>
</defs>
<g fill="none" stroke="black" stroke-width="1" fill-rule="evenodd" stroke-linecap="square" stroke-linejoin="bevel" >

<g fill="#ffffff" fill-opacity="0" stroke="#000000" stroke-opacity="1" stroke-width="1" stroke-linecap="square" stroke-linejoin="bevel" transform="matrix(1,0,0,1,0,0)"
font-family="MS Shell Dlg 2" font-size="8.25" font-weight="400" font-style="normal" 
>
<path vector-effect="non-scaling-stroke" fill-rule="evenodd" d="M19.1181,16 C19.1181,16 19.1181,14.2779 17.7221,12.8819 16,12.8819 C14.2779,12.8819 12.8819,14.2779 12.8819,16"/>
</g>
</g>
</svg>

Хороша ідея. Наразі бореться зі стійкою "центральною лінією" ...: \
Kazuhito

+1 також від мене. Тим часом я намагаюся задуматися над рішенням PyQGIS. @Kazuhito, будь ласка, повідомте мені, чи цього вам буде достатньо чи якщо ви віддаєте перевагу фізичному рішенню.
mgri

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

JGH Чи потенційно у вас є ідея зняти "центральну лінію", крім маскування білою лінією (тобто ваша нижня фігура)? Схоже, це фрагментарно.
Казухіто

@Kazuhito - Ви можете змінити Pen styleна No Pen :)
Джозеф
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.