2016-11-30 4 views
1

Ich suche nach einer Möglichkeit, eine passende Colormap für matplotlib zu erhalten.Python-Äquivalent für Matlabs Demcmap (Elevation +/- entsprechende Colormap)

Die cmap 'terrain' sieht gut aus, aber die Farbskalierung basiert nicht auf Null (dh wenn die Skala zwischen 0 und> 5000m liegt, kann der Bereich zwischen 0 und> 1000m blau sein, was man annehmen würde unter dem Meeresspiegel)

zum Beispiel: enter image description here

die äquivalente Matlab-Funktion ist: demcmap

Was ist der beste Weg, matplotlib zu bekommen ein Gelände colormap Greens/Braun und Blau um die Verschiebung null Erhebung n Marke?

+0

Wie sollte die matplotlib colormap wissen, welcher Datenwert in Ihrem Grundstück auf den Meeresspiegel gehört? – ImportanceOfBeingErnest

+0

@ImportanceOfBeingErnest Der Meeresspiegel ist 0m, deshalb habe ich gesagt, dass ich die Colormap um die Null-Höhenkarte verschieben soll – mjp

Antwort

3

Leider bietet Matplotlib nicht die Funktionalität von Matlab demcmap. Es könnte tatsächlich einige eingebaute Funktionen im Python basemap Paket, von denen ich nicht bewusst bin.

Wenn Sie also an den Optionen von matplotlib on-board festhalten, können wir Normalize ableiten, um eine Farbnormalisierung zu erstellen, die um einen Punkt in der Mitte der Farbpalette zentriert ist. Diese Technik kann in another question auf StackOverflow gefunden und an die spezifischen Bedürfnisse angepasst werden, nämlich eine sealevel (die wahrscheinlich am besten als 0 gewählt wird) und den Wert in der Colormap col_val (zwischen 0 und 1), denen diese Meereshöhe entsprechen sollte . Im Fall der Geländekarte scheint es, dass 0.22, was einer türkisfarbenen Farbe entspricht, eine gute Wahl sein könnte.
Die Normalize-Instanz kann dann als Argument an imshow gegeben werden. Die resultierenden Zahlen sind unten in der ersten Reihe des Bildes zu sehen.

Durch den sanften Übergang um den Meeresspiegel erscheinen die Werte um 0 türkis, so dass es schwierig ist, zwischen Land und Meer zu unterscheiden.
Wir können daher die Geländekarte ein wenig ändern und diese Farben ausschneiden, so dass die Küste besser sichtbar ist. Dies geschieht durch combining two parts der Karte, von 0 bis 0,17 und von 0,25 bis 1, und damit einen Teil davon ausschneiden.

import numpy as np 
import matplotlib.pyplot as plt 
import matplotlib.colors 

class FixPointNormalize(matplotlib.colors.Normalize): 
    """ 
    Inspired by https://stackoverflow.com/questions/20144529/shifted-colorbar-matplotlib 
    Subclassing Normalize to obtain a colormap with a fixpoint 
    somewhere in the middle of the colormap. 

    This may be useful for a `terrain` map, to set the "sea level" 
    to a color in the blue/turquise range. 
    """ 
    def __init__(self, vmin=None, vmax=None, sealevel=0, col_val = 0.21875, clip=False): 
     # sealevel is the fix point of the colormap (in data units) 
     self.sealevel = sealevel 
     # col_val is the color value in the range [0,1] that should represent the sealevel. 
     self.col_val = col_val 
     matplotlib.colors.Normalize.__init__(self, vmin, vmax, clip) 

    def __call__(self, value, clip=None): 
     x, y = [self.vmin, self.sealevel, self.vmax], [0, self.col_val, 1] 
     return np.ma.masked_array(np.interp(value, x, y)) 

# Combine the lower and upper range of the terrain colormap with a gap in the middle 
# to let the coastline appear more prominently. 
# inspired by https://stackoverflow.com/questions/31051488/combining-two-matplotlib-colormaps 
colors_undersea = plt.cm.terrain(np.linspace(0, 0.17, 56)) 
colors_land = plt.cm.terrain(np.linspace(0.25, 1, 200)) 
# combine them and build a new colormap 
colors = np.vstack((colors_undersea, colors_land)) 
cut_terrain_map = matplotlib.colors.LinearSegmentedColormap.from_list('cut_terrain', colors) 



# invent some data (height in meters relative to sea level) 
data = np.linspace(-1000,2400,15**2).reshape((15,15)) 


# plot example data 
fig, ax = plt.subplots(nrows = 2, ncols=3, figsize=(11,6)) 
plt.subplots_adjust(left=0.08, right=0.95, bottom=0.05, top=0.92, hspace = 0.28, wspace = 0.15) 

plt.figtext(.5, 0.95, "Using 'terrain' and FixedPointNormalize", ha="center", size=14) 
norm = FixPointNormalize(sealevel=0, vmax=3400) 
im = ax[0,0].imshow(data+1000, norm=norm, cmap=plt.cm.terrain) 
fig.colorbar(im, ax=ax[0,0]) 

norm2 = FixPointNormalize(sealevel=0, vmax=3400) 
im2 = ax[0,1].imshow(data, norm=norm2, cmap=plt.cm.terrain) 
fig.colorbar(im2, ax=ax[0,1]) 

norm3 = FixPointNormalize(sealevel=0, vmax=0) 
im3 = ax[0,2].imshow(data-2400.1, norm=norm3, cmap=plt.cm.terrain) 
fig.colorbar(im3, ax=ax[0,2]) 

plt.figtext(.5, 0.46, "Using custom cut map and FixedPointNormalize (adding hard edge between land and sea)", ha="center", size=14) 
norm4 = FixPointNormalize(sealevel=0, vmax=3400) 
im4 = ax[1,0].imshow(data+1000, norm=norm4, cmap=cut_terrain_map) 
fig.colorbar(im4, ax=ax[1,0]) 

norm5 = FixPointNormalize(sealevel=0, vmax=3400) 
im5 = ax[1,1].imshow(data, norm=norm5, cmap=cut_terrain_map) 
cbar = fig.colorbar(im5, ax=ax[1,1]) 

norm6 = FixPointNormalize(sealevel=0, vmax=0) 
im6 = ax[1,2].imshow(data-2400.1, norm=norm6, cmap=cut_terrain_map) 
fig.colorbar(im6, ax=ax[1,2]) 

for i, name in enumerate(["land only", "coast line", "sea only"]): 
    for j in range(2): 
     ax[j,i].text(0.96,0.96,name, ha="right", va="top", transform=ax[j,i].transAxes, color="w") 

plt.show() 

enter image description here