Notiz
Klicken Sie hier , um den vollständigen Beispielcode herunterzuladen
Transformations-Tutorial #
Wie alle Grafikpakete baut Matplotlib auf einem Transformationsframework auf, um einfach zwischen Koordinatensystemen, dem Userland
- Datenkoordinatensystem , dem Achsenkoordinatensystem , dem Figurenkoordinatensystem und dem Anzeigekoordinatensystem zu wechseln . In 95 % Ihrer Plots müssen Sie sich darüber keine Gedanken machen, da es unter der Haube passiert, aber wenn Sie die Grenzen der benutzerdefinierten Figurengenerierung erweitern, ist es hilfreich, diese Objekte zu verstehen, damit Sie die vorhandenen wiederverwenden können Transformationen, die Ihnen Matplotlib zur Verfügung stellt, oder erstellen Sie Ihre eigenen (siehe matplotlib.transforms
). Die folgende Tabelle fasst einige nützliche Koordinatensysteme, eine Beschreibung jedes Systems und das Transformationsobjekt für den Wechsel von jedem Koordinatensystem zu dem zusammenKoordinaten anzeigen . In der Spalte „Transformationsobjekt“ ax
ist eine
Axes
Instanz, fig
ist eine
Figure
Instanz und subfigure
ist eine
SubFigure
Instanz.
Koordinatensystem |
Beschreibung |
Transformationsobjekt vom System zum Display |
---|---|---|
"Daten" |
Das Koordinatensystem der Daten in den Achsen. |
|
"Äxte" |
Das Koordinatensystem des
|
|
"subfigurieren" |
Das Koordinatensystem des
|
|
"Zahl" |
Das Koordinatensystem des
|
|
"Zahl-Zoll" |
Das Koordinatensystem
|
|
"xaxis", "yaxis" |
Kombinierte Koordinatensysteme, die Datenkoordinaten in einer Richtung und Achsenkoordinaten in der anderen verwenden. |
|
"Anzeige" |
Das native Koordinatensystem der Ausgabe ; (0, 0) ist unten links im Fenster und (Breite, Höhe) ist oben rechts in der Ausgabe in "Anzeigeeinheiten". Die genaue Interpretation der Einheiten hängt vom Backend ab. Zum Beispiel sind es Pixel für Agg und Punkte für svg/pdf. |
|
Die Transform
Objekte sind für die Quell- und Zielkoordinatensysteme naiv, jedoch sind die Objekte, auf die in der obigen Tabelle verwiesen wird, so konstruiert, dass sie Eingaben in ihr Koordinatensystem aufnehmen und die Eingabe in das Anzeigekoordinatensystem umwandeln . Aus diesem Grund
hat das AnzeigekoordinatensystemNone
für die Spalte „Transformationsobjekt“ – es ist bereits in Anzeigekoordinaten enthalten . Die Benennungs- und Zielkonventionen sind eine Hilfe, um den Überblick über die verfügbaren "Standard"-Koordinatensysteme und Transformationen zu behalten.
Die Transformationen wissen auch, wie sie sich selbst invertieren (über
Transform.inverted
), um eine Transformation vom Ausgabekoordinatensystem zurück zum Eingabekoordinatensystem zu erzeugen. Konvertiert beispielsweise ax.transData
Werte in Datenkoordinaten in Anzeigekoordinaten und
ax.transData.inversed()
ist ein Wert, der matplotlib.transforms.Transform
von Anzeigekoordinaten in Datenkoordinaten übergeht. Dies ist besonders nützlich, wenn Sie Ereignisse von der Benutzeroberfläche verarbeiten, die normalerweise im Anzeigebereich auftreten, und Sie wissen möchten, wo der Mausklick oder Tastendruck in Ihrem Datenkoordinatensystem aufgetreten ist .
Beachten Sie, dass die Angabe der Position von Künstlern in Anzeigekoordinaten ihre relative Position ändern kann, wenn sich die dpi
oder Größe der Figur ändert. Dies kann beim Drucken oder Ändern der Bildschirmauflösung zu Verwirrung führen, da sich Position und Größe des Objekts ändern können. Daher ist es üblich, dass Künstler, die in einer Axt oder Figur platziert sind, ihre Transformation auf etwas
anderes als IdentityTransform()
; Die Standardeinstellung, wenn ein Künstler zu einer Achse hinzugefügt wird, add_artist
ist, dass die Transformation
so ist, dass Sie in Datenkoordinatenax.transData
arbeiten und denken können und Matplotlib sich um die anzuzeigende Transformation kümmern kann .
Datenkoordinaten #
Beginnen wir mit der am häufigsten verwendeten Koordinate, dem Datenkoordinatensystem . Immer wenn Sie Daten zu den Achsen hinzufügen, aktualisiert Matplotlib die Datengrenzen, die am häufigsten mit den Methoden set_xlim()
und
aktualisiert set_ylim()
werden. In der Abbildung unten erstrecken sich die Datengrenzen beispielsweise von 0 bis 10 auf der x-Achse und von -1 bis 1 auf der y-Achse.
Sie können die ax.transData
Instanz verwenden, um Ihre
Daten in Ihr Anzeigekoordinatensystem umzuwandeln , entweder einen einzelnen Punkt oder eine Folge von Punkten, wie unten gezeigt:
In [14]: type(ax.transData)
Out[14]: <class 'matplotlib.transforms.CompositeGenericTransform'>
In [15]: ax.transData.transform((5, 0))
Out[15]: array([ 335.175, 247. ])
In [16]: ax.transData.transform([(5, 0), (1, 2)])
Out[16]:
array([[ 335.175, 247. ],
[ 132.435, 642.2 ]])
Sie können die inverted()
Methode verwenden, um eine Transformation zu erstellen, die Sie von der Anzeige zu den Datenkoordinaten führt
:
In [41]: inv = ax.transData.inverted()
In [42]: type(inv)
Out[42]: <class 'matplotlib.transforms.CompositeGenericTransform'>
In [43]: inv.transform((335.175, 247.))
Out[43]: array([ 5., 0.])
Wenn Sie dieses Tutorial mitschreiben, können die genauen Werte der Anzeigekoordinaten abweichen, wenn Sie eine andere Fenstergröße oder dpi-Einstellung haben. Ebenso sind in der Abbildung unten die mit der Anzeige gekennzeichneten Punkte wahrscheinlich nicht die gleichen wie in der Ipython-Sitzung, da die Standardwerte für die Größe der Dokumentationsfiguren unterschiedlich sind.
x = np.arange(0, 10, 0.005)
y = np.exp(-x/2.) * np.sin(2*np.pi*x)
fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xlim(0, 10)
ax.set_ylim(-1, 1)
xdata, ydata = 5, 0
# This computing the transform now, if anything
# (figure size, dpi, axes placement, data limits, scales..)
# changes re-calling transform will get a different value.
xdisplay, ydisplay = ax.transData.transform((xdata, ydata))
bbox = dict(boxstyle="round", fc="0.8")
arrowprops = dict(
arrowstyle="->",
connectionstyle="angle,angleA=0,angleB=90,rad=10")
offset = 72
ax.annotate('data = (%.1f, %.1f)' % (xdata, ydata),
(xdata, ydata), xytext=(-2*offset, offset), textcoords='offset points',
bbox=bbox, arrowprops=arrowprops)
disp = ax.annotate('display = (%.1f, %.1f)' % (xdisplay, ydisplay),
(xdisplay, ydisplay), xytext=(0.5*offset, -offset),
xycoords='figure pixels',
textcoords='offset points',
bbox=bbox, arrowprops=arrowprops)
plt.show()
Warnung
Wenn Sie den Quellcode im obigen Beispiel in einem GUI-Backend ausführen, stellen Sie möglicherweise auch fest, dass die beiden Pfeile für die Daten- und Anzeigeanmerkungen
nicht genau auf denselben Punkt zeigen. Dies liegt daran, dass der Anzeigepunkt berechnet wurde, bevor die Figur angezeigt wurde, und das GUI-Backend die Figur bei der Erstellung möglicherweise leicht in der Größe ändert. Der Effekt ist ausgeprägter, wenn Sie die Größe der Figur selbst ändern. Dies ist ein guter Grund, warum Sie selten im Anzeigebereich arbeiten möchten
, aber Sie können sich mit dem verbinden, 'on_draw'
Event
um Figurkoordinaten beim Zeichnen von Figuren zu aktualisieren
; siehe Ereignisbehandlung und -auswahl .
Wenn Sie die x- oder y-Grenzen Ihrer Achsen ändern, werden die Datengrenzen aktualisiert, sodass die Transformation einen neuen Anzeigepunkt ergibt. Beachten Sie, dass, wenn wir nur ylim ändern, nur die y-Anzeigekoordinate geändert wird, und wenn wir auch xlim ändern, werden beide geändert. Mehr dazu später, wenn wir über die sprechen
Bbox
.
In [54]: ax.transData.transform((5, 0))
Out[54]: array([ 335.175, 247. ])
In [55]: ax.set_ylim(-1, 2)
Out[55]: (-1, 2)
In [56]: ax.transData.transform((5, 0))
Out[56]: array([ 335.175 , 181.13333333])
In [57]: ax.set_xlim(10, 20)
Out[57]: (10, 20)
In [58]: ax.transData.transform((5, 0))
Out[58]: array([-171.675 , 181.13333333])
Achsenkoordinaten #
Nach dem Datenkoordinatensystem sind Achsen wahrscheinlich das zweitnützlichste Koordinatensystem. Hier ist der Punkt (0, 0) unten links auf Ihren Achsen oder Unterdiagrammen, (0,5, 0,5) ist die Mitte und (1,0, 1,0) ist oben rechts. Sie können sich auch auf Punkte außerhalb des Bereichs beziehen, also ist (-0,1, 1,1) links und über Ihren Achsen. Dieses Koordinatensystem ist äußerst nützlich, wenn Sie Text in Ihren Achsen platzieren, da Sie häufig eine Textblase an einer festen Position haben möchten, z. B. oben links im Achsenbereich, und diese Position beim Schwenken oder Zoomen fixiert bleiben soll. Hier ist ein einfaches Beispiel, das vier Panels erstellt und sie mit „A“, „B“, „C“, „D“ beschriftet, wie Sie es oft in Zeitschriften sehen.
fig = plt.figure()
for i, label in enumerate(('A', 'B', 'C', 'D')):
ax = fig.add_subplot(2, 2, i+1)
ax.text(0.05, 0.95, label, transform=ax.transAxes,
fontsize=16, fontweight='bold', va='top')
plt.show()
Sie können auch Linien oder Flecken im Achsenkoordinatensystem erstellen, aber dies ist meiner Erfahrung nach weniger nützlich als die Verwendung ax.transAxes
zum Platzieren von Text. Nichtsdestotrotz ist hier ein dummes Beispiel, das einige zufällige Punkte im Datenraum zeichnet und eine halbtransparente
Circle
Mitte der Achsen mit einem Radius von einem Viertel der Achsen überlagert - wenn Ihre Achsen das Seitenverhältnis nicht beibehalten (siehe set_aspect()
) , sieht dies wie eine Ellipse aus. Verwenden Sie das Pan/Zoom-Werkzeug, um sich zu bewegen, oder ändern Sie die Daten xlim und ylim manuell, und Sie werden sehen, wie sich die Daten bewegen, aber der Kreis bleibt fest, da er nicht in Datenkoordinaten liegt und immer
in der Mitte der Achsen bleibt .
fig, ax = plt.subplots()
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y, 'go', alpha=0.2) # plot some data in data coordinates
circ = mpatches.Circle((0.5, 0.5), 0.25, transform=ax.transAxes,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
Blended Transformationen #
Das Zeichnen in gemischten Koordinatenräumen, die Achsen mit Datenkoordinaten mischen
, ist äußerst nützlich, um beispielsweise eine horizontale Spanne zu erstellen, die einen Teil der y-Daten hervorhebt, sich jedoch über die x-Achse erstreckt, unabhängig von Datengrenzen, Schwenk- oder Zoomstufe usw Tatsächlich sind diese gemischten Linien und Spannen so nützlich, dass wir Funktionen eingebaut haben, um sie einfach zu zeichnen (siehe
axhline()
,
axvline()
,
axhspan()
,
axvspan()
), aber für didaktische Zwecke werden wir die horizontale Spanne hier mit einer gemischten Transformation implementieren. Dieser Trick funktioniert nur bei trennbaren Transformationen, wie Sie sie in normalen kartesischen Koordinatensystemen sehen, nicht jedoch bei untrennbaren Transformationen wie dem
PolarTransform
.
import matplotlib.transforms as transforms
fig, ax = plt.subplots()
x = np.random.randn(1000)
ax.hist(x, 30)
ax.set_title(r'$\sigma=1 \/ \dots \/ \sigma=2$', fontsize=16)
# the x coords of this transformation are data, and the y coord are axes
trans = transforms.blended_transform_factory(
ax.transData, ax.transAxes)
# highlight the 1..2 stddev region with a span.
# We want x to be in data coordinates and y to span from 0..1 in axes coords.
rect = mpatches.Rectangle((1, 0), width=1, height=1, transform=trans,
color='yellow', alpha=0.5)
ax.add_patch(rect)
plt.show()
Notiz
Die gemischten Transformationen, bei denen x in Datenkoordinaten und y in Achsenkoordinaten steht, sind so nützlich, dass wir Hilfsmethoden
haben, um die Versionen zurückzugeben, die Matplotlib intern zum Zeichnen von Ticks, Ticklabels usw. verwendet. Die Methoden sind matplotlib.axes.Axes.get_xaxis_transform()
und
matplotlib.axes.Axes.get_yaxis_transform()
. Im obigen Beispiel kann der Aufruf
blended_transform_factory()
also ersetzt werden durch get_xaxis_transform
:
trans = ax.get_xaxis_transform()
Plotten in physikalischen Koordinaten #
Manchmal möchten wir, dass ein Objekt auf dem Grundstück eine bestimmte physische Größe hat. Hier zeichnen wir den gleichen Kreis wie oben, aber in physikalischen Koordinaten. Wenn Sie dies interaktiv tun, können Sie sehen, dass das Ändern der Größe der Figur den Abstand des Kreises von der unteren linken Ecke nicht ändert, seine Größe nicht ändert und der Kreis ein Kreis bleibt, unabhängig vom Seitenverhältnis der Achsen.
fig, ax = plt.subplots(figsize=(5, 4))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
Wenn wir die Figurengröße ändern, ändert der Kreis seine absolute Position nicht und wird abgeschnitten.
fig, ax = plt.subplots(figsize=(7, 2))
x, y = 10*np.random.rand(2, 1000)
ax.plot(x, y*10., 'go', alpha=0.2) # plot some data in data coordinates
# add a circle in fixed-coordinates
circ = mpatches.Circle((2.5, 2), 1.0, transform=fig.dpi_scale_trans,
facecolor='blue', alpha=0.75)
ax.add_patch(circ)
plt.show()
Eine andere Verwendung besteht darin, einen Patch mit einer festgelegten physischen Dimension um einen Datenpunkt auf den Achsen zu platzieren. Hier addieren wir zwei Transformationen zusammen. Der erste legt die Skalierung fest, wie groß die Ellipse sein soll, und der zweite legt ihre Position fest. Die Ellipse wird dann am Ursprung platziert, und dann verwenden wir die Hilfstransformation ScaledTranslation
, um sie an die richtige Stelle im ax.transData
Koordinatensystem zu verschieben. Dieser Helfer wird instanziiert mit:
trans = ScaledTranslation(xt, yt, scale_trans)
wobei xt und yt die Translationsoffsets sind und scale_trans eine Transformation ist, die xt und yt zur Transformationszeit skaliert , bevor die Offsets angewendet werden.
Beachten Sie die Verwendung des Plus-Operators bei den Transformationen unten. Dieser Code sagt: Wenden Sie zuerst die Skalierungstransformation fig.dpi_scale_trans
an, um die Ellipse auf die richtige Größe zu bringen, aber immer noch bei (0, 0) zentriert, und übersetzen Sie dann die Daten in xdata[0]
und ydata[0]
in den Datenraum.
Im interaktiven Einsatz bleibt die Ellipse auch dann gleich groß, wenn die Achsengrenzen per Zoom verändert werden.
fig, ax = plt.subplots()
xdata, ydata = (0.2, 0.7), (0.5, 0.5)
ax.plot(xdata, ydata, "o")
ax.set_xlim((0, 1))
trans = (fig.dpi_scale_trans +
transforms.ScaledTranslation(xdata[0], ydata[0], ax.transData))
# plot an ellipse around the point that is 150 x 130 points in diameter...
circle = mpatches.Ellipse((0, 0), 150/72, 130/72, angle=40,
fill=None, transform=trans)
ax.add_patch(circle)
plt.show()
Notiz
Die Reihenfolge der Transformation ist wichtig. Hier wird die Ellipse zuerst im Anzeigeraum mit den richtigen Abmessungen versehen und dann im Datenraum an die richtige Stelle verschoben. Wenn wir das Erste getan hätten ScaledTranslation
, dann
würden xdata[0]
und ydata[0]
zuerst transformiert werden, um Koordinaten anzuzeigen ( auf einem 200-dpi-Monitor), und dann würden diese Koordinaten skaliert, indem die Mitte der Ellipse weit vom Bildschirm verschoben wird (dh ).[ 358.4 475.2]
fig.dpi_scale_trans
[ 71680. 95040.]
Verwenden von Offset-Transformationen zum Erstellen eines Schatteneffekts #
Eine andere Verwendung von ScaledTranslation
ist, eine neue Transformation zu erstellen, die von einer anderen Transformation versetzt ist, zB um ein Objekt relativ zu einem anderen Objekt ein wenig verschoben zu platzieren. In der Regel möchten Sie, dass die Verschiebung in einer physikalischen Dimension erfolgt, z. B. Punkte oder Zoll, und nicht in Datenkoordinaten
, sodass der Verschiebungseffekt bei verschiedenen Zoomstufen und dpi-Einstellungen konstant ist.
Eine Verwendung für einen Versatz besteht darin, einen Schatteneffekt zu erstellen, bei dem Sie ein Objekt zeichnen, das mit dem ersten identisch ist, direkt rechts davon und direkt darunter, indem Sie die Zorder anpassen, um sicherzustellen, dass zuerst der Schatten gezeichnet wird und dann das Objekt, das es ist Schatten darüber.
Hier wenden wir die Transformationen in umgekehrter Reihenfolge zur Verwendung von
ScaledTranslation
oben an. Der Plot wird zuerst in Datenkoordinaten ( ax.transData
) erstellt und dann mit
dx
und um dy
Punkte verschoben fig.dpi_scale_trans
. (In der Typografie entspricht ein Punkt 1/72 Zoll, und wenn Sie Ihre Offsets in Punkten angeben, sieht Ihre Figur unabhängig von der dpi-Auflösung, in der sie gespeichert ist, gleich aus.)
fig, ax = plt.subplots()
# make a simple sine wave
x = np.arange(0., 2., 0.01)
y = np.sin(2*np.pi*x)
line, = ax.plot(x, y, lw=3, color='blue')
# shift the object over 2 points, and down 2 points
dx, dy = 2/72., -2/72.
offset = transforms.ScaledTranslation(dx, dy, fig.dpi_scale_trans)
shadow_transform = ax.transData + offset
# now plot the same data with our offset transform;
# use the zorder to make sure we are below the line
ax.plot(x, y, lw=3, color='gray',
transform=shadow_transform,
zorder=0.5*line.get_zorder())
ax.set_title('creating a shadow effect with an offset transform')
plt.show()
Notiz
Der dpi- und Zoll-Offset ist ein häufiger Anwendungsfall, für den wir eine spezielle Hilfsfunktion haben, um ihn in zu erstellen matplotlib.transforms.offset_copy()
, die eine neue Transformation mit einem hinzugefügten Offset zurückgibt. Oben hätten wir also tun können:
shadow_transform = transforms.offset_copy(ax.transData,
fig=fig, dx, dy, units='inches')
Die Transformationspipeline #
Die ax.transData
Transformation, mit der wir in diesem Lernprogramm gearbeitet haben, ist eine Zusammensetzung aus drei verschiedenen Transformationen, die die Transformationspipeline von data -> display umfassen
Koordinaten. Michael Droettboom implementierte das Transformations-Framework und achtete darauf, eine saubere API bereitzustellen, die die nichtlinearen Projektionen und Skalen, die in polaren und logarithmischen Diagrammen auftreten, von den linearen affinen Transformationen trennt, die beim Schwenken und Zoomen auftreten. Hier gibt es eine Effizienz, da Sie Ihre Achsen schwenken und zoomen können, was sich auf die affine Transformation auswirkt, aber Sie müssen möglicherweise nicht die möglicherweise teuren nichtlinearen Skalen oder Projektionen für einfache Navigationsereignisse berechnen. Es ist auch möglich, affine Transformationsmatrizen miteinander zu multiplizieren und sie dann in einem Schritt auf Koordinaten anzuwenden. Dies gilt nicht für alle möglichen Transformationen.
So wird die ax.transData
Instanz in der Basisklasse der trennbaren Achsen definiert Axes
:
self.transData = self.transScale + (self.transLimits + self.transAxes)
Uns wurde die transAxes
obige Instanz in
Achsenkoordinaten vorgestellt , die die (0, 0), (1, 1) Ecken der Achsen oder des Subplot-Begrenzungsrahmens abbildet, um den Raum anzuzeigen , also schauen wir uns diese anderen beiden Teile an.
self.transLimits
ist die Transformation, die Sie von
Daten zu Achsenkoordinaten führt ; dh es bildet Ihre Ansicht xlim und ylim auf den Einheitsraum der Achsen ab (und transAxes
nimmt dann diesen Einheitsraum, um den Raum anzuzeigen). Wir können dies hier in Aktion sehen
In [80]: ax = plt.subplot()
In [81]: ax.set_xlim(0, 10)
Out[81]: (0, 10)
In [82]: ax.set_ylim(-1, 1)
Out[82]: (-1, 1)
In [84]: ax.transLimits.transform((0, -1))
Out[84]: array([ 0., 0.])
In [85]: ax.transLimits.transform((10, -1))
Out[85]: array([ 1., 0.])
In [86]: ax.transLimits.transform((10, 1))
Out[86]: array([ 1., 1.])
In [87]: ax.transLimits.transform((5, 0))
Out[87]: array([ 0.5, 0.5])
und wir können dieselbe invertierte Transformation verwenden, um von den Einheitsachsenkoordinaten zurück zu den Datenkoordinaten zu gehen .
In [90]: inv.transform((0.25, 0.25))
Out[90]: array([ 2.5, -0.5])
Das letzte Stück ist das self.transScale
Attribut, das für die optionale nichtlineare Skalierung der Daten, zB für logarithmische Achsen, zuständig ist. Wenn eine Achse anfänglich eingerichtet wird, wird diese nur auf die Identitätstransformation gesetzt, da die grundlegenden Matplotlib-Achsen eine lineare Skalierung haben, aber wenn Sie eine logarithmische Skalierungsfunktion wie aufrufen
semilogx()
oder die Skalierung explizit mit auf logarithmisch setzen set_xscale()
, dann wird das
ax.transScale
Attribut auf handle gesetzt die nichtlineare Projektion. Die Skalentransformationen sind Eigenschaften der jeweiligen xaxis
und
yaxis
Axis
-Instanzen. Wenn Sie beispielsweise aufrufen ax.set_xscale('log')
, aktualisiert die xaxis ihre Skalierung auf eine
matplotlib.scale.LogScale
Instanz.
Für nicht trennbare Achsen, die PolarAxes, muss noch ein weiteres Stück berücksichtigt werden, die Projektionstransformation. Das transData
matplotlib.projections.polar.PolarAxes
ist ähnlich wie bei den typischen trennbaren Matplotlib-Achsen, mit einem zusätzlichen Stück
transProjection
:
self.transData = self.transScale + self.transProjection + \
(self.transProjectionAffine + self.transAxes)
transProjection
übernimmt die Projektion aus dem Raum, z. B. Breite und Länge für Kartendaten oder Radius und Theta für Polardaten, auf ein trennbares kartesisches Koordinatensystem. Es gibt mehrere Projektionsbeispiele im matplotlib.projections
Paket, und der beste Weg, mehr zu erfahren, besteht darin, die Quelle für diese Pakete zu öffnen und zu sehen, wie Sie Ihre eigenen erstellen können, da Matplotlib erweiterbare Achsen und Projektionen unterstützt. Michael Droettboom hat ein nettes Tutorial-Beispiel zum Erstellen von Hammer-Projektionsachsen bereitgestellt; siehe
Benutzerdefinierte Projektion .
Gesamtlaufzeit des Skripts: ( 0 Minuten 3.353 Sekunden)