SciTools / cartopy

Cartopy - a cartographic python library with matplotlib support
https://scitools.org.uk/cartopy/docs/latest
BSD 3-Clause "New" or "Revised" License
1.4k stars 359 forks source link

Cannot make contour plot together with coastlines #1673

Closed Chilipp closed 3 years ago

Chilipp commented 3 years ago

Description

Hello lovely cartopy developers!

Thanks a lot for your great work here!

For some reasons, using Geoaxes.contour and Geoaxes.coastlines together fails with AttributeError: 'list' object has no attribute 'xy' (within shapely though, but I suppose the error lies within cartopy). Excluding either the ax.contour call or ax.coastlines call down below, or adding ax.set_global() before the plt.savefig call works. So it seems like cartopy is unable to determine the extent with the two polygons plotted on the GeoAxes.

Code to reproduce

Take the file at psyplot/psy-maps/tests/test-t2m-u-v.nc for instance.

import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import xarray as xr
ds = xr.open_dataset('test-t2m-u-v.nc')
ax = plt.axes(projection=ccrs.Orthographic())
ax.coastlines()
ax.contour(ds.lon.values, ds.lat.values, ds.t2m[0, 0].values, transform=ccrs.PlateCarree())
plt.savefig("test.pdf")

Traceback

Traceback (most recent call last):
  File "error.py", line 9, in <module>
    plt.savefig("test.pdf")
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/pyplot.py", line 859, in savefig
    res = fig.savefig(*args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/figure.py", line 2311, in savefig
    self.canvas.print_figure(fname, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/backend_bases.py", line 2210, in print_figure
    result = print_method(
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/backend_bases.py", line 1639, in wrapper
    return func(*args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/backends/backend_pdf.py", line 2593, in print_pdf
    self.figure.draw(renderer)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/figure.py", line 1863, in draw
    mimage._draw_list_compositing_images(
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/mpl/geoaxes.py", line 479, in draw
    return matplotlib.axes.Axes.draw(self, renderer=renderer, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/cbook/deprecation.py", line 411, in wrapper
    return func(*inner_args, **inner_kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/axes/_base.py", line 2747, in draw
    mimage._draw_list_compositing_images(renderer, self, artists)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/image.py", line 131, in _draw_list_compositing_images
    a.draw(renderer)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/matplotlib/artist.py", line 41, in draw_wrapper
    return draw(artist, renderer, *args, **kwargs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/mpl/feature_artist.py", line 152, in draw
    extent = ax.get_extent(feature_crs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/mpl/geoaxes.py", line 730, in get_extent
    p = self._get_extent_geom(crs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/mpl/geoaxes.py", line 775, in _get_extent_geom
    geom_in_crs = proj.project_geometry(geom_in_src_proj,
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/crs.py", line 218, in project_geometry
    return getattr(self, method_name)(geometry, src_crs)
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/cartopy/crs.py", line 354, in _project_polygon
    is_ccw = polygon.exterior.is_ccw
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/shapely/geometry/polygon.py", line 88, in is_ccw
    return bool(self.impl['is_ccw'](self))
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/shapely/algorithms/cga.py", line 14, in is_ccw_op
    return signed_area(ring) >= 0.0
  File "/home/psommer/miniconda3/envs/test/lib/python3.8/site-packages/shapely/algorithms/cga.py", line 6, in signed_area
    xs, ys = ring.coords.xy
AttributeError: 'list' object has no attribute 'xy'
Full environment definition I created a fresh conda environment via ```bash conda create -c conda-forge --override-channels cartopy scipy matplotlib xarray netcdf4 -n test ``` ### Operating system Linux ### Cartopy version `conda-forge/linux-64::cartopy-0.18.0-py38h88488af_4` ### conda list ``` # packages in environment at /home/psommer/miniconda3/envs/test: # # Name Version Build Channel _libgcc_mutex 0.1 conda_forge conda-forge _openmp_mutex 4.5 1_gnu conda-forge bzip2 1.0.8 h516909a_3 conda-forge c-ares 1.16.1 h516909a_3 conda-forge ca-certificates 2020.6.20 hecda079_0 conda-forge cartopy 0.18.0 py38h88488af_4 conda-forge certifi 2020.6.20 py38h924ce5b_2 conda-forge cftime 1.2.1 py38hab2c0dc_1 conda-forge curl 7.71.1 he644dc0_8 conda-forge cycler 0.10.0 py_2 conda-forge freetype 2.10.4 he06d7ca_0 conda-forge geos 3.8.1 he1b5a44_0 conda-forge hdf4 4.2.13 hf30be14_1003 conda-forge hdf5 1.10.6 nompi_h1022a3e_1110 conda-forge jpeg 9d h516909a_0 conda-forge kiwisolver 1.3.0 py38hbf85e49_0 conda-forge krb5 1.17.1 hfafb76e_3 conda-forge lcms2 2.11 hbd6801e_0 conda-forge ld_impl_linux-64 2.35 h769bd43_9 conda-forge libblas 3.9.0 2_openblas conda-forge libcblas 3.9.0 2_openblas conda-forge libcurl 7.71.1 hcdd3856_8 conda-forge libedit 3.1.20191231 he28a2e2_2 conda-forge libev 4.33 h516909a_1 conda-forge libffi 3.2.1 he1b5a44_1007 conda-forge libgcc-ng 9.3.0 h5dbcf3e_17 conda-forge libgfortran-ng 9.3.0 he4bcb1c_17 conda-forge libgfortran5 9.3.0 he4bcb1c_17 conda-forge libgomp 9.3.0 h5dbcf3e_17 conda-forge liblapack 3.9.0 2_openblas conda-forge libnetcdf 4.7.4 nompi_hefab0ff_106 conda-forge libnghttp2 1.41.0 h8cfc5f6_2 conda-forge libopenblas 0.3.12 pthreads_h4812303_1 conda-forge libpng 1.6.37 hed695b0_2 conda-forge libssh2 1.9.0 hab1572f_5 conda-forge libstdcxx-ng 9.3.0 h2ae2ef3_17 conda-forge libtiff 4.1.0 hc7e4089_6 conda-forge libwebp-base 1.1.0 h516909a_3 conda-forge lz4-c 1.9.2 he1b5a44_3 conda-forge matplotlib 3.3.2 0 conda-forge matplotlib-base 3.3.2 py38h4d1ce4f_1 conda-forge ncurses 6.2 he1b5a44_2 conda-forge netcdf4 1.5.4 nompi_py38hec8b9af_103 conda-forge numpy 1.19.2 py38hf89b668_1 conda-forge olefile 0.46 pyh9f0ad1d_1 conda-forge openssl 1.1.1h h516909a_0 conda-forge pandas 1.1.3 py38hddd6c8b_2 conda-forge pillow 8.0.1 py38h9776b28_0 conda-forge pip 20.2.4 py_0 conda-forge proj 7.1.1 h966b41f_3 conda-forge pyparsing 2.4.7 pyh9f0ad1d_0 conda-forge pyshp 2.1.2 pyh9f0ad1d_0 conda-forge python 3.8.6 h852b56e_0_cpython conda-forge python-dateutil 2.8.1 py_0 conda-forge python_abi 3.8 1_cp38 conda-forge pytz 2020.1 pyh9f0ad1d_0 conda-forge readline 8.0 he28a2e2_2 conda-forge scipy 1.5.2 py38hd9480d8_2 conda-forge setuptools 49.6.0 py38h924ce5b_2 conda-forge shapely 1.7.1 py38hc7361b7_1 conda-forge six 1.15.0 pyh9f0ad1d_0 conda-forge sqlite 3.33.0 h4cf870e_1 conda-forge tk 8.6.10 hed695b0_1 conda-forge tornado 6.0.4 py38h1e0a361_2 conda-forge wheel 0.35.1 pyh9f0ad1d_0 conda-forge xarray 0.16.1 py_0 conda-forge xz 5.2.5 h516909a_1 conda-forge zlib 1.2.11 h516909a_1010 conda-forge zstd 1.4.5 h6597ccf_2 conda-forge ``` ### pip list ``` Package Version --------------- ------------------- Cartopy 0.18.0 certifi 2020.6.20 cftime 1.2.1 cycler 0.10.0 kiwisolver 1.3.0 matplotlib 3.3.2 netCDF4 1.5.4 numpy 1.19.2 olefile 0.46 pandas 1.1.3 Pillow 8.0.1 pip 20.2.4 pyparsing 2.4.7 pyshp 2.1.2 python-dateutil 2.8.1 pytz 2020.1 scipy 1.5.2 setuptools 49.6.0.post20201009 Shapely 1.7.1 six 1.15.0 tornado 6.0.4 wheel 0.35.1 xarray 0.16.1 ```
raphaelquast commented 3 years ago

i run into the very same error-message while i was trying to call ax.add_collection on a geo-axes.

I found that manually setting x- and y- limits (e.g. ax.set_xlim(x0, x1) and ax.set_ylim(y0,y1) prior to any plot-call somehow somehow solved it for me.

funkpreacher commented 3 years ago

i run into the very same error-message while i was trying to call ax.add_collection on a geo-axes.

I found that manually setting x- and y- limits (e.g. ax.set_xlim(x0, x1) and ax.set_ylim(y0,y1) prior to any plot-call somehow somehow solved it for me.

Same issue. This workaround is fixing it. It happened with contour plots on a map with country borders on and only sometimes depending on the data. I am suspecting its due to contour polygons being outside of the plotting area of the country borders. I am wondering if this is a bug in cartopy or shapely, or if it is a usage error.

robrnelson commented 3 years ago

Hi all,

Same issue! Definitely seems like a bug. Setting the xlim/ylim didn't fix it for me. Here's simple code for reproduction:

import cartopy.crs as ccrs
import matplotlib.pyplot as plt

W,E,S,N = 176.7, 178.2, -38.8, -37.3

fig = plt.figure(figsize=(20,20))
ax1 = plt.axes(projection=ccrs.epsg(3857))
ax1.set_extent([W,E,S,N], crs=ccrs.PlateCarree())
ax1.coastlines(resolution="10m",linewidth=1.0)

Making "W" (west) slightly smaller (e.g. 176.2) prevents the error. Or changing the projection.

Conda packages are up-to-date:

matplotlib 3.3.2 h06a4308_0 cartopy 0.18.0 py38h9b98833_8 conda-forge