Rysowanie falistych, kręconych linii w QGIS?

21

Czy istnieje funkcja QGIS lub wtyczka do rysowania krętej linii?

Użyłem narzędzia Spline do ręcznego rysowania niektórych fal, ale jest to czasochłonne. Jeśli to możliwe, chciałbym narysować coś takiego:

wprowadź opis zdjęcia tutaj

Inkscape Function Plotter ( sin(x)krzywa).

Kazuhito
źródło
Naprawdę ciekawe pytanie! Czy myślisz o narzędziu do natychmiastowego rysowania linii (np. Kiedy rysujesz prosty element linii za pomocą myszy), czy może sposób, aby uzyskać ten wynik, zaczynając od współrzędnych jako danych wejściowych (ewentualnie od punktów, linii lub wielokątów)?
mgri
1
@mgri Dziękujemy za komentarz. Spodziewam się, że użyję tej linii do oznaczenia granicy z pewnymi niepewnościami (np. Zmienna linia brzegowa), więc głównym pomysłem było przekształcenie polilinii (poprzez wstępnie zdefiniowane współrzędne) w wiggles. Ale pomysł natychmiastowego narysowania tego rodzaju linii również brzmi atrakcyjnie.
Kazuhito
Sprawdź, czy moje rozwiązanie pomaga. Nie testowałem tego szeroko, więc proszę dać mi znać, jeśli coś pójdzie nie tak (nie mogę teraz tego zrobić, ale prawdopodobnie zmienię odpowiedź, podając więcej informacji).
mgri

Odpowiedzi:

18

Proponuję rozwiązanie za pomocą PyQGIS. Powinien działać zarówno dla warstw Linestring, jak i MultiLineString.

To rozwiązanie opiera się na tworzeniu półokrągłych pierścieni, dlatego należy ustawić wartość średnicy (tj. stepZmienną w poniższym kodzie). Wybrany krok nie będzie rzeczywistym krokiem, ponieważ jest dostosowywany na podstawie długości linii (ale byłby naprawdę podobny do początkowo ustawionej wartości). Musisz wykonać kilka prób, zanim znajdziesz najlepszą wartość dla stepzmiennej.

Kod wymaga również drugiego (opcjonalnego) parametru (tzw. crv_angle), Który pomaga zmniejszyć lub zwiększyć krzywiznę pierścieni (przeprowadziłem dla niego kilka testów, więc sugeruję pozostawienie 45 stopni jako domyślnego kąta, ponieważ prowadziłoby to do rzeczywistego kołowego pierścienie).

Musisz tylko uruchomić ten kod z konsoli 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)

i utworzy nową warstwę pamięci linii z oczekiwanym rezultatem:

wprowadź opis zdjęcia tutaj

mgri
źródło
Łał! Nie mogę przestać się tym bawić, tak cudownie. Co więcej, samo wyjście można wykorzystać do dalszych operacji, takich jak bufor. Dziękuję @ mgri. I ostatnia rzecz: czasami widzę małe kółka na wierzchołkach lub w ich pobliżu. Czy można tego uniknąć? Mogę je usunąć, usuwając środkowy węzeł takiego koła za pomocą Node Tool (więc nie jest to wielka sprawa).
Kazuhito
1
@Kazuhito, proszę zobaczyć mój edytowany kod. Wydaje się, że było coś nie tak z dyskretyzacją i mam nadzieję, że już to zostało naprawione. Przeprowadziłem kilka testów i wydaje się, że działa dobrze (kod jest również bardziej czytelny).
mgri
1
Dziękuję bardzo @mgri. Istnieją bardzo małe kręgi, za co przepraszam, nie potrafię jasno wyjaśnić, jak się one pojawiają. Większość wierzchołków jest teraz „bez kół”. Zauważyłem tylko rozróżnienie, że taki węzeł (z okręgiem) wykazał mniejszą „wartość r” w Tabeli edytorów wierzchołków w narzędziu węzłów. Jest to łatwe do opanowania i znacznie lepsze niż się spodziewałem. Jeszcze raz dziękuję. Pozwól mi przyjąć twoją odpowiedź jako rozwiązanie.
Kazuhito
1
Dzięki, @Kazuhito. Przepraszam, że nie stworzyłem idealnego rozwiązania. Mam jednak nadzieję, że ci się spodoba (w przeciwnym razie możesz również wysłać mi przykładowy plik kształtu i postaram się rozwiązać problem). Jeśli znajdę bardziej wydajny sposób, opublikuję go!
mgri
1
Wielkie dzięki @mgri. Jeśli znajdę jakiś charakterystyczny wzór w wyglądzie kręgów, zaktualizuję cię o powtarzalny przykład. Jest to bardzo przyjemne (a jego wynik jest piękny)!
Kazuhito
20

Krótka odpowiedź: możesz ją uzyskać za pomocą niestandardowego pliku SVG. Zobacz na dole tego postu dla jednego.

Długa odpowiedź:

Uważam, że lepiej go przedstawić, niż zmodyfikować geometrię linii. Jeśli chcesz przesunąć krawędź lub wykonać inne działania na geometrii, koszmarem byłoby poradzić sobie, jeśli wiggles są częścią geometrii, a nie tylko reprezentacją linii prostej.

Możesz grać za pomocą linii znaczników stylu. Istnieje sposób, aby łatwo zbliżyć się do tego, czego potrzebujesz, a przy odrobinie wysiłku możliwe jest uzyskanie tego dokładnie. wprowadź opis zdjęcia tutaj

Aby to uzyskać, stylizujesz linię za pomocą dwóch linii znacznika. Każda linia znacznika składa się z prostego znacznika, półkola. Pierwszy jest obrócony o 180. Oba są ustawione na przezroczyste.

Na linii znacznika nakazujesz przesunięcie jednego z nich, aby dwa symbole nie były rysowane przed sobą, lecz obok siebie. Jeśli użyjesz odstępu offest = 1/2 *, wynikiem będzie krzywa sinusoidalna. Proponuję grać z rozmiarem interwału, przesunięciem i rozmiarami symboli.

Głównym ograniczeniem tego podejścia jest linia średnicy półokręgów, która sumuje się z linią pierwotną. Jeśli twoje tło jest białe (lub ma zwykły kolor), możesz dodać trzecią prostą linię, używając koloru tła.

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

** EDYCJA **

Inną opcją pozbycia się linii środkowej jest utworzenie nowego symbolu SVG. Zmodyfikowałem półkrzywę, żyjąc tylko zaokrągloną częścią. Działa, choć elipsa 1/2 może być bardziej atrakcyjna. Zrzut ekranu został wykonany przy użyciu symbolu o rozmiarze 10, interwał 4, przesunięcie 2.

wprowadź opis zdjęcia tutaj

zapisz poniższy kod w pliku half_circle_line.svg i upewnij się, że ścieżka do svg jest ustawiona w 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
źródło
Dobry pomysł. Obecnie zmaga się z uporczywą „linią środkową” ...: \
Kazuhito
+1 ode mnie też. W międzyczasie staram się znaleźć rozwiązanie PyQGIS. @Kazuhito, daj mi znać, czy to by ci wystarczyło, czy wolisz rozwiązanie fizyczne .
mgri
@mgri Dzięki tej odpowiedzi mam teraz „łańcuch”, a nie „falę” (próbuję ją zmodyfikować). Naprawdę doceniłbym fizyczne rozwiązanie.
Kazuhito
JGH Czy masz pomysł, aby usunąć „linię środkową”, inną niż zamaskowanie jej białą linią (tj. Twoją dolną liczbą)? Wygląda na rozdrobniony.
Kazuhito
@Kazuhito - można zmienić Pen stylena nr Pen :)
Józefa