2016-06-10 22 views
6

Jak korzystać matplotlib lub pyqtgraph rysowania wykresu takiego: two dirrections widths lineJak narysować „dwa kierunki szerokości linii” w matplotlib

Linia AB to dwie wskazówki ulica, część zielona reprezentuje kierunek z punktu A do punktu B, czerwona część oznacza B do A, szerokość każdej części reprezentuje natężenie ruchu. Szerokości są mierzone w punkcie, nie zmieniają się przy różnych poziomach zoomu ani w ustawieniach dpi:.

To tylko przykład, w rzeczywistości mam hunderd z ulic. Ten rodzaj działki jest bardzo powszechny w wielu oprogramowaniach o ruchu. Próbowałem użyć patheffect matplotlib, ale wynik jest sfrustrowany:

from matplotlib import pyplot as plt 
import matplotlib.patheffects as path_effects 

x=[0,1,2,3] 
y=[1,0,0,-1] 
ab_width=20 
ba_width=30 

fig, axes= plt.subplots(1,1) 
center_line, = axes.plot(x,y,color='k',linewidth=2) 

center_line.set_path_effects(
[path_effects.SimpleLineShadow(offset=(0, -ab_width/2),shadow_color='g', alpha=1, linewidth=ab_width), 
path_effects.SimpleLineShadow(offset=(0, ba_width/2), shadow_color='r', alpha=1, linewidth=ba_width), 
path_effects.SimpleLineShadow(offset=(0, -ab_width), shadow_color='k', alpha=1, linewidth=2), 
path_effects.SimpleLineShadow(offset=(0, ba_width), shadow_color='k', alpha=1, linewidth=2), 
path_effects.Normal()]) 

axes.set_xlim(-1,4) 
axes.set_ylim(-1.5,1.5) 

enter image description here

Jeden pomysł przyszedł do mnie do podjęcia każdej części wiersza jako samodzielny linii i przeliczyć to stanowisko przy zmianie poziomu powiększenia, ale to zbyt skomplikowane i powolne.

Czy istnieje jakikolwiek łatwy sposób użycia matplotlib lub pyqtgraph? Każda sugestia zostanie doceniona!

+1

Twoje dane nie działają – Bart

+0

Przepraszam, naprawiam. @ Bart – Macer

+0

Nie, nadal nie działa. Dlaczego nie skorzystać z uploader obrazu dostarczony przez stackoverflow? – Bart

Odpowiedz

4

Jeśli możesz mieć każdą niezależną linię, można to łatwo wykonać za pomocą funkcji fill_between.

from matplotlib import pyplot as plt 
import numpy as np 

x=np.array([0,1,2,3]) 
y=np.array([1,0,0,-1]) 

y1width=-1 
y2width=3 
y1 = y + y1width 
y2 = y + y2width 

fig = plt.figure() 
ax = fig.add_subplot(111) 

plt.plot(x,y, 'k', x,y1, 'k',x,y2, 'k',linewidth=2) 
ax.fill_between(x, y1, y, color='g') 
ax.fill_between(x, y2, y, color='r') 

plt.xlim(-1,4) 
plt.ylim(-3,6) 
plt.show() 

Tutaj uważana za oś jako odniesienia (tak więc negatywny y1width), lecz może być wykonane w różny sposób. Wynik jest następnie:

<code>fill_between</code> result.

Jeśli linie są „skomplikowane”, ostatecznie przecinających się w pewnym momencie, to argument kluczowe interpolate=True muszą być użyte do wypełnienia regiony zwrotnicy prawidłowo. Innym interesującym argumentem, prawdopodobnie przydatnym w twoim przypadku użycia, jest where, aby warunkować region, na przykład where=y1 < 0. Aby uzyskać więcej informacji, zapoznaj się z documentation.

+0

Dziękuję za odpowiedź, ale możesz nie w pełni zrozumieć mojego żądania. Szerokość powinna być mierzona w punkcie i nie zmienia się przy różnych poziomach powiększenia. Jest używany w interaktywnej aplikacji, użytkownik często musi powiększać i pomniejszać i przesuwać, aby obserwować inną linię. W Twojej odpowiedzi szerokości nie są ustalane po powiększeniu. Również mała różnica w punktach końcowych. – Macer

+0

Prawda, nie dostałem części "stałej szerokości". W tym celu należy uchwycić zdarzenie zbliżenia, ustawić odpowiednią skalę cieniowania (zwiększając lub zmniejszając oryginalne szerokości) i ponownie rysować. Śledź [to sedno] (https://gist.github.com/tacaswell/3144287), aby zobaczyć coś związanego (powiązane, nie dokładnie to, czego szukasz). Jeśli chodzi o zakończenia, to takie podejście "fill_between" nie może już pomóc. – rll

3

Jednym ze sposobów rozwiązania problemu jest użycie wypełnionych wielokątów, pewnej algebry liniowej i jakiegoś rachunku różniczkowego. Główną ideą jest narysowanie wielokąta wzdłuż współrzędnych x i y oraz wzdłuż przesuniętych współrzędnych, aby zamknąć i wypełnić wielokąt.

Oto moje wyniki: Filled polygons along path

A oto kod:

from __future__ import division 
import numpy 
from matplotlib import pyplot, patches 


def road(x, y, w, scale=0.005, **kwargs): 
    # Makes sure input coordinates are arrays. 
    x, y = numpy.asarray(x, dtype=float), numpy.asarray(y, dtype=float) 
    # Calculate derivative. 
    dx = x[2:] - x[:-2] 
    dy = y[2:] - y[:-2] 
    dy_dx = numpy.concatenate([ 
     [(y[1] - y[0])/(x[1] - x[0])], 
     dy/dx, 
     [(y[-1] - y[-2])/(x[-1] - x[-2])] 
    ]) 
    # Offsets the input coordinates according to the local derivative. 
    offset = -dy_dx + 1j 
    offset = w * scale * offset/abs(offset) 
    y_offset = y + w * scale 
    # 
    AB = zip(
     numpy.concatenate([x + offset.real, x[::-1]]), 
     numpy.concatenate([y + offset.imag, y[::-1]]), 
    ) 
    p = patches.Polygon(AB, **kwargs) 

    # Returns polygon. 
    return p 


if __name__ == '__main__': 
    # Some plot initializations 
    pyplot.close('all') 
    pyplot.ion() 

    # This is the list of coordinates of each point 
    x = [0, 1, 2, 3, 4] 
    y = [1, 0, 0, -1, 0] 

    # Creates figure and axes. 
    fig, ax = pyplot.subplots(1,1) 
    ax.axis('equal') 
    center_line, = ax.plot(x, y, color='k', linewidth=2) 

    AB = road(x, y, 20, color='g') 
    BA = road(x, y, -30, color='r') 
    ax.add_patch(AB) 
    ax.add_patch(BA) 

Pierwszym etapem obliczania jak przesunięcie Każdy punkt danych jest poprzez obliczenie dyskretnego pochodny dy/dx. Lubię używać złożonej notacji do obsługi wektorów w Pythonie, tj. A = 1 - 1j. Ułatwia to życie niektórym operacjom matematycznym.

Następnym krokiem jest zapamiętanie, że pochodna podaje styczną do krzywej i z algebry liniowej, że normalna do stycznej jest n=-dy_dx + 1j, przy użyciu złożonej notacji.

Ostatnim krokiem do określenia współrzędnych przesunięcia jest zapewnienie, że wektor normalny ma rozmiar jedności n_norm = n/abs(n) i pomnożenie przez żądaną szerokość wielokąta.

Teraz, gdy mamy wszystkie współrzędne dla punktów w wielokącie, reszta jest dość prosta. Użyj patches.Polygon i dodaj je do wykresu.

Ten kod pozwala również określić, czy chcesz umieścić łatkę na górze czy pod nią. Wystarczy podać dodatnią lub ujemną wartość szerokości. Jeśli chcesz zmienić szerokość wielokąta w zależności od poziomu powiększenia i/lub rozdzielczości, dostosuj parametr scale. Daje także swobodę dodawania dodatkowych parametrów do poprawek, takich jak wzory wypełnień, przezroczystość itp.

+0

Doskonale! Ale nadal nie może rozwiązać problemu "brak zmiany po powiększeniu". Być może nie ma dyrektywy w jaki sposób użyć Matplotlib do rozwiązania problemu. Muszę złapać zdarzenie zbliżenia, ponownie obliczyć i ponownie narysować. – Macer

Powiązane problemy