Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A convenient package to read GMT style cpt-files to matplotlib cmaps and mimic the dynamic scaling around a hinge point

License

NotificationsYou must be signed in to change notification settings

shaharkadmiel/cmaptools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

A convenient package to read GMT style cpt-files to matplotlib cmaps andmimic the dynamic scaling around a hinge point. SeeGMT: Master (dynamic) CPTs for more details...

Some colormaps are designed with a color discontinuity to emphasize the boundary between two different domains, for instance between bathymetry and topography. When scaling a dynamic colormap, the lower (v < hinge) and upper (v > hinge) parts are scaled separately. Here is an example:

Install

Clone the repo by typing:

$ git clone https://github.com/shaharkadmiel/cmaptools.git

and install with:

$cd cmaptools$ pip install -e.

Demo

importmatplotlib.pyplotaspltfromcmaptoolsimportreadcpt,joincmap,DynamicColormap

Generic Mapping Tools style cpt files

A GMT color palette table (cpt) file is read and converted to a matplotlib colormap instance withreadcpt. ADynamicColormap instance is returned if the colormap is diverging (vmin < hinge < vmax) or aLinearSegmentedColormap instance if the colormap is sequential.

cptfile='globe.cpt'cmap=readcpt(cptfile)print(cmap.name,cmap.__class__)cmap.preview(-10,8)
globe <class 'cmaptools.DynamicColormap'>

png

In the above example, theglobe.cpt cpt file bundled with GMT is normalized between -1 and 1 with a hinge at 0:

# header info......-1  153/0/255   -0.95   153/0/255...-0.02   241/252/255 0   241/252/2550   51/102/0    0.01    51/204/102...0.95    white   1   white

Thepreview method of theDynamicColormap class shows three representations of the colormap. The top colorbar shows how matplotlib would treat this colormap with out any knowledge of its dynamic properties. Matplotlib colormaps are normalized between 0 and 1 so the hinge value, which is in the middle of the original range, is naturally mapped to 0.5. The colorbar in the middle, shows the original normalization of the colormap and the bottom colorbar shows how this colormap would look like if scaled betweenvmin=-10 andvmax=8. Each of the domains is scaled separately, keeping the hinge at 0 (the original hinge value).

Non GMT style cpt files

There are many more sources for cpt files.cpt-city provides a collection of cpt files for various different applications including ones that are designed for bathymetry and topography, likemby.cpt. This cpt file is not normalized, not symmetric around the color discontinuity and does not contain information about the hinge value in the header:

# header info......-8000   0   0   80      -6000   0   30  100...-10 176 226 255 0   0   97  710       0   97  71      50  16  123 48...4000    206 206 206 5000    255 255 255

Without proper normalization and scaling, this colormap is difficult to manipulate with regard to the color discontinuity. As there is no hinge information in the header, settinghinge=None results in aLinearSegmentedColormap with the color discontinuity somewhere around 0.61:

frommatplotlib.colorbarimportColorbarBasecptfile='mby.cpt'cmap=readcpt(cptfile,hinge=None)print(cmap.name,cmap.__class__)fig,ax=plt.subplots(1,figsize=(4,0.2))fig.subplots_adjust(0,0,1,1,hspace=3)fig.suptitle(cmap.name,y=2)ColorbarBase(ax,cmap,orientation='horizontal')ax.set_xlabel('no norm information')
mby <class 'matplotlib.colors.LinearSegmentedColormap'>

png

readcpt by default assumeshinge=0 so if the values in a cpt file range from negative to positive values, color segments are parsed and scaled to account for the hinge value even if the range is not symmetric around it:

cptfile='mby.cpt'cmap=readcpt(cptfile)print(cmap.name,cmap.__class__)cmap.preview(-10,8)
mby <class 'cmaptools.DynamicColormap'>

png

As before, the top colorbar shows the matplotlib version of the scaled colormap so that the hinge, eventhough not in the middle of the range in the original cpt file, is mapped to 0.5. Middle colorbar shows the colormap with its original norm and the bottom colorbar is shows how this colormap would look like if scaled between vmin=-10 and vmax=8

Joining two colormaps

It is also possible to join two colormaps together:

cptfile1='seafloor.cpt'cmap1=readcpt(cptfile1)print(cmap1.name,cmap1.__class__)cptfile2='dem2.cpt'cmap2=readcpt(cptfile2)print(cmap2.name,cmap2.__class__)cmap=joincmap(cmap1,cmap2)print(cmap.name,cmap.__class__)cmap.preview(-10,8)
seafloor <class 'matplotlib.colors.LinearSegmentedColormap'>dem2 <class 'matplotlib.colors.LinearSegmentedColormap'>seafloor->dem2 <class 'cmaptools.DynamicColormap'>

png

Note thatcmap1 andcmap2 are normal colormap instances ofLinearSegmentedColormap while the joined colormap isDynamicColormap.

This can be done with matplotlib colormaps as well, here scaling between -8 and 5:

cmap=joincmap('Blues_r','pink_r')print(cmap.name,cmap.__class__)cmap.preview(-8,5,0)
Blues_r->pink_r <class 'cmaptools.DynamicColormap'>

png

or a matplotlib colormap and a GMT cpt file:

cmap1=plt.get_cmap('cool_r')print(cmap1.name,cmap1.__class__)cptfile2='dem2.cpt'cmap2=readcpt(cptfile2)print(cmap2.name,cmap2.__class__)cmap=joincmap(cmap1,cmap2)print(cmap.name,cmap.__class__)cmap.preview(-10,8)
cool_r <class 'matplotlib.colors.LinearSegmentedColormap'>dem2 <class 'matplotlib.colors.LinearSegmentedColormap'>cool_r->dem2 <class 'cmaptools.DynamicColormap'>

png

Any colormap can be made dynamic:

cmap=plt.get_cmap('seismic')print(cmap.name,cmap.__class__)cmap=DynamicColormap(cmap)print(cmap.name,cmap.__class__)cmap.preview(-8,2,0)
seismic <class 'matplotlib.colors.LinearSegmentedColormap'>seismic <class 'cmaptools.DynamicColormap'>

png

this is handy when the data being plotted is not symmetric around the hinge value.

The hinge value can set to other than 0

cmap=plt.get_cmap('jet')print(cmap.name,cmap.__class__)cmap=DynamicColormap(cmap)print(cmap.name,cmap.__class__)cmap.preview(-8,5,2)
jet <class 'matplotlib.colors.LinearSegmentedColormap'>jet <class 'cmaptools.DynamicColormap'>

png

Examples with topography and bathymetry

Here I use a subsampled grid of theGEBCO2019 15 arc-second grid.

fromxarrayimportopen_datasetfrommatplotlib.colorsimportLightSourcetopo=open_dataset('GEBCO_2019_960AS.nc')print(topo)ls=LightSource()
<xarray.Dataset>Dimensions:    (extent: 4, lat: 675, lon: 1350)Coordinates:  * lat        (lat) float64 -89.87 -89.6 -89.33 -89.07 ... 89.33 89.6 89.87  * lon        (lon) float64 -179.9 -179.6 -179.3 -179.1 ... 179.3 179.6 179.9  * extent     (extent) float64 -180.0 180.0 -90.0 90.0Data variables:    elevation  (lat, lon) int16 ...    crs        |S1 ...    tlx        float64 ...    tly        float64 ...    dx         float64 ...    dy         float64 ...Attributes:    Conventions:  CF-1.5    GDAL:         GDAL 2.4.1, released 2019/03/15    history:      Subsampled from 15 to 960 arc-second and forced to INT16 wi...    title:        The GEBCO_2019 Grid - a continuous terrain model for oceans...    source:       A subsampled version of the 15 arc-second GEBCO_2019 grid d...

Using the same dynamic colormaps generated above:

cptfile='globe.cpt'cmap=readcpt(cptfile)cmap.set_range(-9e3,6e3)plt.figure(figsize=(6,3))rgb=ls.shade(topo.elevation[::-1].data,cmap,cmap.norm,'overlay',vert_exag=0.01)plt.imshow(rgb,cmap,cmap.norm,extent=topo.extent,aspect='auto',interpolation='bilinear')plt.colorbar(extend='both')plt.contour(topo.elevation,levels=[0],colors='k',linewidths=0.25,extent=topo.extent)

png

cptfile='mby.cpt'cmap=readcpt(cptfile)cmap.set_range(-11e3,6e3)plt.figure(figsize=(6,3))rgb=ls.shade(topo.elevation[::-1].data,cmap,cmap.norm,'overlay',vert_exag=0.01)plt.imshow(rgb,cmap,cmap.norm,extent=topo.extent,aspect='auto',interpolation='bilinear')plt.colorbar(extend='both')plt.contour(topo.elevation,levels=[0],colors='k',linewidths=0.25,extent=topo.extent)

png

cptfile1='seafloor.cpt'cmap1=readcpt(cptfile1)cptfile2='dem2.cpt'cmap2=readcpt(cptfile2)cmap=joincmap(cmap1,cmap2)cmap.set_range(-9e3,6e3)plt.figure(figsize=(6,3))rgb=ls.shade(topo.elevation[::-1].data,cmap,cmap.norm,'overlay',vert_exag=0.01)plt.imshow(rgb,cmap,cmap.norm,extent=topo.extent,aspect='auto',interpolation='bilinear')plt.colorbar(extend='both')plt.contour(topo.elevation,levels=[0],colors='k',linewidths=0.25,extent=topo.extent)

png

cmap=joincmap('Blues_r','pink_r')cmap.set_range(-9e3,5e3)plt.figure(figsize=(6,3))rgb=ls.shade(topo.elevation[::-1].data,cmap,cmap.norm,'overlay',vert_exag=0.01)plt.imshow(rgb,cmap,cmap.norm,extent=topo.extent,aspect='auto',interpolation='bilinear')plt.colorbar(extend='both')plt.contour(topo.elevation,levels=[0],colors='k',linewidths=0.25,extent=topo.extent)

png

Examples with data

make the data:

importnumpyasnpfromscipy.statsimportmultivariate_normaldelta=0.025x=y=np.arange(-3.0,3.0,delta)extent= [-3,3]*2xx,yy=np.meshgrid(x,y)z1=multivariate_normal([0,0], [1,1]).pdf(np.dstack((xx,yy)))z2=multivariate_normal([1,1], [2,0.25]).pdf(np.dstack((xx,yy)))z=z2-2*z1z/=z.max()

plot without normalization:

plt.imshow(z[::-1],'seismic',aspect=1,interpolation='bilinear',extent=extent)plt.colorbar()plt.contour(z,levels=[0],colors='k',linewidths=0.25,extent=extent)

png

Note how the colormap diverges from white but because the min/max of the data is not symmetric around 0, white is not at the center.

One way of handling this is to setvmin andvmax to the- and+ of theabs(z).max():

plt.imshow(z[::-1],'seismic',aspect=1,interpolation='bilinear',extent=extent,vmin=-np.abs(z).max(),vmax=np.abs(z).max())plt.colorbar()plt.contour(z,levels=[0],colors='k',linewidths=0.25,extent=extent)

png

However, the positive part of the data is now pale as some dynamic range is lost.

Making theseismic colormap dynamic, it is now possible to set the range to themin andmax of the data:

cmap=plt.get_cmap('seismic')cmap=DynamicColormap(cmap)cmap.set_range(z.min(),z.max())plt.imshow(z[::-1],cmap,cmap.norm,aspect=1,interpolation='bilinear',extent=extent)plt.colorbar()plt.contour(z,levels=[0],colors='k',linewidths=0.25,extent=extent)

png

About

A convenient package to read GMT style cpt-files to matplotlib cmaps and mimic the dynamic scaling around a hinge point

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp