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.43k stars 365 forks source link

TypeError: 'GeometryCollection' object is not subscriptable (cartopy calling shapely) #2176

Open durack1 opened 1 year ago

durack1 commented 1 year ago

Description

I have hit a repeatable, but confusing error with ~20 timesteps in a long ~1800 timestep global array. The error appears to be sensitive to the levels specified in the optional levels argument, and is reproduced below. It triggers a GeometryCollection error in the shapely library call (see traceback below).

I have included a working example below, which loops through two timesteps, the first works great plotting cleanly, and the second bombs, triggering the GeometryCollection bug. The error continues on subsequent timesteps, after ~20 or so, it starts to work again.

I would appreciate any pointers to resolving this issue, or tips at identifying a workaround to get me plotting through the problem ~20 timesteps.

Code to reproduce

# %% imports
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import numpy as np
from xarray import open_dataset

# %% load dataset
f = "cartopyLevelBugTwoTimesteps.nc"  # (file will be dropped below)
ds = open_dataset(f)
da = ds.siconc

# %% setup plot
levs1 = list(np.arange(-20, 121, 10))  # siconc
centralLon = 202

# %% step through working and failing timesteps
for time in np.arange(0, 2):
    print("Step:", time)
    ar = da.isel(time=time)
    fig = plt.figure(figsize=(10, 15))

    # Start subplots
    ax1 = fig.add_subplot(3, 1, 1,
                          projection=ccrs.Robinson(
                              central_longitude=centralLon,
                              globe=None,
                              false_easting=None,
                              false_northing=None,
                          ),
                          )
    cs1 = ax1.contourf(da.lon.data, da.lat.data, ar.data,
                       levs1,
                       transform=ccrs.PlateCarree(),
                       cmap="RdBu_r"
                       )
    plt.show()
    # time = 1; Should bomb with "TypeError: 'GeometryCollection' object is not subscriptable"

Data to reproduce

cartopyLevelBugTwoTimesteps.nc.gz

Traceback

Traceback (most recent call last):
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/spyder_kernels/py3compat.py:356 in compat_exec
    exec(code, globals, locals)
  File ~/git/amipbcs/cartopyBug.py:41
    cs1 = ax1.contourf(da.lon.data, da.lat.data, ar.data,
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:318 in wrapper
    return func(self, *args, **kwargs)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:362 in wrapper
    return func(self, *args, **kwargs)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:1665 in contourf
    bboxes = [col.get_datalim(self.transData)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:1665 in <listcomp>
    bboxes = [col.get_datalim(self.transData)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/matplotlib/collections.py:266 in get_datalim
    paths = [transform.transform_path_non_affine(p) for p in paths]
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/matplotlib/collections.py:266 in <listcomp>
    paths = [transform.transform_path_non_affine(p) for p in paths]
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/matplotlib/transforms.py:2431 in transform_path_non_affine
    return self._a.transform_path_non_affine(path)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/mpl/geoaxes.py:190 in transform_path_non_affine
    proj_geom = self.target_projection.project_geometry(
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/crs.py:808 in project_geometry
    return getattr(self, method_name)(geometry, src_crs)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/crs.py:963 in _project_polygon
    return self._rings_to_multi_polygon(rings, is_ccw)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/crs.py:1224 in _rings_to_multi_polygon
    multi_poly = sgeom.MultiPolygon(polygon_bits)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/shapely/geometry/multipolygon.py:79 in __new__
    shell = ob[0]
TypeError: 'GeometryCollection' object is not subscriptable
Full environment definition ### Operating system ``` RHEL7.9 (xcd050xes071spy543mat371car0211ipy8132) bash-4.2$ uname -a Linux detect 3.10.0-1160.76.1.el7.x86_64 #1 SMP Tue Jul 26 14:15:37 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux ``` ### Cartopy version ``` cartopy.__version__** '0.21.1' ``` ### conda list ``` (xcd050xes071spy543mat371car0211ipy8132) bash-4.2$ conda list # packages in environment at ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132: # # Name Version Build Channel _libgcc_mutex 0.1 conda_forge conda-forge _openmp_mutex 4.5 2_gnu conda-forge alabaster 0.7.13 pyhd8ed1ab_0 conda-forge alsa-lib 1.2.8 h166bdaf_0 conda-forge anyio 3.6.2 pyhd8ed1ab_0 conda-forge argon2-cffi 21.3.0 pyhd8ed1ab_0 conda-forge argon2-cffi-bindings 21.2.0 py310h5764c6d_3 conda-forge arrow 1.2.3 pyhd8ed1ab_0 conda-forge arrow-cpp 11.0.0 ha770c72_16_cpu conda-forge astroid 2.15.4 py310hff52083_0 conda-forge asttokens 2.2.1 pyhd8ed1ab_0 conda-forge atomicwrites 1.4.1 pyhd8ed1ab_0 conda-forge attr 2.5.1 h166bdaf_1 conda-forge attrs 23.1.0 pyh71513ae_1 conda-forge autopep8 2.0.2 pyhd8ed1ab_0 conda-forge aws-c-auth 0.6.26 h2c7c9e7_6 conda-forge aws-c-cal 0.5.26 h71eb795_0 conda-forge aws-c-common 0.8.17 hd590300_0 conda-forge aws-c-compression 0.2.16 h4f47f36_6 conda-forge aws-c-event-stream 0.2.20 h69ce273_6 conda-forge aws-c-http 0.7.7 h7b8353a_3 conda-forge aws-c-io 0.13.21 hcccde9c_3 conda-forge aws-c-mqtt 0.8.6 h3a1964a_15 conda-forge aws-c-s3 0.2.8 h0933b68_4 conda-forge aws-c-sdkutils 0.1.9 h4f47f36_1 conda-forge aws-checksums 0.1.14 h4f47f36_6 conda-forge aws-crt-cpp 0.19.9 h85076f6_5 conda-forge aws-sdk-cpp 1.10.57 hf40e4db_10 conda-forge babel 2.12.1 pyhd8ed1ab_1 conda-forge backcall 0.2.0 pyh9f0ad1d_0 conda-forge backports 1.0 pyhd8ed1ab_3 conda-forge backports.functools_lru_cache 1.6.4 pyhd8ed1ab_0 conda-forge beautifulsoup4 4.12.2 pyha770c72_0 conda-forge binaryornot 0.4.4 py_1 conda-forge black 23.3.0 py310hff52083_1 conda-forge bleach 6.0.0 pyhd8ed1ab_0 conda-forge blosc 1.21.3 hafa529b_0 conda-forge bokeh 3.1.0 pyhd8ed1ab_0 conda-forge bottleneck 1.3.7 py310h0a54255_0 conda-forge brotli 1.0.9 h166bdaf_8 conda-forge brotli-bin 1.0.9 h166bdaf_8 conda-forge brotlipy 0.7.0 py310h5764c6d_1005 conda-forge bzip2 1.0.8 h7f98852_4 conda-forge c-ares 1.18.1 h7f98852_0 conda-forge ca-certificates 2023.5.7 hbcca054_0 conda-forge cairo 1.16.0 h35add3b_1015 conda-forge cartopy 0.21.1 py310h7eb24ba_1 conda-forge certifi 2023.5.7 pyhd8ed1ab_0 conda-forge cf_xarray 0.8.0 pyhd8ed1ab_0 conda-forge cffi 1.15.1 py310h255011f_3 conda-forge cftime 1.6.2 py310hde88566_1 conda-forge chardet 5.1.0 py310hff52083_0 conda-forge charset-normalizer 3.1.0 pyhd8ed1ab_0 conda-forge click 8.1.3 unix_pyhd8ed1ab_2 conda-forge cloudpickle 2.2.1 pyhd8ed1ab_0 conda-forge colorama 0.4.6 pyhd8ed1ab_0 conda-forge comm 0.1.3 pyhd8ed1ab_0 conda-forge contourpy 1.0.7 py310hdf3cbec_0 conda-forge cookiecutter 2.1.1 pyh6c4a22f_0 conda-forge cryptography 40.0.2 py310h34c0648_0 conda-forge curl 8.0.1 h588be90_0 conda-forge cycler 0.11.0 pyhd8ed1ab_0 conda-forge cytoolz 0.12.0 py310h5764c6d_1 conda-forge dask 2023.4.1 pyhd8ed1ab_0 conda-forge dask-core 2023.4.1 pyhd8ed1ab_0 conda-forge dbus 1.13.6 h5008d03_3 conda-forge debugpy 1.6.7 py310heca2aa9_0 conda-forge decorator 5.1.1 pyhd8ed1ab_0 conda-forge defusedxml 0.7.1 pyhd8ed1ab_0 conda-forge diff-match-patch 20200713 pyh9f0ad1d_0 conda-forge dill 0.3.6 pyhd8ed1ab_1 conda-forge distributed 2023.4.1 pyhd8ed1ab_0 conda-forge docstring-to-markdown 0.12 pyhd8ed1ab_0 conda-forge docutils 0.19 py310hff52083_1 conda-forge entrypoints 0.4 pyhd8ed1ab_0 conda-forge esmf 8.4.2 mpi_mpich_h742b71d_100 conda-forge esmpy 8.4.2 pyhc1e730c_0 conda-forge executing 1.2.0 pyhd8ed1ab_0 conda-forge expat 2.5.0 hcb278e6_1 conda-forge fftw 3.3.10 nompi_hc118613_107 conda-forge flake8 6.0.0 pyhd8ed1ab_0 conda-forge flit-core 3.8.0 pyhd8ed1ab_0 conda-forge font-ttf-dejavu-sans-mono 2.37 hab24e00_0 conda-forge font-ttf-inconsolata 3.000 h77eed37_0 conda-forge font-ttf-source-code-pro 2.038 h77eed37_0 conda-forge font-ttf-ubuntu 0.83 hab24e00_0 conda-forge fontconfig 2.14.2 h14ed4e7_0 conda-forge fonts-conda-ecosystem 1 0 conda-forge fonts-conda-forge 1 0 conda-forge fonttools 4.39.3 py310h1fa729e_0 conda-forge freetype 2.12.1 hca18f0e_1 conda-forge fsspec 2023.5.0 pyh1a96a4e_0 conda-forge geos 3.11.2 hcb278e6_0 conda-forge gettext 0.21.1 h27087fc_0 conda-forge gflags 2.2.2 he1b5a44_1004 conda-forge giflib 5.2.1 h0b41bf4_3 conda-forge glib 2.76.2 hfc55251_0 conda-forge glib-tools 2.76.2 hfc55251_0 conda-forge glog 0.6.0 h6f12383_0 conda-forge graphite2 1.3.13 h58526e2_1001 conda-forge gst-plugins-base 1.22.0 h4243ec0_2 conda-forge gstreamer 1.22.0 h25f0c4b_2 conda-forge gstreamer-orc 0.4.33 h166bdaf_0 conda-forge harfbuzz 6.0.0 h3ff4399_1 conda-forge hdf4 4.2.15 h501b40f_6 conda-forge hdf5 1.14.0 mpi_mpich_hcccbd3f_3 conda-forge icu 72.1 hcb278e6_0 conda-forge idna 3.4 pyhd8ed1ab_0 conda-forge imagesize 1.4.1 pyhd8ed1ab_0 conda-forge importlib-metadata 6.6.0 pyha770c72_0 conda-forge importlib_metadata 6.6.0 hd8ed1ab_0 conda-forge importlib_resources 5.12.0 pyhd8ed1ab_0 conda-forge inflection 0.5.1 pyh9f0ad1d_0 conda-forge intervaltree 3.1.0 pyhd8ed1ab_1 conda-forge ipykernel 6.23.0 pyh210e3f2_0 conda-forge ipython 8.13.2 pyh41d4057_0 conda-forge ipython_genutils 0.2.0 py_1 conda-forge ipywidgets 8.0.6 pyhd8ed1ab_0 conda-forge isort 5.12.0 pyhd8ed1ab_1 conda-forge jack 1.9.22 h11f4161_0 conda-forge jaraco.classes 3.2.3 pyhd8ed1ab_0 conda-forge jedi 0.18.2 pyhd8ed1ab_0 conda-forge jeepney 0.8.0 pyhd8ed1ab_0 conda-forge jellyfish 0.9.0 py310h5764c6d_2 conda-forge jinja2 3.1.2 pyhd8ed1ab_1 conda-forge jinja2-time 0.2.0 pyhd8ed1ab_3 conda-forge jsonschema 4.17.3 pyhd8ed1ab_0 conda-forge jupyter 1.0.0 py310hff52083_8 conda-forge jupyter_client 8.2.0 pyhd8ed1ab_0 conda-forge jupyter_console 6.6.3 pyhd8ed1ab_0 conda-forge jupyter_core 5.3.0 py310hff52083_0 conda-forge jupyter_events 0.6.3 pyhd8ed1ab_0 conda-forge jupyter_server 2.5.0 pyhd8ed1ab_0 conda-forge jupyter_server_terminals 0.4.4 pyhd8ed1ab_1 conda-forge jupyterlab_pygments 0.2.2 pyhd8ed1ab_0 conda-forge jupyterlab_widgets 3.0.7 pyhd8ed1ab_0 conda-forge keyring 23.13.1 py310hff52083_0 conda-forge keyutils 1.6.1 h166bdaf_0 conda-forge kiwisolver 1.4.4 py310hbf28c38_1 conda-forge krb5 1.20.1 h81ceb04_0 conda-forge lame 3.100 h166bdaf_1003 conda-forge lazy-object-proxy 1.9.0 py310h1fa729e_0 conda-forge lcms2 2.15 haa2dc70_1 conda-forge ld_impl_linux-64 2.40 h41732ed_0 conda-forge lerc 4.0.0 h27087fc_0 conda-forge libabseil 20230125.0 cxx17_hcb278e6_1 conda-forge libaec 1.0.6 hcb278e6_1 conda-forge libarrow 11.0.0 h8dc56a0_16_cpu conda-forge libblas 3.9.0 16_linux64_openblas conda-forge libbrotlicommon 1.0.9 h166bdaf_8 conda-forge libbrotlidec 1.0.9 h166bdaf_8 conda-forge libbrotlienc 1.0.9 h166bdaf_8 conda-forge libcap 2.67 he9d0100_0 conda-forge libcblas 3.9.0 16_linux64_openblas conda-forge libclang 16.0.3 default_h83cc7fd_0 conda-forge libclang13 16.0.3 default_hd781213_0 conda-forge libcrc32c 1.1.2 h9c3ff4c_0 conda-forge libcups 2.3.3 h36d4200_3 conda-forge libcurl 8.0.1 h588be90_0 conda-forge libdb 6.2.32 h9c3ff4c_0 conda-forge libdeflate 1.18 h0b41bf4_0 conda-forge libedit 3.1.20191231 he28a2e2_2 conda-forge libev 4.33 h516909a_1 conda-forge libevent 2.1.10 h28343ad_4 conda-forge libexpat 2.5.0 hcb278e6_1 conda-forge libffi 3.4.2 h7f98852_5 conda-forge libflac 1.4.2 h27087fc_0 conda-forge libgcc-ng 12.2.0 h65d4601_19 conda-forge libgcrypt 1.10.1 h166bdaf_0 conda-forge libgfortran-ng 12.2.0 h69a702a_19 conda-forge libgfortran5 12.2.0 h337968e_19 conda-forge libglib 2.76.2 hebfc3b9_0 conda-forge libgomp 12.2.0 h65d4601_19 conda-forge libgoogle-cloud 2.8.0 hac9eb74_2 conda-forge libgpg-error 1.46 h620e276_0 conda-forge libgrpc 1.54.1 hcf146ea_0 conda-forge libiconv 1.17 h166bdaf_0 conda-forge libjpeg-turbo 2.1.5.1 h0b41bf4_0 conda-forge liblapack 3.9.0 16_linux64_openblas conda-forge libllvm11 11.1.0 he0ac6c6_5 conda-forge libllvm16 16.0.3 hbf9e925_1 conda-forge libnetcdf 4.9.2 mpi_mpich_h53eaf7b_4 conda-forge libnghttp2 1.52.0 h61bc06f_0 conda-forge libnsl 2.0.0 h7f98852_0 conda-forge libnuma 2.0.16 h0b41bf4_1 conda-forge libogg 1.3.4 h7f98852_1 conda-forge libopenblas 0.3.21 pthreads_h78a6416_3 conda-forge libopus 1.3.1 h7f98852_1 conda-forge libpng 1.6.39 h753d276_0 conda-forge libpq 15.2 hb675445_0 conda-forge libprotobuf 3.21.12 h3eb15da_0 conda-forge libsndfile 1.2.0 hb75c966_0 conda-forge libsodium 1.0.18 h36c2ea0_1 conda-forge libspatialindex 1.9.3 h9c3ff4c_4 conda-forge libsqlite 3.40.0 h753d276_1 conda-forge libssh2 1.10.0 hf14f497_3 conda-forge libstdcxx-ng 12.2.0 h46fd767_19 conda-forge libsystemd0 253 h8c4010b_1 conda-forge libthrift 0.18.1 h5e4af38_0 conda-forge libtiff 4.5.0 ha587672_6 conda-forge libtool 2.4.7 h27087fc_0 conda-forge libudev1 253 h0b41bf4_1 conda-forge libutf8proc 2.8.0 h166bdaf_0 conda-forge libuuid 2.38.1 h0b41bf4_0 conda-forge libvorbis 1.3.7 h9c3ff4c_0 conda-forge libwebp 1.3.0 hb47c5f0_0 conda-forge libwebp-base 1.3.0 h0b41bf4_0 conda-forge libxcb 1.13 h7f98852_1004 conda-forge libxkbcommon 1.5.0 h79f4944_1 conda-forge libxml2 2.10.4 hfdac1af_0 conda-forge libxslt 1.1.37 h873f0b0_0 conda-forge libzip 1.9.2 hc929e4a_1 conda-forge libzlib 1.2.13 h166bdaf_4 conda-forge llvmlite 0.39.1 py310h58363a5_1 conda-forge locket 1.0.0 pyhd8ed1ab_0 conda-forge lxml 4.9.2 py310hbdc0903_0 conda-forge lz4 4.3.2 py310h0cfdcf0_0 conda-forge lz4-c 1.9.4 hcb278e6_0 conda-forge markupsafe 2.1.2 py310h1fa729e_0 conda-forge matplotlib 3.7.1 py310hff52083_0 conda-forge matplotlib-base 3.7.1 py310he60537e_0 conda-forge matplotlib-inline 0.1.6 pyhd8ed1ab_0 conda-forge mccabe 0.7.0 pyhd8ed1ab_0 conda-forge mistune 2.0.5 pyhd8ed1ab_0 conda-forge more-itertools 9.1.0 pyhd8ed1ab_0 conda-forge mpg123 1.31.3 hcb278e6_0 conda-forge mpi 1.0 mpich conda-forge mpich 4.1.1 h846660c_100 conda-forge msgpack-python 1.0.5 py310hdf3cbec_0 conda-forge munkres 1.1.4 pyh9f0ad1d_0 conda-forge mypy_extensions 1.0.0 pyha770c72_0 conda-forge mysql-common 8.0.32 hf1915f5_2 conda-forge mysql-libs 8.0.32 hca2cd23_2 conda-forge nbclassic 1.0.0 pyhb4ecaf3_1 conda-forge nbclient 0.7.4 pyhd8ed1ab_0 conda-forge nbconvert 7.3.1 pyhd8ed1ab_0 conda-forge nbconvert-core 7.3.1 pyhd8ed1ab_0 conda-forge nbconvert-pandoc 7.3.1 pyhd8ed1ab_0 conda-forge nbformat 5.8.0 pyhd8ed1ab_0 conda-forge ncurses 6.3 h27087fc_1 conda-forge nest-asyncio 1.5.6 pyhd8ed1ab_0 conda-forge netcdf-fortran 4.6.0 mpi_mpich_hebcc635_5 conda-forge netcdf4 1.6.3 nompi_py310h2d0b64f_102 conda-forge notebook 6.5.4 pyha770c72_0 conda-forge notebook-shim 0.2.3 pyhd8ed1ab_0 conda-forge nspr 4.35 h27087fc_0 conda-forge nss 3.89 he45b914_0 conda-forge numba 0.56.4 py310h0e39c9b_1 conda-forge numpy 1.23.5 py310h53a5b5f_0 conda-forge numpydoc 1.5.0 pyhd8ed1ab_0 conda-forge openjpeg 2.5.0 hfec8fc6_2 conda-forge openssl 3.1.0 hd590300_3 conda-forge orc 1.8.3 hfdbbad2_0 conda-forge packaging 23.1 pyhd8ed1ab_0 conda-forge pandas 2.0.1 py310h7cbd5c2_1 conda-forge pandoc 2.19.2 h32600fe_2 conda-forge pandocfilters 1.5.0 pyhd8ed1ab_0 conda-forge parallelio 2.5.10 mpi_mpich_h0b4b548_102 conda-forge parquet-cpp 1.5.1 2 conda-forge parso 0.8.3 pyhd8ed1ab_0 conda-forge partd 1.4.0 pyhd8ed1ab_0 conda-forge pathspec 0.11.1 pyhd8ed1ab_0 conda-forge pcre2 10.40 hc3806b6_0 conda-forge pexpect 4.8.0 pyh1a96a4e_2 conda-forge pickleshare 0.7.5 py_1003 conda-forge pillow 9.5.0 py310h065c6d2_0 conda-forge pip 23.1.2 pyhd8ed1ab_0 conda-forge pixman 0.40.0 h36c2ea0_0 conda-forge pkgutil-resolve-name 1.3.10 pyhd8ed1ab_0 conda-forge platformdirs 3.5.0 pyhd8ed1ab_0 conda-forge pluggy 1.0.0 pyhd8ed1ab_5 conda-forge ply 3.11 py_1 conda-forge pooch 1.7.0 pyha770c72_3 conda-forge proj 9.2.0 h8ffa02c_0 conda-forge prometheus_client 0.16.0 pyhd8ed1ab_0 conda-forge prompt-toolkit 3.0.38 pyha770c72_0 conda-forge prompt_toolkit 3.0.38 hd8ed1ab_0 conda-forge psutil 5.9.5 py310h1fa729e_0 conda-forge pthread-stubs 0.4 h36c2ea0_1001 conda-forge ptyprocess 0.7.0 pyhd3deb0d_0 conda-forge pulseaudio 16.1 hcb278e6_3 conda-forge pulseaudio-client 16.1 h5195f5e_3 conda-forge pulseaudio-daemon 16.1 ha8d29e2_3 conda-forge pure_eval 0.2.2 pyhd8ed1ab_0 conda-forge pyarrow 11.0.0 py310he6bfd7f_16_cpu conda-forge pycodestyle 2.10.0 pyhd8ed1ab_0 conda-forge pycparser 2.21 pyhd8ed1ab_0 conda-forge pydocstyle 6.3.0 pyhd8ed1ab_0 conda-forge pyflakes 3.0.1 pyhd8ed1ab_0 conda-forge pygments 2.15.1 pyhd8ed1ab_0 conda-forge pylint 2.17.4 pyhd8ed1ab_0 conda-forge pylint-venv 3.0.1 pyhd8ed1ab_0 conda-forge pyls-spyder 0.4.0 pyhd8ed1ab_0 conda-forge pyopenssl 23.1.1 pyhd8ed1ab_0 conda-forge pyparsing 3.0.9 pyhd8ed1ab_0 conda-forge pyproj 3.5.0 py310hb814896_1 conda-forge pyqt 5.15.7 py310hab646b1_3 conda-forge pyqt5-sip 12.11.0 py310heca2aa9_3 conda-forge pyqtwebengine 5.15.7 py310h1165ae2_3 conda-forge pyrsistent 0.19.3 py310h1fa729e_0 conda-forge pyshp 2.3.1 pyhd8ed1ab_0 conda-forge pysocks 1.7.1 pyha2e5f31_6 conda-forge python 3.10.10 he550d4f_0_cpython conda-forge python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge python-fastjsonschema 2.16.3 pyhd8ed1ab_0 conda-forge python-json-logger 2.0.7 pyhd8ed1ab_0 conda-forge python-lsp-black 1.2.1 pyhd8ed1ab_0 conda-forge python-lsp-jsonrpc 1.0.0 pyhd8ed1ab_0 conda-forge python-lsp-server 1.7.2 pyhd8ed1ab_0 conda-forge python-lsp-server-base 1.7.2 pyhd8ed1ab_0 conda-forge python-slugify 8.0.1 pyhd8ed1ab_1 conda-forge python-tzdata 2023.3 pyhd8ed1ab_0 conda-forge python_abi 3.10 3_cp310 conda-forge pytoolconfig 1.2.5 pyhd8ed1ab_0 conda-forge pytz 2023.3 pyhd8ed1ab_0 conda-forge pyxdg 0.28 pyhd8ed1ab_0 conda-forge pyyaml 6.0 py310h5764c6d_5 conda-forge pyzmq 25.0.2 py310h059b190_0 conda-forge qdarkstyle 3.1 pyhd8ed1ab_0 conda-forge qstylizer 0.2.2 pyhd8ed1ab_0 conda-forge qt-main 5.15.8 h5c52f38_10 conda-forge qt-webengine 5.15.8 h77d6d84_0 conda-forge qtawesome 1.2.3 pyhd8ed1ab_0 conda-forge qtconsole 5.4.3 pyhd8ed1ab_0 conda-forge qtconsole-base 5.4.3 pyha770c72_0 conda-forge qtpy 2.3.1 pyhd8ed1ab_0 conda-forge re2 2023.02.02 hcb278e6_0 conda-forge readline 8.2 h8228510_1 conda-forge requests 2.29.0 pyhd8ed1ab_0 conda-forge rfc3339-validator 0.1.4 pyhd8ed1ab_0 conda-forge rfc3986-validator 0.1.1 pyh9f0ad1d_0 conda-forge rope 1.8.0 pyhd8ed1ab_0 conda-forge rtree 1.0.1 py310hbdcdc62_1 conda-forge s2n 1.3.42 h3358134_0 conda-forge scipy 1.10.1 py310h8deb116_1 conda-forge secretstorage 3.3.3 py310hff52083_1 conda-forge send2trash 1.8.2 pyh41d4057_0 conda-forge setuptools 67.7.2 pyhd8ed1ab_0 conda-forge shapely 2.0.1 py310h056c13c_1 conda-forge sip 6.7.9 py310hc6cd4ac_0 conda-forge six 1.16.0 pyh6c4a22f_0 conda-forge snappy 1.1.10 h9fff704_0 conda-forge sniffio 1.3.0 pyhd8ed1ab_0 conda-forge snowballstemmer 2.2.0 pyhd8ed1ab_0 conda-forge sortedcontainers 2.4.0 pyhd8ed1ab_0 conda-forge soupsieve 2.3.2.post1 pyhd8ed1ab_0 conda-forge sparse 0.14.0 pyhd8ed1ab_0 conda-forge sphinx 7.0.0 pyhd8ed1ab_0 conda-forge sphinxcontrib-applehelp 1.0.4 pyhd8ed1ab_0 conda-forge sphinxcontrib-devhelp 1.0.2 py_0 conda-forge sphinxcontrib-htmlhelp 2.0.1 pyhd8ed1ab_0 conda-forge sphinxcontrib-jsmath 1.0.1 py_0 conda-forge sphinxcontrib-qthelp 1.0.3 py_0 conda-forge sphinxcontrib-serializinghtml 1.1.5 pyhd8ed1ab_2 conda-forge spyder 5.4.3 py310hff52083_0 conda-forge spyder-kernels 2.4.3 unix_pyhd8ed1ab_0 conda-forge sqlite 3.40.0 h4ff8645_1 conda-forge stack_data 0.6.2 pyhd8ed1ab_0 conda-forge tblib 1.7.0 pyhd8ed1ab_0 conda-forge terminado 0.17.1 pyh41d4057_0 conda-forge text-unidecode 1.3 py_0 conda-forge textdistance 4.5.0 pyhd8ed1ab_0 conda-forge three-merge 0.1.1 pyh9f0ad1d_0 conda-forge tinycss2 1.2.1 pyhd8ed1ab_0 conda-forge tk 8.6.12 h27826a3_0 conda-forge toml 0.10.2 pyhd8ed1ab_0 conda-forge tomli 2.0.1 pyhd8ed1ab_0 conda-forge tomlkit 0.11.8 pyha770c72_0 conda-forge toolz 0.12.0 pyhd8ed1ab_0 conda-forge tornado 6.3 py310h1fa729e_0 conda-forge traitlets 5.9.0 pyhd8ed1ab_0 conda-forge typing-extensions 4.5.0 hd8ed1ab_0 conda-forge typing_extensions 4.5.0 pyha770c72_0 conda-forge tzdata 2023c h71feb2d_0 conda-forge ucx 1.14.0 h3484d09_2 conda-forge ujson 5.7.0 py310heca2aa9_0 conda-forge unicodedata2 15.0.0 py310h5764c6d_0 conda-forge unidecode 1.3.6 pyhd8ed1ab_0 conda-forge urllib3 1.26.15 pyhd8ed1ab_0 conda-forge watchdog 3.0.0 py310hff52083_0 conda-forge wcwidth 0.2.6 pyhd8ed1ab_0 conda-forge webencodings 0.5.1 py_1 conda-forge websocket-client 1.5.1 pyhd8ed1ab_0 conda-forge whatthepatch 1.0.5 pyhd8ed1ab_0 conda-forge wheel 0.40.0 pyhd8ed1ab_0 conda-forge widgetsnbextension 4.0.7 pyhd8ed1ab_0 conda-forge wrapt 1.15.0 py310h1fa729e_0 conda-forge wurlitzer 3.0.3 pyhd8ed1ab_0 conda-forge xarray 2023.4.2 pyhd8ed1ab_0 conda-forge xcb-util 0.4.0 h516909a_0 conda-forge xcb-util-image 0.4.0 h166bdaf_0 conda-forge xcb-util-keysyms 0.4.0 h516909a_0 conda-forge xcb-util-renderutil 0.3.9 h166bdaf_0 conda-forge xcb-util-wm 0.4.1 h516909a_0 conda-forge xcdat 0.5.0 pyhd8ed1ab_0 conda-forge xesmf 0.7.1 pyhd8ed1ab_0 conda-forge xkeyboard-config 2.38 h0b41bf4_0 conda-forge xorg-kbproto 1.0.7 h7f98852_1002 conda-forge xorg-libice 1.0.10 h7f98852_0 conda-forge xorg-libsm 1.2.3 hd9c2040_1000 conda-forge xorg-libx11 1.8.4 h0b41bf4_0 conda-forge xorg-libxau 1.0.9 h7f98852_0 conda-forge xorg-libxdmcp 1.1.3 h7f98852_0 conda-forge xorg-libxext 1.3.4 h0b41bf4_2 conda-forge xorg-libxrender 0.9.10 h7f98852_1003 conda-forge xorg-renderproto 0.11.1 h7f98852_1002 conda-forge xorg-xextproto 7.3.0 h0b41bf4_1003 conda-forge xorg-xf86vidmodeproto 2.3.1 h7f98852_1002 conda-forge xorg-xproto 7.0.31 h7f98852_1007 conda-forge xyzservices 2023.2.0 pyhd8ed1ab_0 conda-forge xz 5.2.6 h166bdaf_0 conda-forge yaml 0.2.5 h7f98852_2 conda-forge yapf 0.32.0 pyhd8ed1ab_0 conda-forge zeromq 4.3.4 h9c3ff4c_1 conda-forge zict 3.0.0 pyhd8ed1ab_0 conda-forge zipp 3.15.0 pyhd8ed1ab_0 conda-forge zlib 1.2.13 h166bdaf_4 conda-forge zstd 1.5.2 h3eb15da_6 conda-forge ``` ### pip list ``` nil ```
greglucas commented 1 year ago

This is likely some bad projection going on when transforming the geometries near a boundary. As a workaround, you can use the transform_first keyword which should avoid that path. https://scitools.org.uk/cartopy/docs/latest/gallery/scalar_data/contour_transforms.html#sphx-glr-gallery-scalar-data-contour-transforms-py

durack1 commented 1 year ago

Thanks @greglucas! This tweak gets me back up and running again, great.

Should I close this issue, or leave this open for a more permanent diagnosis and fix? Apologies, I became confused as to what was going on, so without guidance you will not be getting a PR from me.

greglucas commented 1 year ago

I think we should leave it open and hope for a better solution rather than a workaround.

If you're interested in tackling this that would be great! It may not be too difficult, but it is sometimes hard to tell how deep into the code you'll have to get. If you want some guidance, I'd suggest isolating the failure geometry from the contour call. Adding some print or to_wkt() statements into the geometry transform region of the code should help get the one right before the failure.

  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/crs.py:963 in _project_polygon
    return self._rings_to_multi_polygon(rings, is_ccw)
  File ~/mambaforge/envs/xcd050xes071spy543mat371car0211ipy8132/lib/python3.10/site-packages/cartopy/crs.py:1224 in _rings_to_multi_polygon
    multi_poly = sgeom.MultiPolygon(polygon_bits)

Then find out why that input geometry gets transformed into an output GeometryCollection and what geometries are in that GeometryCollection, maybe it is as simple as removing any extraneous point geometries after cutting/stitching.

david-shu commented 12 months ago

I encountered the same problem. Trace to cartopy.crs.Projection Rings To Multi Polygon method. When boundary_poly. intersection (polygon), a GeoCollection object is obtained instead of a Polygon object, resulting in Exception thrown when sgeom.MultiPolygon(polygon_bits).

from shapely.geometry.collection import GeometryCollection
import shapely.geometry as sgeom
from shapely.prepared import prep

def _rings_to_multi_polygon(self, rings, is_ccw):
    exterior_rings = []
    interior_rings = []
    for ring in rings:
        if ring.is_ccw != is_ccw:
            interior_rings.append(ring)
        else:
            exterior_rings.append(ring)

    polygon_bits = []

    # Turn all the exterior rings into polygon definitions,
    # "slurping up" any interior rings they contain.
    for exterior_ring in exterior_rings:
        polygon = sgeom.Polygon(exterior_ring)
        prep_polygon = prep(polygon)
        holes = []
        for interior_ring in interior_rings[:]:
            if prep_polygon.contains(interior_ring):
                holes.append(interior_ring)
                interior_rings.remove(interior_ring)
            elif polygon.crosses(interior_ring):
                # Likely that we have an invalid geometry such as
                # that from #509 or #537.
                holes.append(interior_ring)
                interior_rings.remove(interior_ring)
        polygon_bits.append((exterior_ring.coords,
                             [ring.coords for ring in holes]))

    # Any left over "interior" rings need "inverting" with respect
    # to the boundary.
    if interior_rings:
        boundary_poly = self.domain
        x3, y3, x4, y4 = boundary_poly.bounds
        bx = (x4 - x3) * 0.1
        by = (y4 - y3) * 0.1
        x3 -= bx
        y3 -= by
        x4 += bx
        y4 += by
        for ring in interior_rings:
            # Use shapely buffer in an attempt to fix invalid geometries
            polygon = sgeom.Polygon(ring).buffer(0)
            if not polygon.is_empty and polygon.is_valid:
                x1, y1, x2, y2 = polygon.bounds
                bx = (x2 - x1) * 0.1
                by = (y2 - y1) * 0.1
                x1 -= bx
                y1 -= by
                x2 += bx
                y2 += by
                box = sgeom.box(min(x1, x3), min(y1, y3),
                                max(x2, x4), max(y2, y4))

                # Invert the polygon
                polygon = box.difference(polygon)

                # Intersect the inverted polygon with the boundary
                polygon = boundary_poly.intersection(polygon)
                if not polygon.is_empty:
                    polygon_bits.append(polygon)

    if polygon_bits:
        ##### added by david.shu ###################################
        polygon_bits2 = []
        for polygon in polygon_bits:
            if isinstance( polygon, GeometryCollection ):
                for g in polygon.geoms:
                    if isinstance( g, sgeom.Polygon ):
                        polygon_bits2.append( g )
                    else:
                        print( f'{type(g)} found. but Polygon required.' )
            else:
                polygon_bits2.append( polygon )
        polygon_bits = polygon_bits2 
        #######################################################

        multi_poly = sgeom.MultiPolygon(polygon_bits)
    else:
        multi_poly = sgeom.MultiPolygon()
    return multi_poly        

ccrs.Projection._rings_to_multi_polygon = _rings_to_multi_polygon
kafitzgerald commented 6 months ago

I'm also seeing this error with cartopy (version 0.22.0) and shapely version 2 and above (dropping down to version 1.8.x allows things to work properly).

In this particular case it's coming from Xarray DataArray.plot which uses pcolormesh and pcolor

I can try to dig into this a bit more and/or put together a MRE, but wanted to at least log this here in case it helps other users or devs.

``` --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In[7], line 13 10 tas_present = test.sel(time="2014-07").squeeze() 12 # plot the model data ---> 13 (tas_present - 273.15).plot( 14 ax=ax_present, 15 x="lon", 16 y="lat", 17 transform=ccrs.PlateCarree(), 18 vmin=-10, 19 vmax=30, 20 cmap="magma" 21 #robust=True 22 ) 23 ax_present.coastlines() 24 ax_present.set_title("July 2014") File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/xarray/plot/accessor.py:48, in DataArrayPlotAccessor.__call__(self, **kwargs) 46 @functools.wraps(dataarray_plot.plot, assigned=("__doc__", "__annotations__")) 47 def __call__(self, **kwargs) -> Any: ---> 48 return dataarray_plot.plot(self._da, **kwargs) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/xarray/plot/dataarray_plot.py:309, in plot(darray, row, col, col_wrap, ax, hue, subplot_kws, **kwargs) 305 plotfunc = hist 307 kwargs["ax"] = ax --> 309 return plotfunc(darray, **kwargs) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/xarray/plot/dataarray_plot.py:1601, in _plot2d..newplotfunc(***failed resolving arguments***) 1597 raise ValueError("plt.imshow's `aspect` kwarg is not available in xarray") 1599 ax = get_axis(figsize, size, aspect, ax, **subplot_kws) -> 1601 primitive = plotfunc( 1602 xplt, 1603 yplt, 1604 zval, 1605 ax=ax, 1606 cmap=cmap_params["cmap"], 1607 vmin=cmap_params["vmin"], 1608 vmax=cmap_params["vmax"], 1609 norm=cmap_params["norm"], 1610 **kwargs, 1611 ) 1613 # Label the plot with metadata 1614 if add_labels: File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/xarray/plot/dataarray_plot.py:2309, in pcolormesh(x, y, z, ax, xscale, yscale, infer_intervals, **kwargs) 2306 y = _infer_interval_breaks(y, axis=0, scale=yscale) 2308 ax.grid(False) -> 2309 primitive = ax.pcolormesh(x, y, z, **kwargs) 2311 # by default, pcolormesh picks "round" values for bounds 2312 # this results in ugly looking plots with lots of surrounding whitespace 2313 if not hasattr(ax, "projection") and x.ndim == 1 and y.ndim == 1: 2314 # not a cartopy geoaxis File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:315, in _add_transform..wrapper(self, *args, **kwargs) 310 raise ValueError(f'Invalid transform: Spherical {func.__name__} ' 311 'is not supported - consider using ' 312 'PlateCarree/RotatedPole.') 314 kwargs['transform'] = transform --> 315 return func(self, *args, **kwargs) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:1783, in GeoAxes.pcolormesh(self, *args, **kwargs) 1781 result = super().pcolormesh(*args, **kwargs) 1782 # Wrap the quadrilaterals if necessary -> 1783 result = self._wrap_quadmesh(result, **kwargs) 1784 # Re-cast the QuadMesh as a GeoQuadMesh to enable future wrapping 1785 # updates to the collection as well. 1786 result.__class__ = cartopy.mpl.geocollection.GeoQuadMesh File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:1965, in GeoAxes._wrap_quadmesh(self, collection, **kwargs) 1955 if _MPL_VERSION.release[:2] < (3, 8): 1956 # We will add the original data mask in later to 1957 # make sure that set_array can work in future (...) 1962 # fill in the proper data later with set_array() 1963 # calls. 1964 pcolor_zeros = np.ma.array(np.zeros(C.shape), mask=pcolor_mask) -> 1965 pcolor_col = self.pcolor(coords[..., 0], coords[..., 1], 1966 pcolor_zeros, zorder=zorder, 1967 **kwargs) 1969 # The pcolor_col is now possibly shorter than the 1970 # actual collection, so grab the masked cells 1971 pcolor_col.set_array(pcolor_data[mask].ravel()) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:315, in _add_transform..wrapper(self, *args, **kwargs) 310 raise ValueError(f'Invalid transform: Spherical {func.__name__} ' 311 'is not supported - consider using ' 312 'PlateCarree/RotatedPole.') 314 kwargs['transform'] = transform --> 315 return func(self, *args, **kwargs) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:2012, in GeoAxes.pcolor(self, *args, **kwargs) 2009 result = super().pcolor(*args, **kwargs) 2011 # Update the datalim for this pcolor. -> 2012 limits = result.get_datalim(self.transData) 2013 self.update_datalim(limits) 2015 self.autoscale_view() File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/matplotlib/collections.py:266, in Collection.get_datalim(self, transData) 263 return transforms.Bbox.null() 265 if not transform.is_affine: --> 266 paths = [transform.transform_path_non_affine(p) for p in paths] 267 # Don't convert transform to transform.get_affine() here because 268 # we may have transform.contains_branch(transData) but not 269 # transforms.get_affine().contains_branch(transData). But later, 270 # be careful to only apply the affine part that remains. 272 if any(transform.contains_branch_seperately(transData)): 273 # collections that are just in data units (like quiver) 274 # can properly have the axes limits set by their shape + 275 # offset. LineCollections that have no offsets can 276 # also use this algorithm (like streamplot). File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/matplotlib/collections.py:266, in (.0) 263 return transforms.Bbox.null() 265 if not transform.is_affine: --> 266 paths = [transform.transform_path_non_affine(p) for p in paths] 267 # Don't convert transform to transform.get_affine() here because 268 # we may have transform.contains_branch(transData) but not 269 # transforms.get_affine().contains_branch(transData). But later, 270 # be careful to only apply the affine part that remains. 272 if any(transform.contains_branch_seperately(transData)): 273 # collections that are just in data units (like quiver) 274 # can properly have the axes limits set by their shape + 275 # offset. LineCollections that have no offsets can 276 # also use this algorithm (like streamplot). File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/matplotlib/transforms.py:2439, in CompositeGenericTransform.transform_path_non_affine(self, path) 2437 return path 2438 elif not self._a.is_affine and self._b.is_affine: -> 2439 return self._a.transform_path_non_affine(path) 2440 else: 2441 return self._b.transform_path_non_affine( 2442 self._a.transform_path(path)) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/mpl/geoaxes.py:186, in InterProjectionTransform.transform_path_non_affine(self, src_path) 182 geoms = cpatch.path_to_geos(src_path, 183 getattr(self, 'force_path_ccw', False)) 185 for geom in geoms: --> 186 proj_geom = self.target_projection.project_geometry( 187 geom, self.source_projection) 188 transformed_geoms.append(proj_geom) 190 if not transformed_geoms: File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/crs.py:817, in Projection.project_geometry(self, geometry, src_crs) 815 if not method_name: 816 raise ValueError(f'Unsupported geometry type {geom_type!r}') --> 817 return getattr(self, method_name)(geometry, src_crs) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/crs.py:972, in Projection._project_polygon(self, polygon, src_crs) 968 rings.extend(self._attach_lines_to_boundary(multi_lines, is_ccw)) 970 # Resolve all the inside vs. outside rings, and convert to the 971 # final MultiPolygon. --> 972 return self._rings_to_multi_polygon(rings, is_ccw) File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/cartopy/crs.py:1233, in Projection._rings_to_multi_polygon(self, rings, is_ccw) 1230 polygon_bits.append(polygon) 1232 if polygon_bits: -> 1233 multi_poly = sgeom.MultiPolygon(polygon_bits) 1234 else: 1235 multi_poly = sgeom.MultiPolygon() File ~/miniconda3/envs/geocat-examples/lib/python3.11/site-packages/shapely/geometry/multipolygon.py:79, in MultiPolygon.__new__(self, polygons) 77 ob = polygons[i] 78 if not isinstance(ob, polygon.Polygon): ---> 79 shell = ob[0] 80 if len(ob) > 1: 81 holes = ob[1] TypeError: 'GeometryCollection' object is not subscriptable ```
guidoca commented 1 day ago

We are getting the same issue. Somehow a GeometryCollection appears. Modifying rings_to_multipolygon to skip any GeometryCollections fixes the issue.

It's probably the result of a weird geometry in the underlying contours, but impossible to tell exactly what when working with very big datasets.

I would suggest adding a mechanism to skip GeometrieCollection if present within polygon_bits and throw a warning, or recursively call rings_to_multipolygon with the GeometryCollection parts ?