2016-06-10 6 views
6

Wie verwenden matplotlib oder pyqtgraph draw Stück so zeichnen: two dirrections widths lineWie „zwei Richtungen Breiten line“ in matplotlib

Linie AB ist eine Zwei-Richtungen-Straße, stellt grüner Teil die Richtung vom Punkt A zum Punkt B, roter Teil stellt B zu A dar, Breite jedes Teils stellt das Verkehrsvolumen dar. Breiten werden in Punkt gemessen, werden nicht bei verschiedenen Zoomstufen oder dpi-Einstellungen geändert.

Dies ist nur ein Beispiel, in der Tat habe ich Hunderte von Straßen. Diese Art von Handlung ist in vielen Verkehrssoftware sehr häufig. Ich habe versucht, matplotlib der patheffect zu verwenden, aber Ergebnis ist frustriert:

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

Eine Idee kam mir ist jeder Teil der Leitung als eigenständige Linie zu nehmen und neu zu berechnen es Position ist, wenn Zoomstufe zu ändern, aber es ist zu kompliziert und langsam.

Wenn es eine einfache Möglichkeit gibt, matplotlib oder pyqtgraph zu verwenden, zeichnen, was ich will? Jeder Vorschlag wird geschätzt!

+1

Ihre Zahlen arbeiten nicht – Bart

+0

Tut mir leid, ich repariere es. @ Bart – Macer

+0

Nein, funktioniert immer noch nicht .. Warum nicht die Bild-Uploader von stackoverflow zur Verfügung gestellt? – Bart

Antwort

4

Wenn Sie jede unabhängige Linie haben können, kann dies leicht mit der fill_between Funktion getan werden.

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() 

Hier hielt ich die Mittellinie als Referenz (also die negativen y1width), aber anders gemacht werden könnte. Das Ergebnis ist dann:

<code>fill_between</code> result.

Wenn die Linien ‚kompliziert‘ sind, was schließlich zu einem bestimmten Punkt schneiden, dann das Schlüsselwort Argument interpolate=True verwendet werden müssen, um die Kreuzungsbereiche richtig zu füllen. Ein weiteres interessantes Argument, das wahrscheinlich für Ihren Anwendungsfall nützlich ist, ist where, um die Region zu konditionieren, zum Beispiel where=y1 < 0. Für weitere Informationen können Sie die documentation überprüfen.

+0

Vielen Dank für Ihre Antwort, aber Sie können meine Nachfrage nicht vollständig verstehen. Die Breite sollte in Punkt gemessen werden und wird nicht bei verschiedenen Zoomstufen geändert. Es wird in einer interaktiven Anwendung verwendet, Benutzer müssen oft ein- und auszoomen und schwenken, um verschiedene Linien zu beobachten. In Ihrer Antwort sind die Breiten nach dem Zoom nicht festgelegt. Auch ein kleiner Unterschied in den Endpunkten. – Macer

+0

Stimmt, ich habe den Teil der "konstanten Breite" nicht bekommen. Zu diesem Zweck müssen Sie das Zoom-Ereignis abfangen, eine geeignete Skalierung für die Schattierung (durch Vergrößern oder Verkleinern der ursprünglichen Breite) festlegen und erneut zeichnen. Folgen Sie [dieser Text] (https://gist.github.com/tacaswell/3144287), um etwas zu sehen, das verwandt ist (nicht genau das, wonach Sie suchen). Was die Endungen betrifft, kann dieser 'fill_between'-Ansatz nicht mehr helfen. – rll

3

Eine Möglichkeit, Ihr Problem zu lösen, sind gefüllte Polygone, einige lineare Algebra und etwas Kalkül. Der Grundgedanke ist, ein Polygon entlang Ihrer x und y Koordinaten und verschobenen Koordinaten zu zeichnen, um das Polygon zu schließen und zu füllen.

Das sind meine Ergebnisse: Filled polygons along path

Und hier ist der Code:

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) 

Der erste Schritt bei der Berechnung, wie die einzelnen Datenpunkt zu kompensieren ist durch die diskrete Ableitung dy/dx berechnen. Ich verwende gerne komplexe Schreibweisen, um Vektoren in Python zu handhaben, d. H. A = 1 - 1j. Dies erleichtert einigen mathematischen Operationen das Leben. Der nächste Schritt besteht darin, sich daran zu erinnern, dass die Ableitung die Tangente an die Kurve gibt, und von der linearen Algebra, dass die Normale zur Tangente n=-dy_dx + 1j ist, wobei die komplexe Notation verwendet wird.

Der letzte Schritt bei der Bestimmung der Versatzkoordinaten besteht darin, sicherzustellen, dass der Normalenvektor die Einheitsgröße n_norm = n/abs(n) hat und mit der gewünschten Breite des Polygons multipliziert.

Nun, da wir alle Koordinaten für die Punkte im Polygon haben, ist der Rest ziemlich einfach. Verwenden Sie patches.Polygon und fügen Sie sie zum Plot hinzu.

Mit diesem Code können Sie auch festlegen, ob der Patch über Ihrer Route oder darunter liegen soll. Gib einfach einen positiven oder negativen Wert für die Breite. Wenn Sie die Breite des Polygons abhängig von Ihrer Zoomstufe und/oder Auflösung ändern möchten, passen Sie den Parameter scale an. Es gibt Ihnen auch Freiheit, zusätzliche Parameter zu den Flecken wie Füllmuster, Transparenz usw. hinzuzufügen.

+0

Ausgezeichnet! Aber immer noch kann das "Gewicht keine Änderung nach dem Zoomen" Problem lösen. Vielleicht gibt es keine Richtlinie, um matplotlib zur Lösung des Problems zu verwenden. Ich muss das Zoom-Ereignis abfangen, neu berechnen und neu zeichnen. – Macer

Verwandte Themen