Skip to content

Commit f37aed3

Browse files
committedOct 25, 2022
Merge branch 'hotfix-1.3.5'
2 parents 41cfd4f + a8c0e86 commit f37aed3

12 files changed

+487
-369
lines changed
 

‎.github/workflows/basemap-data-hires.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: basemap-data-hires
22

33
env:
44
PKGDIR: "packages/basemap_data_hires"
5+
PYTHONWARNINGS: "ignore:DEPRECATION"
6+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
57

68
on:
79
push:
@@ -21,7 +23,7 @@ jobs:
2123
steps:
2224
-
2325
name: Checkout
24-
uses: actions/checkout@v2
26+
uses: actions/checkout@v3
2527
-
2628
name: Upload checkout
2729
uses: actions/upload-artifact@v1

‎.github/workflows/basemap-data.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: basemap-data
22

33
env:
44
PKGDIR: "packages/basemap_data"
5+
PYTHONWARNINGS: "ignore:DEPRECATION"
6+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
57

68
on:
79
push:
@@ -21,7 +23,7 @@ jobs:
2123
steps:
2224
-
2325
name: Checkout
24-
uses: actions/checkout@v2
26+
uses: actions/checkout@v3
2527
-
2628
name: Upload checkout
2729
uses: actions/upload-artifact@v1

‎.github/workflows/basemap-for-manylinux.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: basemap-for-manylinux
22

33
env:
44
PKGDIR: "packages/basemap"
5+
PYTHONWARNINGS: "ignore:DEPRECATION"
6+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
57

68
on:
79
push:
@@ -21,7 +23,7 @@ jobs:
2123
steps:
2224
-
2325
name: Checkout
24-
uses: actions/checkout@v2
26+
uses: actions/checkout@v3
2527
-
2628
name: Upload checkout
2729
uses: actions/upload-artifact@v1

‎.github/workflows/basemap-for-windows.yml

+14-25
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ name: basemap-for-windows
22

33
env:
44
PKGDIR: "packages/basemap"
5+
PYTHONWARNINGS: "ignore:DEPRECATION"
6+
PIP_DISABLE_PIP_VERSION_CHECK: "1"
57

68
on:
79
push:
@@ -21,7 +23,7 @@ jobs:
2123
steps:
2224
-
2325
name: Checkout
24-
uses: actions/checkout@v2
26+
uses: actions/checkout@v3
2527
-
2628
name: Upload checkout
2729
uses: actions/upload-artifact@v1
@@ -47,7 +49,7 @@ jobs:
4749
path: .
4850
-
4951
name: Set Python
50-
uses: actions/setup-python@v2
52+
uses: actions/setup-python@v4
5153
with:
5254
architecture: ${{ matrix.arch }}
5355
python-version: ${{ matrix.python-version }}
@@ -85,23 +87,10 @@ jobs:
8587
build-geos:
8688
strategy:
8789
matrix:
88-
include:
89-
-
90-
arch: "x64"
91-
msvc-toolset: "14.16"
92-
cmake-version: "3.14.7"
93-
-
94-
arch: "x86"
95-
msvc-toolset: "14.16"
96-
cmake-version: "3.13.2"
97-
-
98-
arch: "x64"
99-
msvc-toolset: "9.0"
100-
cmake-version: "3.14.7"
101-
-
102-
arch: "x86"
103-
msvc-toolset: "9.0"
104-
cmake-version: "3.13.2"
90+
arch:
91+
["x64", "x86"]
92+
msvc-toolset:
93+
["9.0", "14.16"]
10594
max-parallel: 4
10695
fail-fast: false
10796
needs: lint
@@ -121,12 +110,12 @@ jobs:
121110
version: ${{ matrix.msvc-toolset }}
122111
-
123112
name: Set CMake
124-
uses: jwlawson/actions-setup-cmake@v1.9
113+
uses: jwlawson/actions-setup-cmake@v1.13
125114
with:
126-
cmake-version: ${{ matrix.cmake-version }}
115+
cmake-version: "3.14.7"
127116
-
128117
name: Set Python
129-
uses: actions/setup-python@v2
118+
uses: actions/setup-python@v4
130119
with:
131120
architecture: ${{ matrix.arch }}
132121
python-version: "3.6"
@@ -176,7 +165,7 @@ jobs:
176165
version: ${{ env.msvc-toolset }}
177166
-
178167
name: Set Python
179-
uses: actions/setup-python@v2
168+
uses: actions/setup-python@v4
180169
with:
181170
architecture: ${{ matrix.arch }}
182171
python-version: ${{ matrix.python-version }}
@@ -229,7 +218,7 @@ jobs:
229218
steps:
230219
-
231220
name: Set Python
232-
uses: actions/setup-python@v2
221+
uses: actions/setup-python@v4
233222
with:
234223
architecture: ${{ matrix.arch }}
235224
python-version: ${{ matrix.python-version }}
@@ -264,7 +253,7 @@ jobs:
264253
steps:
265254
-
266255
name: Set Python
267-
uses: actions/setup-python@v2
256+
uses: actions/setup-python@v4
268257
with:
269258
architecture: ${{ matrix.arch }}
270259
python-version: ${{ matrix.python-version }}

‎CHANGELOG.md

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

1212

13+
## [1.3.5] - 2022-10-25
14+
15+
### Fixed
16+
- Fix broken array slicing inside `addcyclic` (PR [#559], solves issue
17+
[#555], thanks to @fragkoul).
18+
- Fix `GeosLibrary` wrapper to also work with GEOS >= 3.7.0 on Windows
19+
and GNU/Linux.
20+
- Fix wrong Antarctica coastline boundary with GEOS >= 3.9.0 (PR [#560],
21+
solves issue [#522]).
22+
1323
## [1.3.4] - 2022-08-10
1424

1525
### Changed
@@ -924,6 +934,12 @@ https://semver.org/spec/v2.0.0.html
924934
- Fix glitches in drawing of parallels and meridians.
925935

926936

937+
[#560]:
938+
https://github.com/matplotlib/basemap/pull/560
939+
[#559]:
940+
https://github.com/matplotlib/basemap/pull/559
941+
[#555]:
942+
https://github.com/matplotlib/basemap/issues/555
927943
[#548]:
928944
https://github.com/matplotlib/basemap/pull/548
929945
[#547]:
@@ -956,6 +972,8 @@ https://github.com/matplotlib/basemap/issues/527
956972
https://github.com/matplotlib/basemap/issues/526
957973
[#525]:
958974
https://github.com/matplotlib/basemap/issues/525
975+
[#522]:
976+
https://github.com/matplotlib/basemap/issues/522
959977
[#521]:
960978
https://github.com/matplotlib/basemap/issues/521
961979
[#518]:
@@ -1002,7 +1020,9 @@ https://github.com/matplotlib/basemap/issues/228
10021020
https://github.com/matplotlib/basemap/issues/179
10031021

10041022
[Unreleased]:
1005-
https://github.com/matplotlib/basemap/compare/v1.3.4...develop
1023+
https://github.com/matplotlib/basemap/compare/v1.3.5...develop
1024+
[1.3.5]:
1025+
https://github.com/matplotlib/basemap/compare/v1.3.4...v1.3.5
10061026
[1.3.4]:
10071027
https://github.com/matplotlib/basemap/compare/v1.3.3...v1.3.4
10081028
[1.3.3]:

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

‎examples/fcstmaps_axesgrid.py

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

‎examples/plothighsandlows.py

+105-91
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,108 @@
1-
from __future__ import (absolute_import, division, print_function)
1+
"""Plot H's and L's on a sea-level pressure map."""
2+
from __future__ import print_function
23

3-
"""
4-
plot H's and L's on a sea-level pressure map
5-
(uses scipy.ndimage.filters and netcdf4-python)
6-
"""
4+
import datetime as dt
5+
import netCDF4
76
import numpy as np
87
import matplotlib.pyplot as plt
9-
from datetime import datetime
10-
from mpl_toolkits.basemap import Basemap, addcyclic
11-
from scipy.ndimage.filters import minimum_filter, maximum_filter
12-
from netCDF4 import Dataset
13-
14-
def extrema(mat,mode='wrap',window=10):
15-
"""find the indices of local extrema (min and max)
16-
in the input array."""
17-
mn = minimum_filter(mat, size=window, mode=mode)
18-
mx = maximum_filter(mat, size=window, mode=mode)
19-
# (mat == mx) true if pixel is equal to the local max
20-
# (mat == mn) true if pixel is equal to the local in
21-
# Return the indices of the maxima, minima
22-
return np.nonzero(mat == mn), np.nonzero(mat == mx)
23-
24-
# plot 00 UTC today.
25-
date = datetime.now().strftime('%Y%m%d')+'00'
26-
27-
# open OpenDAP dataset.
28-
#data=Dataset("http://nomads.ncep.noaa.gov:9090/dods/gfs/gfs/%s/gfs_%sz_anl" %\
29-
# (date[0:8],date[8:10]))
30-
data=Dataset("http://nomads.ncep.noaa.gov:9090/dods/gfs_hd/gfs_hd%s/gfs_hd_%sz"%\
31-
(date[0:8],date[8:10]))
32-
33-
34-
35-
# read lats,lons.
36-
lats = data.variables['lat'][:]
37-
lons1 = data.variables['lon'][:]
38-
nlats = len(lats)
39-
nlons = len(lons1)
40-
# read prmsl, convert to hPa (mb).
41-
prmsl = 0.01*data.variables['prmslmsl'][0]
42-
# the window parameter controls the number of highs and lows detected.
43-
# (higher value, fewer highs and lows)
44-
local_min, local_max = extrema(prmsl, mode='wrap', window=50)
45-
# create Basemap instance.
46-
m =\
47-
Basemap(llcrnrlon=0,llcrnrlat=-80,urcrnrlon=360,urcrnrlat=80,projection='mill')
48-
# add wrap-around point in longitude.
49-
prmsl, lons = addcyclic(prmsl, lons1)
50-
# contour levels
51-
clevs = np.arange(900,1100.,5.)
52-
# find x,y of map projection grid.
53-
lons, lats = np.meshgrid(lons, lats)
54-
x, y = m(lons, lats)
55-
# create figure.
56-
fig=plt.figure(figsize=(8,4.5))
57-
ax = fig.add_axes([0.05,0.05,0.9,0.85])
58-
cs = m.contour(x,y,prmsl,clevs,colors='k',linewidths=1.)
59-
m.drawcoastlines(linewidth=1.25)
60-
m.fillcontinents(color='0.8')
61-
m.drawparallels(np.arange(-80,81,20),labels=[1,1,0,0])
62-
m.drawmeridians(np.arange(0,360,60),labels=[0,0,0,1])
63-
xlows = x[local_min]; xhighs = x[local_max]
64-
ylows = y[local_min]; yhighs = y[local_max]
65-
lowvals = prmsl[local_min]; highvals = prmsl[local_max]
66-
# plot lows as blue L's, with min pressure value underneath.
67-
xyplotted = []
68-
# don't plot if there is already a L or H within dmin meters.
69-
yoffset = 0.022*(m.ymax-m.ymin)
70-
dmin = yoffset
71-
for x,y,p in zip(xlows, ylows, lowvals):
72-
if x < m.xmax and x > m.xmin and y < m.ymax and y > m.ymin:
73-
dist = [np.sqrt((x-x0)**2+(y-y0)**2) for x0,y0 in xyplotted]
74-
if not dist or min(dist) > dmin:
75-
plt.text(x,y,'L',fontsize=14,fontweight='bold',
76-
ha='center',va='center',color='b')
77-
plt.text(x,y-yoffset,repr(int(p)),fontsize=9,
78-
ha='center',va='top',color='b',
79-
bbox = dict(boxstyle="square",ec='None',fc=(1,1,1,0.5)))
80-
xyplotted.append((x,y))
81-
# plot highs as red H's, with max pressure value underneath.
82-
xyplotted = []
83-
for x,y,p in zip(xhighs, yhighs, highvals):
84-
if x < m.xmax and x > m.xmin and y < m.ymax and y > m.ymin:
85-
dist = [np.sqrt((x-x0)**2+(y-y0)**2) for x0,y0 in xyplotted]
86-
if not dist or min(dist) > dmin:
87-
plt.text(x,y,'H',fontsize=14,fontweight='bold',
88-
ha='center',va='center',color='r')
89-
plt.text(x,y-yoffset,repr(int(p)),fontsize=9,
90-
ha='center',va='top',color='r',
91-
bbox = dict(boxstyle="square",ec='None',fc=(1,1,1,0.5)))
92-
xyplotted.append((x,y))
93-
plt.title('Mean Sea-Level Pressure (with Highs and Lows) %s' % date)
94-
plt.show()
8+
from mpl_toolkits.basemap import Basemap
9+
from mpl_toolkits.basemap import addcyclic
10+
from scipy.ndimage import minimum_filter
11+
from scipy.ndimage import maximum_filter
12+
13+
14+
def extrema(mat, mode="wrap", window=10):
15+
"""Find the indices of local extrema (min and max) in the input array."""
16+
17+
minimum = minimum_filter(mat, size=window, mode=mode)
18+
maximum = maximum_filter(mat, size=window, mode=mode)
19+
20+
# Return the indices of the maxima, minima.
21+
# (mat == maximum) true if pixel is equal to the local max.
22+
# (mat == minimum) true if pixel is equal to the local in.
23+
return np.nonzero(mat == minimum), np.nonzero(mat == maximum)
24+
25+
26+
def main():
27+
"""Main function."""
28+
29+
# Plot 00 UTC today.
30+
url = "http://nomads.ncep.noaa.gov/dods/gfs_0p25/gfs%Y%m%d/gfs_0p25_00z"
31+
date = dt.datetime.now()
32+
33+
# Open OPeNDAP dataset.
34+
data = netCDF4.Dataset(date.strftime(url))
35+
36+
# Read lats and lons.
37+
lats = data.variables["lat"][:]
38+
lons1 = data.variables["lon"][:]
39+
40+
# Read prmsl and convert to hPa (mbar).
41+
prmsl = 0.01 * data.variables["prmslmsl"][0]
42+
43+
# The window parameter controls the number of highs and lows detected
44+
# (higher value, fewer highs and lows).
45+
local_min, local_max = extrema(prmsl, mode="wrap", window=50)
46+
47+
# Create Basemap instance.
48+
bmap = Basemap(projection="mill",
49+
llcrnrlon=0, llcrnrlat=-80,
50+
urcrnrlon=360, urcrnrlat=80)
51+
52+
# Add wrap-around point in longitude.
53+
prmsl, lons = addcyclic(prmsl, lons1)
54+
55+
# Define contour levels.
56+
clevs = np.arange(900, 1100., 5.)
57+
58+
# Find x, y of map projection grid.
59+
lons, lats = np.meshgrid(lons, lats)
60+
x, y = bmap(lons, lats)
61+
62+
# Create figure.
63+
fig = plt.figure(figsize=(8, 4.5))
64+
fig.add_axes([0.05, 0.05, 0.9, 0.85])
65+
bmap.contour(x, y, prmsl, clevs, colors="k", linewidths=1.0)
66+
bmap.drawcoastlines(linewidth=1.25)
67+
bmap.fillcontinents(color="0.8")
68+
bmap.drawparallels(np.arange(-80, 81, 20), labels=[1, 1, 0, 0])
69+
bmap.drawmeridians(np.arange(0, 360, 60), labels=[0, 0, 0, 1])
70+
xlows, xhighs = x[local_min], x[local_max]
71+
ylows, yhighs = y[local_min], y[local_max]
72+
lowvals, highvals = prmsl[local_min], prmsl[local_max]
73+
74+
# Plot lows as blue L's, with min pressure value underneath.
75+
# Do not plot if there is already a L or H within dmin meters.
76+
xyplotted = []
77+
yoffset = 0.022 * (bmap.ymax - bmap.ymin)
78+
dmin = yoffset
79+
for x, y, p in zip(xlows, ylows, lowvals):
80+
if bmap.xmin < x < bmap.xmax and bmap.ymin < y < bmap.ymax:
81+
dist = [np.sqrt((x - x0)**2 + (y - y0)**2) for x0, y0 in xyplotted]
82+
if not dist or min(dist) > dmin:
83+
bbox = dict(boxstyle="square", ec="None", fc=(1, 1, 1, 0.5))
84+
plt.text(x, y, "L", fontsize=14, fontweight="bold",
85+
ha="center", va="center", color="b")
86+
plt.text(x, y - yoffset, repr(int(p)), fontsize=9,
87+
ha="center", va="top", color="b", bbox=bbox)
88+
xyplotted.append((x, y))
89+
# Plot highs as red H's, with max pressure value underneath.
90+
xyplotted = []
91+
for x, y, p in zip(xhighs, yhighs, highvals):
92+
if bmap.xmin < x < bmap.xmax and bmap.ymin < y < bmap.ymax:
93+
dist = [np.sqrt((x - x0)**2 + (y - y0)**2) for x0, y0 in xyplotted]
94+
if not dist or min(dist) > dmin:
95+
bbox = dict(boxstyle="square", ec="None", fc=(1, 1, 1, 0.5))
96+
plt.text(x, y, "H", fontsize=14, fontweight="bold",
97+
ha="center", va="center", color="r")
98+
plt.text(x, y - yoffset, repr(int(p)), fontsize=9,
99+
ha="center", va="top", color="r", bbox=bbox)
100+
xyplotted.append((x, y))
101+
102+
# Set plot title and show.
103+
plt.title("Mean Sea-Level Pressure (with Highs and Lows) %s" % date)
104+
plt.show()
105+
106+
107+
if __name__ == "__main__":
108+
main()

‎packages/basemap/setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def run(self):
172172
"name":
173173
"basemap",
174174
"version":
175-
"1.3.4",
175+
"1.3.5",
176176
"license":
177177
"MIT",
178178
"description":

‎packages/basemap/src/mpl_toolkits/basemap/__init__.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@
5353
import _geoslib
5454
import functools
5555

56+
57+
__version__ = "1.3.5"
58+
5659
# basemap data files now installed in lib/matplotlib/toolkits/basemap/data
5760
# check to see if environment variable BASEMAPDATA set to a directory,
5861
# and if so look for the data there.
@@ -64,8 +67,6 @@
6467
from mpl_toolkits import basemap_data
6568
basemap_datadir = os.path.abspath(list(basemap_data.__path__)[0])
6669

67-
__version__ = "1.3.4"
68-
6970
# module variable that sets the default value for the 'latlon' kwarg.
7071
# can be set to True by user so plotting functions can take lons,lats
7172
# in degrees by default, instead of x,y (map projection coords in meters).
@@ -1336,7 +1337,7 @@ def _readboundarydata(self,name,as_polygons=False):
13361337
b2 = b.copy()
13371338
# fix Antartica.
13381339
if name == 'gshhs' and south < -89:
1339-
b = b[4:,:]
1340+
b = b[3:,:]
13401341
b2 = b.copy()
13411342
poly = Shape(b)
13421343
# if map boundary polygon is a valid one in lat/lon
@@ -5127,7 +5128,7 @@ def _addcyclic(a):
51275128
except IndexError:
51285129
raise ValueError('The specified axis does not correspond to an '
51295130
'array dimension.')
5130-
return npsel.concatenate((a,a[slicer]),axis=axis)
5131+
return npsel.concatenate((a,a[tuple(slicer)]),axis=axis)
51315132
def _addcyclic_lon(a):
51325133
"""addcyclic function for a single longitude array"""
51335134
# select the right numpy functions

‎packages/basemap/src/mpl_toolkits/basemap/proj.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
# as textwrap.dedent.
1111
from matplotlib.cbook import dedent
1212

13-
__version__ = "1.3.4"
13+
14+
__version__ = "1.3.5"
15+
1416
_dg2rad = math.radians(1.)
1517
_rad2dg = math.degrees(1.)
1618

‎packages/basemap/utils/GeosLibrary.py

+98-53
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import io
2222
import os
23+
import sys
2324
import ssl
2425
import glob
2526
import shutil
@@ -126,8 +127,7 @@ def extract(self, overwrite=True):
126127
if os.path.exists(zipfold):
127128
if not overwrite:
128129
raise OSError("folder '{0}' already exists".format(zipfold))
129-
else:
130-
shutil.rmtree(zipfold)
130+
shutil.rmtree(zipfold)
131131

132132
# Decompress zip file.
133133
with contextlib.closing(ZipFile(zippath, "r")) as fd:
@@ -137,16 +137,6 @@ def extract(self, overwrite=True):
137137
for path in sorted(glob.glob(os.path.join(zipfold, "tools", "*.sh"))):
138138
os.chmod(path, 0o755)
139139

140-
# Patch CMakeLists so that libgeos_c.so does not depend on libgeos.so.
141-
cmakefile = os.path.join(zipfold, "capi", "CMakeLists.txt")
142-
with io.open(cmakefile, "r", encoding="utf-8") as fd:
143-
lines = fd.readlines()
144-
with io.open(cmakefile, "wb") as fd:
145-
oldtext = "target_link_libraries(geos_c geos)"
146-
newtext = "target_link_libraries(geos_c geos-static)"
147-
for line in lines:
148-
fd.write(line.replace(oldtext, newtext).encode())
149-
150140
# Apply specific patches for GEOS < 3.6.0.
151141
if self.version_tuple < (3, 6, 0):
152142
# The SVN revision file is not created on the fly before 3.6.0.
@@ -166,66 +156,121 @@ def extract(self, overwrite=True):
166156
for line in lines:
167157
fd.write(line.replace(oldtext, newtext).encode())
168158

159+
# Apply specific patches for 3.6.0 <= GEOS < 3.7.0 on Windows.
160+
if (3, 6, 0) <= self.version_tuple < (3, 7, 0) and os.name == "nt":
161+
autogen_file = os.path.join(zipfold, "autogen.bat")
162+
subprocess.call([autogen_file], cwd=zipfold)
163+
cppfile = os.path.join(zipfold, "src", "geomgraph", "DirectedEdgeStar.cpp")
164+
with io.open(cppfile, "r", encoding="utf-8") as fd:
165+
lines = fd.readlines()
166+
with io.open(cppfile, "wb") as fd:
167+
oldtext = "DirectedEdgeStar::print() const"
168+
newtext = oldtext.replace(" const", "")
169+
for line in lines:
170+
fd.write(line.replace(oldtext, newtext).encode())
171+
hfile = os.path.join(zipfold, "include", "geos", "geomgraph", "DirectedEdgeStar.h")
172+
with io.open(hfile, "r", encoding="utf-8") as fd:
173+
lines = fd.readlines()
174+
with io.open(hfile, "wb") as fd:
175+
oldtext = "virtual std::string print() const;"
176+
newtext = oldtext.replace(" const", "")
177+
for line in lines:
178+
fd.write(line.replace(oldtext, newtext).encode())
179+
180+
# Patch CMakeLists to link shared geos_c with static geos.
181+
if self.version_tuple < (3, 8, 0):
182+
cmakefile = os.path.join(zipfold, "capi", "CMakeLists.txt")
183+
oldtext = "target_link_libraries(geos_c geos)"
184+
newtext = "target_link_libraries(geos_c geos-static)"
185+
else:
186+
cmakefile = os.path.join(zipfold, "CMakeLists.txt")
187+
oldtext = 'add_library(geos "")'
188+
newtext = 'add_library(geos STATIC "")'
189+
with io.open(cmakefile, "r", encoding="utf-8") as fd:
190+
lines = fd.readlines()
191+
with io.open(cmakefile, "wb") as fd:
192+
found_sharedline = False
193+
shared_oldtext = "if(BUILD_SHARED_LIBS)"
194+
shared_newtext = "if(FALSE)"
195+
for line in lines:
196+
if not found_sharedline and shared_oldtext in line:
197+
line = line.replace(shared_oldtext, shared_newtext)
198+
found_sharedline = True
199+
fd.write(line.replace(oldtext, newtext).encode())
200+
201+
# Patch doc CMakeLists in GEOS 3.8.x series.
202+
if (3, 8, 0) <= self.version_tuple < (3, 9, 0):
203+
cmakefile = os.path.join(zipfold, "doc", "CMakeLists.txt")
204+
oldtext1 = "target_include_directories(test_geos_unit\n"
205+
newtext1 = "if(BUILD_TESTING)\n {0}".format(oldtext1)
206+
oldtext2 = "$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}>)\n"
207+
newtext2 = "{0}endif()\n".format(oldtext2)
208+
with io.open(cmakefile, "r", encoding="utf-8") as fd:
209+
lines = fd.readlines()
210+
with io.open(cmakefile, "wb") as fd:
211+
for line in lines:
212+
line = line.replace(oldtext1, newtext1)
213+
line = line.replace(oldtext2, newtext2)
214+
fd.write(line.encode())
215+
169216
def build(self, installdir=None, njobs=1):
170217
"""Build and install GEOS from source."""
171218

172219
# Download and extract zip file if not present.
173220
zipfold = os.path.join(self.root, "geos-{0}".format(self.version))
174221
self.extract(overwrite=True)
222+
version = self.version_tuple
175223

176-
# Define build directory.
224+
# Define build and install directory.
177225
builddir = os.path.join(zipfold, "build")
178-
179-
# Define installation directory.
180226
if installdir is None:
181227
installdir = os.path.expanduser("~/.local/share/libgeos")
182228
installdir = os.path.abspath(installdir)
183229

184-
# Define configure options.
230+
# Define generic configure and build options.
185231
config_opts = [
186-
"-DCMAKE_INSTALL_PREFIX={0}".format(installdir),
187-
"-DGEOS_ENABLE_TESTS=OFF",
188232
"-DCMAKE_BUILD_TYPE=Release",
233+
"-DCMAKE_INSTALL_PREFIX={0}".format(installdir),
234+
"-D{0}=OFF".format("GEOS_ENABLE_TESTS" if version < (3, 8, 0)
235+
else "BUILD_TESTING")
189236
]
190-
if os.name == "nt" and self.version_tuple < (3, 6, 0):
191-
config_opts = ["-G", "NMake Makefiles"] + config_opts
192-
193-
# Define build options.
194-
build_env = os.environ.copy()
195237
build_opts = [
196238
"--config", "Release",
197239
"--target", "install",
198240
]
199-
if os.name != "nt":
200-
build_env["MAKEFLAGS"] = "-j {0:d}".format(njobs)
201-
elif self.version_tuple < (3, 6, 0):
202-
win64 = (8 * struct.calcsize("P") == 64)
203-
build_opts.extend([
204-
"--",
205-
"WIN64={0}".format("YES" if win64 else "NO"),
206-
"BUILD_BATCH={0}".format("YES" if njobs > 1 else "NO"),
207-
])
241+
build_env = os.environ.copy()
242+
243+
# Define custom configure and build options.
244+
if os.name == "nt":
245+
config_opts += ["-DCMAKE_CXX_FLAGS='/wd4251 /wd4458 /wd4530'"]
246+
if version >= (3, 6, 0) and sys.version_info[:2] >= (3, 3):
247+
build_opts = ["-j", "{0:d}".format(njobs)] + build_opts
248+
else:
249+
win64 = (8 * struct.calcsize("P") == 64)
250+
config_opts = ["-G", "NMake Makefiles"] + config_opts
251+
build_opts.extend([
252+
"--",
253+
"WIN64={0}".format("YES" if win64 else "NO"),
254+
"BUILD_BATCH={0}".format("YES" if njobs > 1 else "NO"),
255+
])
256+
if sys.version_info[:2] < (3, 3):
257+
build_opts += ["MSVC_VER=1500"]
208258
else:
209-
build_opts = ["-j", "{0:d}".format(njobs)] + build_opts
259+
build_env["MAKEFLAGS"] = "-j {0:d}".format(njobs)
260+
if version >= (3, 7, 0):
261+
config_opts += ["-DCMAKE_CXX_FLAGS='-fPIC'"]
210262

211-
# Now move to the GEOS source code folder and build with CMake.
212-
cwd = os.getcwd()
263+
# Call cmake configure after ensuring that the build directory exists.
213264
try:
214-
# Ensure that the build directory exists.
215-
try:
216-
os.makedirs(builddir)
217-
except OSError:
218-
pass
219-
os.chdir(builddir)
220-
# Call cmake configure.
221-
subprocess.call(["cmake", ".."] + config_opts)
222-
# Ensure that the install directory exists.
223-
try:
224-
os.makedirs(installdir)
225-
except OSError:
226-
pass
227-
# Call cmake build and install.
228-
subprocess.call(["cmake", "--build", "."] + build_opts,
229-
env=build_env)
230-
finally:
231-
os.chdir(cwd)
265+
os.makedirs(builddir)
266+
except OSError:
267+
pass
268+
subprocess.call(["cmake", ".."] + config_opts, cwd=builddir)
269+
270+
# Call cmake build after ensuring that the install directory exists.
271+
try:
272+
os.makedirs(installdir)
273+
except OSError:
274+
pass
275+
subprocess.call(["cmake", "--build", "."] + build_opts,
276+
cwd=builddir, env=build_env)

0 commit comments

Comments
 (0)
Please sign in to comment.