diff --git a/CHANGELOG.md b/CHANGELOG.md index 15a3918fb..0f8acf943 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,33 @@ https://keepachangelog.com/en/1.0.0/ https://semver.org/spec/v2.0.0.html +## [1.3.9] - 2023-12-26 + +### Fixed +- Fix `GeosLibrary` wrapper to also work with CMake >= 3.27.0 and + Python 2.7 on Windows by adding '/MANIFEST:NO' to override the new + default '/MANIFEST:EMBED,ID=2' provided to linker. +- Fix broken `Proj.__call__` when the input arguments are provided as + a combined single array. +- Fix flipped coastlines with pseudocylindrical projections when `lon_0` + is greater than 0 deg (solves issues [#443] and [#463], thanks to + @YilongWang). +- Fix `antialiased` argument being ignored in `Basemap.drawcounties` and + `Basemap.readshapefile` (solves issue [#501], thanks to @TheFizzWare). +- Fix `BaseGeometry.intersection` in `_geoslib` so that it also works + with `GEOS_GEOMETRYCOLLECTION` objects returned by `GEOSIntersection` + (solves issue [#566], where country boundaries are missing due to this + bug, thanks to @guidocioni). +- Fix bug with elliptical maps causing warped images (Blue Marble, + ETOPO, Shaded Relief) to be shown behind the map background when the + map boundary is not initialised manually (solves issue [#577], thanks + to @YilongWang). +- Fix references to removed `numpy.float` alias (solves issue [#589], + thanks to @quickbrett). +- Fix wrong reference to `ireland.py` example in FAQ, which should be + `hires.py` instead, and fix wrong use of locals and invalid syntax + in this example (solves issue [#592], thanks to @timcoote). + ## [1.3.8] - 2023-08-18 ### Changed @@ -976,6 +1003,10 @@ https://semver.org/spec/v2.0.0.html - Fix glitches in drawing of parallels and meridians. +[#592]: +https://github.com/matplotlib/basemap/issues/592 +[#589]: +https://github.com/matplotlib/basemap/issues/589 [#583]: https://github.com/matplotlib/basemap/issues/583 [#582]: @@ -986,8 +1017,12 @@ https://github.com/matplotlib/basemap/issues/581 https://github.com/matplotlib/basemap/pull/580 [#579]: https://github.com/matplotlib/basemap/issues/579 +[#577]: +https://github.com/matplotlib/basemap/issues/577 [#573]: https://github.com/matplotlib/basemap/issues/573 +[#566]: +https://github.com/matplotlib/basemap/issues/566 [#564]: https://github.com/matplotlib/basemap/pull/564 [#563]: @@ -1046,6 +1081,8 @@ https://github.com/matplotlib/basemap/issues/512 https://github.com/matplotlib/basemap/issues/510 [#505]: https://github.com/matplotlib/basemap/pull/505 +[#501]: +https://github.com/matplotlib/basemap/issues/501 [#491]: https://github.com/matplotlib/basemap/issues/491 [#489]: @@ -1056,12 +1093,16 @@ https://github.com/matplotlib/basemap/issues/488 https://github.com/matplotlib/basemap/issues/487 [#476]: https://github.com/matplotlib/basemap/pull/476 +[#463]: +https://github.com/matplotlib/basemap/issues/463 [#461]: https://github.com/matplotlib/basemap/issues/461 [#456]: https://github.com/matplotlib/basemap/issues/456 [#445]: https://github.com/matplotlib/basemap/issues/445 +[#443]: +https://github.com/matplotlib/basemap/issues/443 [#436]: https://github.com/matplotlib/basemap/issues/436 [#422]: @@ -1080,7 +1121,9 @@ https://github.com/matplotlib/basemap/issues/228 https://github.com/matplotlib/basemap/issues/179 [Unreleased]: -https://github.com/matplotlib/basemap/compare/v1.3.8...develop +https://github.com/matplotlib/basemap/compare/v1.3.9...develop +[1.3.9]: +https://github.com/matplotlib/basemap/compare/v1.3.8...v1.3.9 [1.3.8]: https://github.com/matplotlib/basemap/compare/v1.3.7...v1.3.8 [1.3.7]: diff --git a/FAQ b/FAQ index cd2c11cdd..9ee747efe 100644 --- a/FAQ +++ b/FAQ @@ -1,33 +1,33 @@ -Q: It takes a long time to make a plot with the 'intermediate' or 'high' +Q: It takes a long time to make a plot with the 'intermediate' or 'high' resolution coastlines, how can I speed it up? -A: There is a overhead in processing boundary datasets when a Basemap -class in created, and this overhead can be significant for the higher -resolution boundaries. If you are makeing many maps for the same region, -you only need to create you Basemap class instance once, then re-use it -for each plot. If the plots are being created by different scripts, you -can save the Basemap class instance to a Pickle on disk, then read it in -whatever script needs it (it's much faster to read a pickle from disk than -it is to create the Basemap instance originally). The ireland.py example +A: There is a overhead in processing boundary datasets when a Basemap +class in created, and this overhead can be significant for the higher +resolution boundaries. If you are makeing many maps for the same region, +you only need to create you Basemap class instance once, then re-use it +for each plot. If the plots are being created by different scripts, you +can save the Basemap class instance to a Pickle on disk, then read it in +whatever script needs it (it's much faster to read a pickle from disk than +it is to create the Basemap instance originally). The hires.py example illustrates how to do this. -Q: I have my own boundary dataset that I would like to use, how do I use +Q: I have my own boundary dataset that I would like to use, how do I use it in place of (or in addition to) the built-in basemap boundary datasets? -A: If your dataset is in ESRI shapefile format, this is relatively easy. -Just create your Basemap class instance, then call the 'readshapefile' -method on that instance to import your data. Setting 'drawbounds=True' -will draw the boundaries in the shapefile. The fillstates.py example +A: If your dataset is in ESRI shapefile format, this is relatively easy. +Just create your Basemap class instance, then call the 'readshapefile' +method on that instance to import your data. Setting 'drawbounds=True' +will draw the boundaries in the shapefile. The fillstates.py example shows how to do this. -Q: How do I specify the map projection region if I don't know what the +Q: How do I specify the map projection region if I don't know what the latitude and longitudes of the corners are? -A: As an alternative to specifying the lat/lon values for the upper-right -and lower-left corners of the projection domain (using the llcrnrlat, -llcrnrlon, urcrnrlat and urcrnrlon keywords) you can specify the center of -the map projection domain (using the lat_0 and lon_0 keywords) and the -width and height of the domain in map projection coordinates (meters) -using the width and height keywords. Basemap will then calculate the -corresponging values of llcrnrlat, llcrnrlon, urcrnrlat and urcrnrlon. +A: As an alternative to specifying the lat/lon values for the upper-right +and lower-left corners of the projection domain (using the llcrnrlat, +llcrnrlon, urcrnrlat and urcrnrlon keywords) you can specify the center of +the map projection domain (using the lat_0 and lon_0 keywords) and the +width and height of the domain in map projection coordinates (meters) +using the width and height keywords. Basemap will then calculate the +corresponging values of llcrnrlat, llcrnrlon, urcrnrlat and urcrnrlon. Examples of this are given in the garp.py and setwh.py examples. diff --git a/examples/hires.py b/examples/hires.py index 32ba3d7f4..3a388068f 100644 --- a/examples/hires.py +++ b/examples/hires.py @@ -1,49 +1,51 @@ -from __future__ import (absolute_import, division, print_function) +from __future__ import print_function + +import time +import pickle -from mpl_toolkits.basemap import Basemap import numpy as np import matplotlib.pyplot as plt -import pickle, time +from mpl_toolkits.basemap import Basemap + -# create figure with aqua background (will be oceans) +# Create figure. fig = plt.figure() -# create Basemap instance. Use 'high' resolution coastlines. -t1 = time.clock() -#m = Basemap(llcrnrlon=-10.5,llcrnrlat=49.5,urcrnrlon=3.5,urcrnrlat=59.5, -# resolution='h',projection='tmerc',lon_0=-4,lat_0=0) -m = Basemap(width=920000,height=1100000, - resolution='f',projection='tmerc',lon_0=-4.2,lat_0=54.6) -# make sure countries and rivers are loaded -m.drawcountries() -m.drawrivers() -print(time.clock()-t1,' secs to create original Basemap instance') - -# pickle the class instance. -pickle.dump(m,open('map.pickle','wb'),-1) - -# clear the figure +# Create Basemap instance: +# - Use 'full' resolution coastlines. +# - Make sure that countries and rivers are loaded. +t0 = time.time() +bmap1 = Basemap(width=920000, height=1100000, resolution="f", + projection="tmerc", lon_0=-4.2, lat_0=54.6) +bmap1.drawcountries() +bmap1.drawrivers() +t1 = time.time() +print("{0:.3f} secs to plot with a Basemap instance created at runtime".format(t1 - t0)) + +# Clear the figure. plt.clf() -# read pickle back in and plot it again (should be much faster). -t1 = time.clock() -m2 = pickle.load(open('map.pickle','rb')) -# draw coastlines and fill continents. -m.drawcoastlines() -# fill continents and lakes -m.fillcontinents(color='coral',lake_color='aqua') -# draw political boundaries. -m.drawcountries(linewidth=1) -# fill map projection region light blue (this will -# paint ocean areas same color as lakes). -m.drawmapboundary(fill_color='aqua') -# draw major rivers. -m.drawrivers(color='b') -print(time.clock()-t1,' secs to plot using using a pickled Basemap instance') -# draw parallels -circles = np.arange(48,65,2).tolist() -m.drawparallels(circles,labels=[1,1,0,0]) -# draw meridians -meridians = np.arange(-12,13,2) -m.drawmeridians(meridians,labels=[0,0,1,1]) -plt.title("High-Res British Isles",y=1.04) + +# Pickle the class instance. +with open("map.pickle", "wb") as fd: + pickle.dump(bmap1, fd, protocol=-1) + +# Read pickle back in and plot it again (should be much faster): +# - Draw coastlines and fill continents and lakes. +# - Draw political boundaries and rivers. +# - Draw parallels and meridians. +# - Draw map boundary and fill map background. +t0 = time.time() +with open("map.pickle", "rb") as fd: + bmap2 = pickle.load(fd) +bmap2.drawcoastlines() +bmap2.fillcontinents(color="coral", lake_color="aqua") +bmap2.drawcountries(linewidth=1) +bmap2.drawrivers(color="b") +bmap2.drawparallels(np.arange(48, 65, 2), labels=[1, 1, 0, 0]) +bmap2.drawmeridians(np.arange(-12, 13, 2), labels=[0, 0, 1, 1]) +bmap2.drawmapboundary(fill_color="aqua") +t1 = time.time() +print("{0:.3f} secs to plot with a pickled Basemap instance".format(t1 - t0)) + +plt.title("High-Res British Isles", y=1.04) plt.show() diff --git a/packages/basemap/README.md b/packages/basemap/README.md index 393bde003..cfe0f6bf0 100644 --- a/packages/basemap/README.md +++ b/packages/basemap/README.md @@ -45,6 +45,6 @@ https://spdx.org/licenses/LGPL-2.1-only.html https://spdx.org/licenses/MIT.html [`LICENSE`]: -https://github.com/matplotlib/basemap/blob/v1.3.8/packages/basemap/LICENSE +https://github.com/matplotlib/basemap/blob/v1.3.9/packages/basemap/LICENSE [`LICENSE.geos`]: -https://github.com/matplotlib/basemap/blob/v1.3.8/packages/basemap/LICENSE.geos +https://github.com/matplotlib/basemap/blob/v1.3.9/packages/basemap/LICENSE.geos diff --git a/packages/basemap/doc/users/figures/plotwindvec.py b/packages/basemap/doc/users/figures/plotwindvec.py index d048454f3..de0797617 100644 --- a/packages/basemap/doc/users/figures/plotwindvec.py +++ b/packages/basemap/doc/users/figures/plotwindvec.py @@ -21,7 +21,7 @@ uin = data.variables['U-component_of_wind_height_above_ground'][:].squeeze() vin = data.variables['V-component_of_wind_height_above_ground'][:].squeeze() # add cyclic points manually (could use addcyclic function) -slp = np.zeros((slpin.shape[0],slpin.shape[1]+1),np.float) +slp = np.zeros((slpin.shape[0],slpin.shape[1]+1),np.float64) slp[:,0:-1] = slpin[::-1]; slp[:,-1] = slpin[::-1,0] u = np.zeros((uin.shape[0],uin.shape[1]+1),np.float64) u[:,0:-1] = uin[::-1]; u[:,-1] = uin[::-1,0] diff --git a/packages/basemap/setup.py b/packages/basemap/setup.py index c4f998f77..a47f15e21 100644 --- a/packages/basemap/setup.py +++ b/packages/basemap/setup.py @@ -172,7 +172,7 @@ def run(self): "name": "basemap", "version": - "1.3.8", + "1.3.9", "license": "MIT", "description": diff --git a/packages/basemap/src/_geoslib.pyx b/packages/basemap/src/_geoslib.pyx index 7ef440b4c..a6d819a0e 100644 --- a/packages/basemap/src/_geoslib.pyx +++ b/packages/basemap/src/_geoslib.pyx @@ -307,6 +307,30 @@ cdef class BaseGeometry: b = _get_coords(gout) p = LineString(b) pout.append(p) + elif typeid == GEOS_GEOMETRYCOLLECTION: + numgeoms = GEOSGetNumGeometries(g3) + pout = [] + for i from 0 <= i < numgeoms: + gout = GEOSGetGeometryN(g3, i) + typeid = GEOSGeomTypeId(gout) + if typeid == GEOS_POLYGON: + b = _get_coords(gout) + p = Polygon(b) + pout.append(p) + elif typeid == GEOS_LINESTRING: + b = _get_coords(gout) + p = LineString(b) + pout.append(p) + else: + # More cases might need to be handled here: + # - GEOS_MULTILINESTRING + # - GEOS_MULTIPOLYGON + # - GEOS_GEOMETRYCOLLECTION + # The underlying problem is the need of a generic + # converter from GEOSGeom pointers to `_geoslib` + # objects, since postprocessing `GeometryCollections` + # might need recursiveness. + pass else: #type = PyBytes_FromString(GEOSGeomType(g3)) #raise NotImplementedError("intersections of type '%s' not yet implemented" % (type)) diff --git a/packages/basemap/src/mpl_toolkits/basemap/__init__.py b/packages/basemap/src/mpl_toolkits/basemap/__init__.py index 8e2715cc7..2ebf3a27d 100644 --- a/packages/basemap/src/mpl_toolkits/basemap/__init__.py +++ b/packages/basemap/src/mpl_toolkits/basemap/__init__.py @@ -54,7 +54,7 @@ import functools -__version__ = "1.3.8" +__version__ = "1.3.9" # basemap data files now installed in lib/matplotlib/toolkits/basemap/data # check to see if environment variable BASEMAPDATA set to a directory, @@ -1990,7 +1990,7 @@ def drawcounties(self,linewidth=0.1,linestyle='solid',color='k',antialiased=1, county_info = self.readshapefile(gis_file,'counties',\ default_encoding='latin-1',drawbounds=drawbounds) counties = [coords for coords in self.counties] - counties = PolyCollection(counties) + counties = PolyCollection(counties, antialiaseds=(antialiased,)) counties.set_linestyle(linestyle) counties.set_linewidth(linewidth) counties.set_edgecolor(color) @@ -2203,7 +2203,7 @@ def readshapefile(self,shapefile,name,drawbounds=True,zorder=None, # get current axes instance (if none specified). ax = ax or self._check_ax() # make LineCollections for each polygon. - lines = LineCollection(coords,antialiaseds=(1,)) + lines = LineCollection(coords,antialiaseds=(antialiased,)) lines.set_color(color) lines.set_linewidth(linewidth) lines.set_label('_nolabel_') @@ -3203,7 +3203,7 @@ def set_axes_limits(self,ax=None): # first draw boundary, no fill limb1 = self.drawmapboundary(fill_color='none', ax=ax) # draw another filled patch, with no boundary. - limb2 = self.drawmapboundary(linewidth=0, ax=ax) + limb2 = self.drawmapboundary(fill_color='none', linewidth=0, ax=ax) self._mapboundarydrawn = limb2 # for elliptical map, always turn off axis_frame. if ((self.projection in ['ortho', 'geos', 'nsper', 'aeqd'] and @@ -4181,9 +4181,9 @@ def warpimage(self,image="bluemarble",scale=None,**kwargs): lonsr,latsr = self(x,y,inverse=True) mask = ma.zeros((ny,nx,4),np.int8) lon_0 = self.projparams['lon_0'] - lonright = lon_0+180. - lonleft = lon_0-180. - x1 = np.array(ny*[0.5*(self.xmax + self.xmin)],np.float) + lonright = lon_0 + 180. - 1E-10 + lonleft = lon_0 - 180. + 1E-10 + x1 = np.array(ny*[0.5*(self.xmax + self.xmin)],np.float64) y1 = np.linspace(self.ymin, self.ymax, ny) lons1, lats1 = self(x1,y1,inverse=True) lats1 = np.where(lats1 < -89.999999, -89.999999, lats1) diff --git a/packages/basemap/src/mpl_toolkits/basemap/proj.py b/packages/basemap/src/mpl_toolkits/basemap/proj.py index 9c0692e87..32e83a228 100644 --- a/packages/basemap/src/mpl_toolkits/basemap/proj.py +++ b/packages/basemap/src/mpl_toolkits/basemap/proj.py @@ -11,7 +11,7 @@ from matplotlib.cbook import dedent -__version__ = "1.3.8" +__version__ = "1.3.9" _dg2rad = math.radians(1.) _rad2dg = math.degrees(1.) @@ -195,8 +195,8 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat, raise ValueError(_lower_left_out_of_bounds) elif self.projection in _pseudocyl: self._proj4 = pyproj.Proj(projparams) - xtmp,urcrnry = self(projparams['lon_0'],90.) - urcrnrx,xtmp = self(projparams['lon_0']+180.,0) + xtmp, urcrnry = self(projparams['lon_0'], 90.) + urcrnrx, xtmp = self(projparams['lon_0'] + 180. - 1E-10, 0) llcrnrx = -urcrnrx llcrnry = -urcrnry if self.ellipsoid and self.projection in ['kav7','eck4','mbtfpq']: @@ -236,8 +236,8 @@ def __init__(self,projparams,llcrnrlon,llcrnrlat, if urcrnrx > 1.e20 or urcrnry > 1.e20: raise ValueError(_upper_right_out_of_bounds) elif self.projection in _pseudocyl: - xtmp,urcrnry = self(projparams['lon_0'],90.) - urcrnrx,xtmp = self(projparams['lon_0']+180.,0) + xtmp, urcrnry = self(projparams['lon_0'], 90.) + urcrnrx, xtmp = self(projparams['lon_0'] + 180. - 1E-10, 0) else: urcrnrx = urcrnrlon urcrnry = urcrnrlat @@ -290,7 +290,8 @@ def __call__(self, *args, **kw): return x,y inverse = kw.get('inverse', False) if onearray: - outxy = self._proj4(xy, inverse=inverse) + outx, outy = self._proj4(*xy.T, inverse=inverse) + outxy = np.asarray([outx, outy]).T else: outx,outy = self._proj4(x, y, inverse=inverse) if inverse: diff --git a/packages/basemap/src/mpl_toolkits/basemap/test.py b/packages/basemap/src/mpl_toolkits/basemap/test.py index c7325a1ff..fe340b422 100644 --- a/packages/basemap/src/mpl_toolkits/basemap/test.py +++ b/packages/basemap/src/mpl_toolkits/basemap/test.py @@ -60,35 +60,35 @@ class TestShiftGrid(TestCase): def make_data_cyc(self): loncyc = np.array([0, 30, 60, 90, 120, 150, 180, 210, 240, 270, 300,\ - 330, 360],dtype=np.float) + 330, 360],dtype=np.float64) gridcyc = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,\ - 11, 0]],dtype=np.float) + 11, 0]],dtype=np.float64) lonoutcyc = np.array([-180, -150, -120, -90, -60, -30, 0, 30,60,90,\ - 120, 150, 180],dtype=np.float) + 120, 150, 180],dtype=np.float64) gridoutcyc = np.array([[ 6, 7, 8, 9, 10, 11, 0, 1, 2,3,\ - 4, 5, 6]],dtype=np.float) + 4, 5, 6]],dtype=np.float64) return loncyc, gridcyc, lonoutcyc, gridoutcyc def make_data_nocyc(self): lonnocyc = np.array([0, 30, 60, 90, 120, 150, 180, 210, 240, 270,\ - 300, 330],dtype=np.float) + 300, 330],dtype=np.float64) gridnocyc = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11]],dtype=np.float) + 10, 11]],dtype=np.float64) lonoutnocyc = np.array([-180, -150, -120, -90, -60, -30, 0, 30, 60,\ - 90, 120, 150],dtype=np.float) + 90, 120, 150],dtype=np.float64) gridoutnocyc = np.array([[ 6, 7, 8, 9, 10, 11, 0, 1, 2,\ - 3, 4, 5]],dtype=np.float) + 3, 4, 5]],dtype=np.float64) return lonnocyc, gridnocyc, lonoutnocyc, gridoutnocyc def make_data_nocyc2(self): lonnocyc2 = np.array([15, 45, 75, 105, 135, 165, 195, 225, 255, 285,\ - 315, 345],dtype=np.float) + 315, 345],dtype=np.float64) gridnocyc2 = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9,\ - 10, 11]],dtype=np.float) + 10, 11]],dtype=np.float64) lonoutnocyc2 = np.array([-165, -135, -105, -75, -45, -15, 15,45,75,\ - 105, 135, 165],dtype=np.float) + 105, 135, 165],dtype=np.float64) gridoutnocyc2 = np.array([[ 6, 7, 8, 9, 10, 11, 0, 1, 2,\ - 3, 4, 5]],dtype=np.float) + 3, 4, 5]],dtype=np.float64) return lonnocyc2, gridnocyc2, lonoutnocyc2, gridoutnocyc2 def test_cyc(self): diff --git a/packages/basemap/utils/GeosLibrary.py b/packages/basemap/utils/GeosLibrary.py index abbb839c7..8da530e5f 100644 --- a/packages/basemap/utils/GeosLibrary.py +++ b/packages/basemap/utils/GeosLibrary.py @@ -250,6 +250,8 @@ def build(self, installdir=None, toolset=None, njobs=1): else: win64 = (8 * struct.calcsize("P") == 64) config_opts = ["-G", "NMake Makefiles"] + config_opts + config_opts += ["-DCMAKE_EXE_LINKER_FLAGS='/MANIFEST:NO'"] + config_opts += ["-DCMAKE_SHARED_LINKER_FLAGS='/MANIFEST:NO'"] build_opts.extend([ "--", "WIN64={0}".format("YES" if win64 else "NO"),