Zeichnen von welligen, wackeligen Linien in QGIS?

21

Gibt es eine QGIS-Funktion oder ein Plugin zum Zeichnen einer wackeligen Linie?

Ich habe das Spline-Tool zum manuellen Zeichnen einiger Wellen verwendet, aber es ist zeitaufwändig. Wenn möglich, möchte ich etwas zeichnen wie:

Bildbeschreibung hier eingeben

Inkscape Function Plotter ( sin(x)Kurve).

Kazuhito
quelle
Wirklich interessante Frage! Denken Sie über ein Werkzeug zum sofortigen Zeichnen der Linien nach (z. B. wenn Sie ein einfaches Linien-Feature mit der Maus zeichnen), oder über eine Möglichkeit, dieses Ergebnis ausgehend von Koordinaten als Eingabe zu erhalten (eventuell aus Punkten, Linien oder Polygonen)?
26.
1
@mgri Danke für deinen Kommentar. Ich erwarte, diese Linie zu verwenden, um eine Grenze mit einigen Unsicherheiten zu markieren (wie eine schwankende Küstenlinie). Daher bestand die Hauptidee darin, eine Polylinie (durch vordefinierte Koordinaten) in Wackelbewegungen umzuwandeln. Aber die Idee, diese Art von Linie sofort zu zeichnen, klingt auch attraktiv.
Kazuhito
Bitte sehen Sie, ob meine Lösung hilft. Ich habe es nicht ausführlich getestet. Bitte lassen Sie mich wissen, wenn etwas schief geht (ich kann es derzeit nicht tun, aber ich werde meine Antwort wahrscheinlich mit weiteren Informationen bearbeiten).
Mi

Antworten:

18

Ich schlage eine Lösung mit PyQGIS vor. Es sollte sowohl für Linestring- als auch für MultiLineString-Layer funktionieren.

Diese Lösung basiert auf der Erstellung von Halbkreisringen. Sie müssen daher einen Wert für den Durchmesser festlegen (dh die stepVariable im folgenden Code). Der Schritt, den Sie auswählen, ist nicht der tatsächliche Schritt, der verwendet wird, da er auf der Grundlage der Zeilenlänge angepasst wird (er wäre jedoch dem ursprünglich festgelegten Wert sehr ähnlich). Sie müssen einige Versuche durchführen, bevor Sie den besten Wert für die stepVariable finden.

Der Code erfordert auch einen zweiten (optionalen) Parameter (genannt crv_angle), der dazu beiträgt, die Krümmung der Ringe zu verringern oder zu erhöhen (ich habe einige Tests durchgeführt, daher schlage ich vor, 45 Grad als Standardwinkel zu belassen, da dies zu einer echten Kreisform führen würde Ringe).

Sie müssen diesen Code nur in der Python-Konsole ausführen:

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)

und es wird eine neue Zeilenspeicherebene mit dem erwarteten Ergebnis erstellt:

Bildbeschreibung hier eingeben

mgri
quelle
Wow! Ich kann nicht aufhören damit zu spielen, so wunderschön. Darüber hinaus kann der Ausgang selbst für weitere Operationen wie Puffer verwendet werden. Vielen Dank @mgri. Als letztes sehe ich manchmal kleine Kreise an oder in der Nähe von Eckpunkten. Ist das vermeidbar? Ich kann sie entfernen, indem ich den Mittelknoten eines solchen Kreises mit dem Node Tool lösche (es ist also keine große Sache).
Kazuhito
1
@ Kazuhito bitte, siehe meinen bearbeiteten Code. Anscheinend stimmte etwas mit der Diskretisierung nicht, und ich hoffe, dass sie inzwischen behoben ist. Ich habe mehrere Tests durchgeführt und es scheint gut zu funktionieren (der Code ist auch besser lesbar).
27.
1
Vielen Dank @mgri. Es gibt sehr kleine Kreise, für die es mir leid tut, dass ich nicht klar erklären kann, wie diese aussehen. Die meisten Scheitelpunkte sind jetzt "kreisfrei". Der einzige Unterschied, den ich bemerkte, war, dass ein solcher Knoten (mit Kreis) einen kleineren "r-Wert" in der Vertex-Editor-Tabelle des Node-Tools angezeigt hatte. Dies ist leicht zu handhaben und weitaus besser als ich erwartet hatte. Danke nochmal. Lassen Sie mich Ihre Antwort als Lösung akzeptieren.
Kazuhito
1
Danke, @Kazuhito. Es tut mir leid, dass ich keine perfekte Lösung gefunden habe. Ich hoffe jedoch, dass es Ihnen gefallen wird (ansonsten können Sie mir auch ein Beispiel-Shapefile senden und ich werde versuchen, das Problem zu beheben). Wenn ich einen effizienteren Weg finde, werde ich ihn posten!
27.
1
Vielen Dank @mgri. Wenn ich ein bestimmtes Muster im Erscheinungsbild von Kreisen finde, werde ich Sie mit einem reproduzierbaren Beispiel aktualisieren. Das macht so viel Spaß (und die Ausgabe ist wunderschön)!
Kazuhito
20

Kurze Antwort: Sie können es mit einem benutzerdefinierten SVG erhalten. Siehe unten in diesem Beitrag.

Lange Antwort:

Ich glaube, es ist besser, es darzustellen, als die Liniengeometrie zu modifizieren. Wenn Sie eine Kante verschieben oder andere Aktionen an der Geometrie ausführen möchten, ist es ein Albtraum, wenn die Wackelbewegungen Teil der Geometrie sind, anstatt nur eine gerade Linie darzustellen.

Sie können mit der Stilmarkierungslinie spielen. Es gibt eine Möglichkeit, schnell an das heranzukommen, was Sie benötigen, und mit etwas mehr Aufwand ist es wahrscheinlich möglich, genau das zu erreichen. Bildbeschreibung hier eingeben

Um dies zu erreichen, würden Sie die Linie mit zwei Markierungslinien stylen. Jede Markierungslinie besteht aus einer einfachen Markierung, dem Halbkreis. Die erste wird um 180 gedreht. Beide sind auf transparent eingestellt.

In der Markierungslinie weisen Sie einen von ihnen an, versetzt zu sein, damit die beiden Symbole nicht voreinander, sondern nebeneinander gezeichnet werden. Wenn Sie offest = 1/2 * Intervallgröße verwenden, ist die Ausgabe eine Sinuskurve. Ich schlage vor, Sie spielen mit der Intervallgröße, dem Versatz und den Symbolgrößen.

Die Hauptbeschränkung bei diesem Ansatz ist die Durchmesserlinie der Halbkreise, die sich zur ursprünglichen Linie summiert. Wenn Ihr Hintergrund weiß ist (oder eine normale Farbe), können Sie eine dritte einfache Linie mit der Hintergrundfarbe hinzufügen.

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

** EDIT **

Eine weitere Möglichkeit, die Mittellinie zu entfernen, besteht darin, ein neues SVG-Symbol zu erstellen. Ich habe die Halbkurve modifiziert und nur den abgerundeten Teil gelebt. Es funktioniert, obwohl eine 1/2-Ellipse ansprechender sein könnte. Der Screenshot wurde mit Symbolgröße 10, Intervall 4, Offset 2 erstellt.

Bildbeschreibung hier eingeben

Speichern Sie den folgenden Code in einer Datei half_circle_line.svg und vergewissern Sie sich, dass der Pfad zur svg angegeben ist 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>
JGH
quelle
Gute Idee. Derzeit kämpfen mit anhaltenden "Mittellinie" ...:
Kazuhito
+1 von mir auch. In der Zwischenzeit versuche ich, an eine PyQGIS-Lösung zu denken. @Kazuhito, lass mich bitte wissen, ob dies für dich ausreichen würde oder ob du eine physikalische Lösung bevorzugst .
Mi
@mgri Mit dieser Antwort habe ich jetzt eine "Kette", nicht "Welle" (versucht, sie zu ändern). Würde mich sehr über eine physikalische Lösung freuen.
Kazuhito
JGH Haben Sie möglicherweise eine Idee, die "Mittellinie" zu entfernen, anstatt sie durch eine weiße Linie zu maskieren (dh Ihre untere Figur)? Es sieht aus, als ob es fragmentiert ist.
Kazuhito
@ Kazuhito - Sie können die Pen stylezu No Pen ändern :)
Joseph