Dessiner des lignes ondulées et ondulées dans QGIS?

21

Existe-t-il une fonction ou un plugin QGIS pour tracer une ligne ondulée?

J'ai utilisé Spline Tool pour dessiner manuellement quelques vagues, mais cela prend du temps. Si possible, je voudrais dessiner quelque chose comme:

entrez la description de l'image ici

Inkscape Function Plotter ( sin(x)courbe).

Kazuhito
la source
Question vraiment intéressante! Envisagez-vous un outil pour dessiner instantanément les lignes (c'est-à-dire comme lorsque vous dessinez une entité linéaire simple à l'aide de la souris), ou peut-être un moyen d'obtenir ce résultat à partir de coordonnées en entrée (éventuellement à partir de points, de lignes ou de polygones)?
mgri
1
@mgri Merci pour votre commentaire. Je m'attends à utiliser cette ligne pour marquer une frontière avec certaines incertitudes (comme un rivage fluctuant), donc l'idée principale était de convertir une polyligne (à travers des coordonnées prédéfinies) en ondulations. Mais l'idée de dessiner instantanément ce type de ligne semble également séduisante.
Kazuhito
S'il vous plaît, voyez si ma solution aide. Je ne l'ai pas testé largement, alors faites-moi savoir si quelque chose ne va pas (je ne peux pas le faire pour le moment, mais je vais probablement modifier ma réponse avec plus d'informations).
mgri

Réponses:

18

Je propose une solution utilisant PyQGIS. Cela devrait fonctionner à la fois pour les couches Linestring et MultiLineString.

Cette solution est basée sur la création d'anneaux semi-circulaires, vous devez donc définir une valeur pour le diamètre (c'est-à-dire la stepvariable dans le code ci-dessous). Le pas que vous choisissez ne sera pas le vrai pas utilisé car il est ajusté en fonction de la longueur de la ligne (mais il serait vraiment similaire à la valeur initialement définie). Vous devez faire quelques tentatives avant de trouver la meilleure valeur pour la stepvariable.

Le code nécessite également un deuxième paramètre (facultatif) (appelé crv_angle), qui permet de diminuer ou d'augmenter la courbure des anneaux (j'ai effectué quelques tests pour cela, je suggère donc de laisser 45 degrés comme angle par défaut car cela conduirait à une véritable circulaire anneaux).

Il vous suffit d'exécuter ce code à partir de la console 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)

et cela créera une nouvelle couche de mémoire de ligne avec le résultat attendu:

entrez la description de l'image ici

mgri
la source
Hou la la! Je ne peux pas m'arrêter de jouer avec ça, tellement magnifique. De plus, la sortie elle-même peut être utilisée pour d'autres opérations comme le tampon. Merci @mgri. Une dernière chose, je vois parfois de petits cercles apparaître aux sommets ou à proximité. Est-ce évitable? Je peux les supprimer en effaçant le nœud central d'un tel cercle par Node Tool (donc ce n'est pas un gros problème).
Kazuhito
1
@Kazuhito s'il vous plaît, voir mon code édité. Il semble qu'il y ait eu quelque chose de mal dans la discrétisation et j'espère qu'elle est maintenant corrigée. J'ai effectué plusieurs tests et cela semble bien fonctionner (le code est également plus lisible).
mgri
1
Merci beaucoup @mgri. Il y a des cercles très mineurs, pour lesquels je suis désolé de ne pas pouvoir expliquer clairement comment ils apparaissent. La plupart des sommets sont désormais "sans cercle". La seule distinction que j'ai remarquée était qu'un tel nœud (avec un cercle) avait montré une "valeur r" plus petite sur Vertex Editor Table of Node Tool. C'est facilement gérable et bien meilleur que ce que j'avais prévu. Merci encore. Permettez-moi d'accepter votre réponse comme solution.
Kazuhito
1
Merci, @Kazuhito. Je suis désolé de ne pas avoir créé de solution parfaite. Cependant, j'espère que vous l'apprécierez (sinon, vous pouvez également m'envoyer un exemple de fichier de formes et j'essaierai de résoudre le problème). Si je trouve un moyen plus efficace, je le posterai!
mgri
1
Merci beaucoup @mgri. Si je trouve un motif distinctif dans l'apparence des cercles, je vous mettrai à jour avec un exemple reproductible. C'est tellement agréable (et sa sortie est magnifique)!
Kazuhito
20

Réponse courte: vous pouvez l'obtenir en utilisant un SVG personnalisé. Voir le bas de ce post pour un.

Longue réponse:

Je pense qu'il vaut mieux le représenter que de modifier la géométrie de la ligne. Si vous souhaitez déplacer un bord ou effectuer d'autres actions sur la géométrie, ce serait un cauchemar à gérer si les ondulations font partie de la géométrie au lieu d'une simple représentation d'une ligne droite.

Vous pouvez jouer avec la ligne de marqueur de style. Il existe un moyen de se rapprocher facilement de ce dont vous avez besoin, et avec un peu plus d'effort, il est probablement possible de l'obtenir exactement. entrez la description de l'image ici

Pour obtenir cela, vous devez styliser la ligne avec deux lignes de marqueur. Chaque ligne de marqueur est constituée d'un marqueur simple, le demi-cercle. Le premier est tourné de 180. Les deux sont définis sur transparent.

Sur la ligne Marqueur, vous demandez à l'un d'eux d'être décalé afin que les deux symboles ne soient pas dessinés l'un en face de l'autre, mais côte à côte. Si vous utilisez la taille d'intervalle offest = 1/2 *, la sortie sera une courbe sinusoïdale. Je vous suggère de jouer avec la taille de l'intervalle, le décalage et la taille des symboles.

La principale limitation de cette approche est la ligne de diamètre des demi-cercles, qui correspond à la ligne d'origine. Si votre arrière-plan est blanc (ou n'importe quelle couleur unie), vous pouvez ajouter une troisième ligne simple en utilisant la couleur d'arrière-plan.

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

** MODIFIER **

Une autre option pour se débarrasser de la ligne médiane est de créer un nouveau symbole SVG. J'ai modifié la demi-courbe, ne vivant que la partie arrondie. Cela fonctionne, même si une 1/2 ellipse pourrait être plus attrayante. La capture d'écran a été effectuée à l'aide de la taille de symbole 10, intervalle 4, décalage 2.

entrez la description de l'image ici

enregistrez le code ci-dessous dans un fichier half_circle_line.svg et assurez-vous que le chemin vers le svg est défini dans 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
la source
Bonne idée. Actuellement aux prises avec une "ligne médiane" persistante ...: \
Kazuhito
+1 de moi aussi. En attendant, j'essaie de penser à une solution PyQGIS. @Kazuhito, faites-moi savoir si cela vous suffira ou si vous préférez une solution physique .
mgri
@mgri Avec cette réponse, j'ai maintenant une "chaîne" et non une "vague" (essayant de la modifier). J'apprécierais vraiment d'avoir une solution physique.
Kazuhito
JGH Avez-vous potentiellement une idée de supprimer la "ligne centrale", autre que de la masquer par une ligne blanche (c'est-à-dire votre chiffre du bas)? Il semble que ce soit fragmenté.
Kazuhito
@Kazuhito - Vous pouvez changer Pen stylepour No Pen :)
Joseph