Python equivalente para el Demcmap de Matlab (elevación +/- mapa de colores apropiado)

Estoy buscando una manera de obtener un mapa de matplotlib apropiado para la elevación de matplotlib .

el ‘ terrain ‘ de cmap se ve muy bien, pero la escala de colores no se basa en cero (es decir, si la escala es 0-> 5000m, el rango 0-> 1000m puede tener tonos de azul, lo que supondría que está por debajo del nivel del mar )

por ejemplo: introduzca la descripción de la imagen aquí

La función equivalente de Matlab es: demcmap

¿Cuál es la mejor manera de hacer que matplotlib desplace los verdes / marrones y azules de un mapa de terreno sobre un terreno alrededor de la marca de elevación cero?

Desafortunadamente, matplotlib no proporciona la funcionalidad del demcmap de Matlab. En realidad, podría haber algunas características basemap paquete de basemap Python, de las que no tengo conocimiento.

Por lo tanto, al apegarnos a las opciones a bordo de matplotlib, podemos hacer una subclase de Normalize para crear una normalización de color centrada alrededor de un punto en el medio del mapa de colores. Esta técnica se puede encontrar en otra pregunta en StackOverflow y se adapta a las necesidades específicas, es decir, para establecer un sealevel (que probablemente se elija mejor como 0 ) y el valor en el mapa de col_val (que varía entre 0 y 1) para el cual este nivel del mar debería corresponder. En el caso del mapa del terreno, parece que 0.22 , correspondiente a un color turquesa, podría ser una buena opción.
La instancia de Normalizar se puede dar entonces como un argumento para imshow . Las figuras resultantes se pueden ver abajo en la primera fila de la imagen.

Debido a la transición suave alrededor del nivel del mar, los valores alrededor de 0 aparecen en un color turquesa, lo que dificulta la distinción entre tierra y mar.
Por lo tanto, podemos cambiar un poco el mapa del terreno y recortar esos colores, para que la costa sea mejor visible. Esto se hace combinando dos partes del mapa, que van de 0 a 0.17 y de 0.25 a 1, y por lo tanto, se recorta una parte del mapa.

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

introduzca la descripción de la imagen aquí