Skip to content

Commit 673cf47

Browse files
authored
Merge pull request #559 from matplotlib/bugfix-555
Fix broken array slicing inside `addcyclic`
2 parents 41cfd4f + 2e2544c commit 673cf47

File tree

5 files changed

+347
-282
lines changed

5 files changed

+347
-282
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ https://keepachangelog.com/en/1.0.0/
1010
https://semver.org/spec/v2.0.0.html
1111

1212

13+
## [1.3.5]
14+
15+
### Fixed
16+
- Fix broken array slicing inside `addcyclic` (PR [#559], solves issue
17+
[#555], thanks to @fragkoul).
18+
1319
## [1.3.4] - 2022-08-10
1420

1521
### Changed
@@ -924,6 +930,10 @@ https://semver.org/spec/v2.0.0.html
924930
- Fix glitches in drawing of parallels and meridians.
925931

926932

933+
[#559]:
934+
https://github.com/matplotlib/basemap/pull/559
935+
[#555]:
936+
https://github.com/matplotlib/basemap/issues/555
927937
[#548]:
928938
https://github.com/matplotlib/basemap/pull/548
929939
[#547]:

examples/fcstmaps.py

+109-89
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,112 @@
1-
from __future__ import (absolute_import, division, print_function)
1+
"""Make a multi-panel plot from numerical weather forecast in NOAA OPeNDAP."""
2+
from __future__ import print_function
23

3-
from __future__ import unicode_literals
4-
# this example reads today's numerical weather forecasts
5-
# from the NOAA OpenDAP servers and makes a multi-panel plot.
4+
import netCDF4
65
import numpy as np
76
import matplotlib.pyplot as plt
8-
import sys
9-
import numpy.ma as ma
10-
import datetime
11-
from mpl_toolkits.basemap import Basemap, addcyclic
12-
from netCDF4 import Dataset as NetCDFFile, num2date
13-
14-
15-
# today's date is default.
16-
if len(sys.argv) > 1:
17-
YYYYMMDD = sys.argv[1]
18-
else:
19-
YYYYMMDD = datetime.datetime.today().strftime('%Y%m%d')
20-
21-
# set OpenDAP server URL.
22-
try:
23-
URLbase="http://nomads.ncep.noaa.gov:9090/dods/gfs/gfs"
24-
URL=URLbase+YYYYMMDD+'/gfs_00z'
25-
print(URL)
26-
data = NetCDFFile(URL)
27-
except:
28-
msg = """
29-
opendap server not providing the requested data.
30-
Try another date by providing YYYYMMDD on command line."""
31-
raise IOError(msg)
32-
33-
34-
# read lats,lons,times.
35-
36-
print(data.variables.keys())
37-
latitudes = data.variables['lat']
38-
longitudes = data.variables['lon']
39-
fcsttimes = data.variables['time']
40-
times = fcsttimes[0:6] # first 6 forecast times.
41-
ntimes = len(times)
42-
# convert times for datetime instances.
43-
fdates = num2date(times,units=fcsttimes.units,calendar='standard')
44-
# make a list of YYYYMMDDHH strings.
45-
verifdates = [fdate.strftime('%Y%m%d%H') for fdate in fdates]
46-
# convert times to forecast hours.
47-
fcsthrs = []
48-
for fdate in fdates:
49-
fdiff = fdate-fdates[0]
50-
fcsthrs.append(fdiff.days*24. + fdiff.seconds/3600.)
51-
print(fcsthrs)
52-
print(verifdates)
53-
lats = latitudes[:]
54-
nlats = len(lats)
55-
lons1 = longitudes[:]
56-
nlons = len(lons1)
57-
58-
# unpack 2-meter temp forecast data.
59-
60-
t2mvar = data.variables['tmp2m']
61-
t2m = np.zeros((ntimes,nlats,nlons+1),np.float32)
62-
# create Basemap instance for Orthographic projection.
63-
m = Basemap(lon_0=-90,lat_0=60,projection='ortho')
64-
# add wrap-around point in longitude.
65-
for nt in range(ntimes):
66-
t2m[nt,:,:], lons = addcyclic(t2mvar[nt,:,:], lons1)
67-
# convert to celsius.
68-
t2m = t2m-273.15
69-
# contour levels
70-
clevs = np.arange(-30,30.1,2.)
71-
lons, lats = np.meshgrid(lons, lats)
72-
x, y = m(lons, lats)
73-
# create figure.
74-
fig=plt.figure(figsize=(6,8))
75-
# make subplots.
76-
for nt,fcsthr in enumerate(fcsthrs):
77-
ax = fig.add_subplot(321+nt)
78-
cs = m.contourf(x,y,t2m[nt,:,:],clevs,cmap=plt.cm.jet,extend='both')
79-
m.drawcoastlines(linewidth=0.5)
80-
m.drawcountries()
81-
m.drawparallels(np.arange(-80,81,20))
82-
m.drawmeridians(np.arange(0,360,20))
83-
# panel title
84-
plt.title('%d-h forecast valid '%fcsthr+verifdates[nt],fontsize=9)
85-
# figure title
86-
plt.figtext(0.5,0.95,
87-
"2-m temp (\N{DEGREE SIGN}C) forecasts from %s"%verifdates[0],
88-
horizontalalignment='center',fontsize=14)
89-
# a single colorbar.
90-
cax = plt.axes([0.1, 0.05, 0.8, 0.025])
91-
plt.colorbar(cax=cax, orientation='horizontal')
92-
plt.show()
7+
from mpl_toolkits.basemap import Basemap
8+
from mpl_toolkits.basemap import addcyclic
9+
10+
11+
def main(date, verbose=True):
12+
"""Main function."""
13+
14+
# Open dataset from OPeNDAP URL.
15+
url = "http://nomads.ncep.noaa.gov/dods/gfs_0p25/gfs%Y%m%d/gfs_0p25_00z"
16+
try:
17+
data = netCDF4.Dataset(date.strftime(url), "r")
18+
if verbose:
19+
print("Data variables:")
20+
print(sorted(data.variables))
21+
except OSError as err:
22+
err.args = (err.args[0], "date not found in OPeNDAP server")
23+
raise
24+
25+
# Read lats, lons, and times.
26+
latitudes = data.variables["lat"]
27+
longitudes = data.variables["lon"]
28+
fcsttimes = data.variables["time"]
29+
times = fcsttimes[0:6] # First 6 forecast times.
30+
ntimes = len(times)
31+
32+
# Convert times for datetime instances.
33+
fdates = netCDF4.num2date(
34+
times, units=fcsttimes.units, calendar="standard")
35+
36+
# Make a list of YYYYMMDDHH strings.
37+
verifdates = [fdate.strftime("%Y%m%d%H") for fdate in fdates]
38+
if verbose:
39+
print("Forecast datetime strings:")
40+
print(verifdates)
41+
42+
# Convert times to forecast hours.
43+
fcsthrs = []
44+
for fdate in fdates:
45+
fdiff = fdate - fdates[0]
46+
fcsthrs.append(fdiff.days * 24. + fdiff.seconds / 3600.)
47+
if verbose:
48+
print("Forecast datetime hours:")
49+
print(fcsthrs)
50+
51+
# Unpack 2-meter temp forecast data.
52+
lats = latitudes[:]
53+
nlats = len(lats)
54+
lons1 = longitudes[:]
55+
nlons = len(lons1)
56+
t2mvar = data.variables["tmp2m"]
57+
58+
# Create Basemap instance for orthographic projection.
59+
bmap = Basemap(lon_0=-90, lat_0=60, projection="ortho")
60+
61+
# Add wrap-around point in longitude.
62+
t2m = np.zeros((ntimes, nlats, nlons + 1), np.float32)
63+
for nt in range(ntimes):
64+
t2m[nt, :, :], lons = addcyclic(t2mvar[nt, :, :], lons1)
65+
66+
# Convert to Celsius.
67+
t2m = t2m - 273.15
68+
69+
# Define contour levels.
70+
clevs = np.arange(-30, 30.1, 2.0)
71+
lons, lats = np.meshgrid(lons, lats)
72+
x, y = bmap(lons, lats)
73+
74+
# Create figure.
75+
fig = plt.figure(figsize=(6, 8))
76+
77+
# Make subplots.
78+
for nt, fcsthr in enumerate(fcsthrs):
79+
fig.add_subplot(321 + nt)
80+
cs = bmap.contourf(x, y, t2m[nt, :, :], clevs,
81+
cmap=plt.cm.jet, extend="both")
82+
bmap.drawcoastlines(linewidth=0.5)
83+
bmap.drawcountries()
84+
bmap.drawparallels(np.arange(-80, 81, 20))
85+
bmap.drawmeridians(np.arange(0, 360, 20))
86+
# Set panel title.
87+
plt.title(
88+
"%d-h forecast valid " % fcsthr + verifdates[nt], fontsize=9)
89+
90+
# Set figure title.
91+
plt.figtext(
92+
0.5, 0.95,
93+
"2-m temp (\N{DEGREE SIGN}C) forecasts from %s" % verifdates[0],
94+
horizontalalignment="center", fontsize=14)
95+
96+
# Draw a single colorbar.
97+
cax = plt.axes([0.1, 0.05, 0.8, 0.025])
98+
plt.colorbar(cs, cax=cax, orientation="horizontal")
99+
plt.show()
100+
101+
102+
if __name__ == "__main__":
103+
104+
import sys
105+
import datetime as dt
106+
107+
# Parse input date (default: today).
108+
if len(sys.argv) > 1:
109+
dateobj = dt.datetime.strptime(sys.argv[1], "%Y%m%d")
110+
else:
111+
dateobj = dt.datetime.today()
112+
main(dateobj, verbose=True)

0 commit comments

Comments
 (0)