Базовая карта Matplotlib, по-видимому, не хранит центр карты для последующего переполнения данных - PullRequest
0 голосов
/ 17 февраля 2019

Я хочу нанести среднюю дневную температуру из из Отдела физических наук *1002* Лаборатории исследования системы Земли NOAA на карту, созданную с помощью matplotlib s Basemap.

Набор данных можно загрузить в виде netCDF-файла из здесь .

Однако моя проблема в том, что Basemap, похоже, не хранит центр (или границуполе) координаты карты, поскольку последующий оверплот заполняет только часть карты, см. следующий рисунок:

Map

Код для создания фигуры:следующим образом:

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, все выглядит хорошо (это просто не центральная позиция, которую я хотел бы иметь)

У меня такое ощущение, что решениедовольно очевидно, и я был бы признателен за любую подсказку, указывающую мне в этом направлении.

Ответы [ 3 ]

0 голосов
/ 19 февраля 2019

Как прокомментировано, данные упорядочены от 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()

enter image description here

0 голосов
/ 20 февраля 2019

Оба ответа, опубликованные на данный момент, являются решением моего вопроса (спасибо, ImportanceOfBeingErnest и 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 вызова.


[1] https://matplotlib.org/basemap/api/basemap_api.html

0 голосов
/ 18 февраля 2019

Это сложно.Я разделил массив данных на 2 части.Первая часть простирается от 0 ° до 180 ° восточной долготы.Вторая часть, лежащая на западной стороне 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()

Полученный график:

enter image description here

...