Matplotlib 的 Basemap 似乎不存储地图的中心以供以后过度绘制数据
Matplotlib's Basemap seems to not store map's center for later overplotting of data
我想将 NOAA 地球系统研究实验室物理科学部 的日平均温度绘制到使用 matplotlib
的 Basemap
创建的地图上.
数据集可以作为 netCDF 文件从 here 下载。
但是我的问题是,Basemap
似乎没有存储地图的中心(或边界框)坐标,因为后续的overplot只填充了地图的一部分,见下图:
生成图形的代码如下:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
# to check whether a file exists (before downloading it)
import os.path
import sys
fig1, ax1 = plt.subplots(1,1, figsize=(8,6) )
temperature_fname = 'air.sig995.2016.nc'
url = 'https://www.esrl.noaa.gov/psd/thredds/fileServer/Datasets/ncep.reanalysis.dailyavgs/surface/{0}'.format( temperature_fname)
if not os.path.isfile( temperature_fname ):
print( "ERROR: you need to download the file {0}".format(url) )
sys.exit(1)
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset( temperature_fname )
# extract (copy) the relevant data
tmprt_vals = tmprt_dSet.variables['air'][:] - 273.15
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# close dataset
tmprt_dSet.close()
# use the Miller projection
map1 = Basemap( projection='mill', resolution='l',
lon_0=0., lat_0=0.
)
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.),labels=[0,0,0,1] )
# overplot temperature
## make the longitude and latitude grid projected onto map
tmprt_x, tmprt_y = map1(*np.meshgrid(tmprt_lon,tmprt_lat))
## make the contour plot
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:,:],
cmap=plt.cm.jet
)
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
注意:如果我设置 lon_0=180
一切看起来都很好(这不是我想要的中心位置)
我觉得解决方案非常明显,如果有任何提示可以指出我的方向,我将不胜感激。
这很有挑战性。我将数据数组分成两部分。第一部分跨越 0° 到 180°E 经度。位于0°西侧的第二部分需要经度偏移360°。颜色图必须标准化并应用以获得通用参考颜色。这是工作代码和结果图:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
import matplotlib as mpl
#import os.path
#import sys
fig1, ax1 = plt.subplots(1,1, figsize=(10,6) )
temperature_fname = r'.\air.sig995.2018.nc'
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset( temperature_fname )
# extract (copy) the relevant data
shift_val = - 273.15
tmprt_vals = tmprt_dSet.variables['air'][:] + shift_val
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# prep norm of the color map
color_shf = 40 # to get better lower range of colormap
normalize = mpl.colors.Normalize(tmprt_vals.data.min()+color_shf, \
tmprt_vals.data.max())
# close dataset
#tmprt_dSet.close()
# use the Miller projection
map1 = Basemap( projection='mill', resolution='i', \
lon_0=0., lat_0=0.)
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.), labels=[0,0,0,1] )
# overplot temperature
# split data into 2 parts at column 73 (longitude: +180)
# part1 (take location as is)
beg_col = 0
end_col = 73
grdx, grdy = np.meshgrid(tmprt_lon[beg_col:end_col], tmprt_lat[:])
tmprt_x, tmprt_y = map1(grdx, grdy)
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:, beg_col:end_col],
cmap=plt.cm.jet, norm=normalize)
# part2 (longitude is shifted -360 degrees, but -359.5 looks better)
beg_col4 = 73
end_col4 = 144
grdx, grdy = np.meshgrid(tmprt_lon[beg_col4:end_col4]-359.5, tmprt_lat[:])
tmprt_x, tmprt_y = map1(grdx, grdy)
CS4 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:, beg_col4:end_col4],
cmap=plt.cm.jet, norm=normalize)
# color bars CS1, CS4 are the same (normalized), plot one only
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
结果图:
如评论所述,数据的排列范围为 0 到 360,而不是 -180 到 180。因此您需要
- 将 180 到 360 度之间的范围映射到 -180 到 0。
- 将数据的后半部分移到前半部分的前面,使其升序排列。
在数据提取和绘图函数之间添加以下代码即可。
# map lon values to -180..180 range
f = lambda x: ((x+180) % 360) - 180
tmprt_lon = f(tmprt_lon)
# rearange data
ind = np.argsort(tmprt_lon)
tmprt_lon = tmprt_lon[ind]
tmprt_vals = tmprt_vals[:, :, ind]
完整代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset('data/air.sig995.2018.nc')
# extract (copy) the relevant data
tmprt_vals = tmprt_dSet.variables['air'][:] - 273.15
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# close dataset
tmprt_dSet.close()
### Section added ################
# map lon values to -180..180 range
f = lambda x: ((x+180) % 360) - 180
tmprt_lon = f(tmprt_lon)
# rearange data
ind = np.argsort(tmprt_lon)
tmprt_lon = tmprt_lon[ind]
tmprt_vals = tmprt_vals[:, :, ind]
##################################
fig1, ax1 = plt.subplots(1,1, figsize=(8,6) )
# use the Miller projection
map1 = Basemap( projection='mill', resolution='l',
lon_0=0., lat_0=0. )
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.),labels=[0,0,0,1] )
# overplot temperature
## make the longitude and latitude grid projected onto map
tmprt_x, tmprt_y = map1(*np.meshgrid(tmprt_lon,tmprt_lat))
## make the contour plot
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:,:],
cmap=plt.cm.jet
)
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
到目前为止发布的两个答案都是我问题的解决方案(谢谢,ImportanceOfBeingErnest and swatchai)。
但是,我认为必须有更简单的方法来执行此操作(简单 我的意思是一些 Basemap
实用程序)。所以我再次查看了文档 [1],发现了一些我到目前为止忽略的东西:mpl_toolkits.basemap.shiftgrid
。需要在代码中加入以下两行:
from mpl_toolkits.basemap import shiftgrid
tmprt_vals, tmprt_lon = shiftgrid(180., tmprt_vals, tmprt_lon, start=False)
请注意,必须在 meshgrid
调用之前添加第二行。
我想将 NOAA 地球系统研究实验室物理科学部 的日平均温度绘制到使用 matplotlib
的 Basemap
创建的地图上.
数据集可以作为 netCDF 文件从 here 下载。
但是我的问题是,Basemap
似乎没有存储地图的中心(或边界框)坐标,因为后续的overplot只填充了地图的一部分,见下图:
生成图形的代码如下:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
# to check whether a file exists (before downloading it)
import os.path
import sys
fig1, ax1 = plt.subplots(1,1, figsize=(8,6) )
temperature_fname = 'air.sig995.2016.nc'
url = 'https://www.esrl.noaa.gov/psd/thredds/fileServer/Datasets/ncep.reanalysis.dailyavgs/surface/{0}'.format( temperature_fname)
if not os.path.isfile( temperature_fname ):
print( "ERROR: you need to download the file {0}".format(url) )
sys.exit(1)
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset( temperature_fname )
# extract (copy) the relevant data
tmprt_vals = tmprt_dSet.variables['air'][:] - 273.15
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# close dataset
tmprt_dSet.close()
# use the Miller projection
map1 = Basemap( projection='mill', resolution='l',
lon_0=0., lat_0=0.
)
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.),labels=[0,0,0,1] )
# overplot temperature
## make the longitude and latitude grid projected onto map
tmprt_x, tmprt_y = map1(*np.meshgrid(tmprt_lon,tmprt_lat))
## make the contour plot
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:,:],
cmap=plt.cm.jet
)
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
注意:如果我设置 lon_0=180
一切看起来都很好(这不是我想要的中心位置)
我觉得解决方案非常明显,如果有任何提示可以指出我的方向,我将不胜感激。
这很有挑战性。我将数据数组分成两部分。第一部分跨越 0° 到 180°E 经度。位于0°西侧的第二部分需要经度偏移360°。颜色图必须标准化并应用以获得通用参考颜色。这是工作代码和结果图:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
import matplotlib as mpl
#import os.path
#import sys
fig1, ax1 = plt.subplots(1,1, figsize=(10,6) )
temperature_fname = r'.\air.sig995.2018.nc'
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset( temperature_fname )
# extract (copy) the relevant data
shift_val = - 273.15
tmprt_vals = tmprt_dSet.variables['air'][:] + shift_val
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# prep norm of the color map
color_shf = 40 # to get better lower range of colormap
normalize = mpl.colors.Normalize(tmprt_vals.data.min()+color_shf, \
tmprt_vals.data.max())
# close dataset
#tmprt_dSet.close()
# use the Miller projection
map1 = Basemap( projection='mill', resolution='i', \
lon_0=0., lat_0=0.)
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.), labels=[0,0,0,1] )
# overplot temperature
# split data into 2 parts at column 73 (longitude: +180)
# part1 (take location as is)
beg_col = 0
end_col = 73
grdx, grdy = np.meshgrid(tmprt_lon[beg_col:end_col], tmprt_lat[:])
tmprt_x, tmprt_y = map1(grdx, grdy)
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:, beg_col:end_col],
cmap=plt.cm.jet, norm=normalize)
# part2 (longitude is shifted -360 degrees, but -359.5 looks better)
beg_col4 = 73
end_col4 = 144
grdx, grdy = np.meshgrid(tmprt_lon[beg_col4:end_col4]-359.5, tmprt_lat[:])
tmprt_x, tmprt_y = map1(grdx, grdy)
CS4 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:, beg_col4:end_col4],
cmap=plt.cm.jet, norm=normalize)
# color bars CS1, CS4 are the same (normalized), plot one only
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
结果图:
如评论所述,数据的排列范围为 0 到 360,而不是 -180 到 180。因此您需要
- 将 180 到 360 度之间的范围映射到 -180 到 0。
- 将数据的后半部分移到前半部分的前面,使其升序排列。
在数据提取和绘图函数之间添加以下代码即可。
# map lon values to -180..180 range
f = lambda x: ((x+180) % 360) - 180
tmprt_lon = f(tmprt_lon)
# rearange data
ind = np.argsort(tmprt_lon)
tmprt_lon = tmprt_lon[ind]
tmprt_vals = tmprt_vals[:, :, ind]
完整代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import netCDF4
# read netCDF4 dataset
tmprt_dSet = netCDF4.Dataset('data/air.sig995.2018.nc')
# extract (copy) the relevant data
tmprt_vals = tmprt_dSet.variables['air'][:] - 273.15
tmprt_lat = tmprt_dSet.variables['lat'][:]
tmprt_lon = tmprt_dSet.variables['lon'][:]
# close dataset
tmprt_dSet.close()
### Section added ################
# map lon values to -180..180 range
f = lambda x: ((x+180) % 360) - 180
tmprt_lon = f(tmprt_lon)
# rearange data
ind = np.argsort(tmprt_lon)
tmprt_lon = tmprt_lon[ind]
tmprt_vals = tmprt_vals[:, :, ind]
##################################
fig1, ax1 = plt.subplots(1,1, figsize=(8,6) )
# use the Miller projection
map1 = Basemap( projection='mill', resolution='l',
lon_0=0., lat_0=0. )
# draw coastline, map-boundary
map1.drawcoastlines()
map1.drawmapboundary( fill_color='white' )
# draw grid
map1.drawparallels( np.arange(-90.,90.,30.), labels=[1,0,0,0] )
map1.drawmeridians( np.arange(-180.,180.,60.),labels=[0,0,0,1] )
# overplot temperature
## make the longitude and latitude grid projected onto map
tmprt_x, tmprt_y = map1(*np.meshgrid(tmprt_lon,tmprt_lat))
## make the contour plot
CS1 = map1.contourf( tmprt_x, tmprt_y, tmprt_vals[0,:,:],
cmap=plt.cm.jet
)
cbar1 = map1.colorbar( CS1, location='right' )
cbar1.set_label( r'$T$ in $^\circ$C')
plt.show()
到目前为止发布的两个答案都是我问题的解决方案(谢谢,ImportanceOfBeingErnest and swatchai)。
但是,我认为必须有更简单的方法来执行此操作(简单 我的意思是一些 Basemap
实用程序)。所以我再次查看了文档 [1],发现了一些我到目前为止忽略的东西:mpl_toolkits.basemap.shiftgrid
。需要在代码中加入以下两行:
from mpl_toolkits.basemap import shiftgrid
tmprt_vals, tmprt_lon = shiftgrid(180., tmprt_vals, tmprt_lon, start=False)
请注意,必须在 meshgrid
调用之前添加第二行。