cartopy
cartopy copied to clipboard
Problem with annotation in image
Description
Hello, I encountered a problem where text will not be cut off properly in cartopy at the edge of the figure. This is a simplyfied version of the problem, but it should be obvious what is going wrong. You can clearly see that the higher numbers (90+) are still displayed, even though they are outside of the image. This is a problem coming from the projection. I encounter the same problem in my "real" code where I have river names on a map. The problem also happens when you zoom in and it display text that is outside of the figure.
Code to reproduce
import matplotlib.pyplot as plt
import numpy as np
import cartopy.crs as ccrs
if __name__ == '__main__':
x = np.arange(100)
fig = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
scatter_plot=plt.scatter(x, x)
for item in x:
plt.annotate(str(item), (item, item))
plt.show()
Traceback
There is no error message. Problem is purely optical.
Operating system
Windows 10
Cartopy version
0.21.0
conda list
# Name Version Build Channel
adjusttext 0.7.3.1 py_1 conda-forge
arrow-cpp 9.0.0 py39h07ee6b1_6_cpu conda-forge
asciitree 0.3.3 py_2 conda-forge
aws-c-cal 0.5.11 he19cf47_0 conda-forge
aws-c-common 0.6.2 h8ffe710_0 conda-forge
aws-c-event-stream 0.2.7 h70e1b0c_13 conda-forge
aws-c-io 0.10.5 h2fe331c_0 conda-forge
aws-checksums 0.1.11 h1e232aa_7 conda-forge
aws-sdk-cpp 1.8.186 hb0612c5_3 conda-forge
brotli 1.0.9 h8ffe710_7 conda-forge
brotli-bin 1.0.9 h8ffe710_7 conda-forge
brotlipy 0.7.0 py39hb82d6ee_1004 conda-forge
bzip2 1.0.8 h8ffe710_4 conda-forge
c-ares 1.18.1 h8ffe710_0 conda-forge
ca-certificates 2022.9.24 h5b45459_0 conda-forge
cartopy 0.21.0 py39h4915f10_0 conda-forge
certifi 2022.9.24 pyhd8ed1ab_0 conda-forge
cffi 1.15.1 py39h0878f49_0 conda-forge
cftime 1.6.2 py39hc266a54_0 conda-forge
charset-normalizer 2.1.1 pyhd8ed1ab_0 conda-forge
colorama 0.4.5 pyhd8ed1ab_0 conda-forge
conda 22.9.0 py39hcbf5309_1 conda-forge
conda-package-handling 1.9.0 py39h09fa780_0 conda-forge
console_shortcut 0.1.1 4
contourpy 1.0.5 py39h1f6ef14_0 conda-forge
cryptography 38.0.1 py39h58e9bdb_0 conda-forge
curl 7.85.0 heaf79c2_0 conda-forge
cycler 0.11.0 pyhd8ed1ab_0 conda-forge
entrypoints 0.4 pyhd8ed1ab_0 conda-forge
eofs 1.4.0 py_0 conda-forge
fasteners 0.17.3 pyhd8ed1ab_0 conda-forge
fonttools 4.37.4 py39ha55989b_0 conda-forge
freetype 2.12.1 h546665d_0 conda-forge
geos 3.11.0 h39d44d4_0 conda-forge
gettext 0.19.8.1 h5728263_1009 conda-forge
gflags 2.2.2 ha925a31_1004 conda-forge
glib 2.74.0 h12be248_0 conda-forge
glib-tools 2.74.0 h12be248_0 conda-forge
glog 0.6.0 h4797de2_0 conda-forge
grpc-cpp 1.47.1 h535cfc9_6 conda-forge
gst-plugins-base 1.20.3 h001b923_2 conda-forge
gstreamer 1.20.3 h6b5321d_2 conda-forge
hdf4 4.2.15 h0e5069d_4 conda-forge
hdf5 1.12.2 nompi_h2a0e4a3_100 conda-forge
icu 70.1 h0e60522_0 conda-forge
idna 3.4 pyhd8ed1ab_0 conda-forge
imageio 2.22.0 pyhfa7a67d_0 conda-forge
intel-openmp 2022.1.0 h57928b3_3787 conda-forge
joblib 1.2.0 pyhd8ed1ab_0 conda-forge
jpeg 9e h8ffe710_2 conda-forge
kiwisolver 1.4.4 py39h2e07f2f_0 conda-forge
krb5 1.19.3 h1176d77_0 conda-forge
lcms2 2.12 h2a16943_0 conda-forge
lerc 4.0.0 h63175ca_0 conda-forge
libabseil 20220623.0 cxx17_h1a56200_4 conda-forge
libblas 3.9.0 16_win64_mkl conda-forge
libbrotlicommon 1.0.9 h8ffe710_7 conda-forge
libbrotlidec 1.0.9 h8ffe710_7 conda-forge
libbrotlienc 1.0.9 h8ffe710_7 conda-forge
libcblas 3.9.0 16_win64_mkl conda-forge
libclang 14.0.6 default_h77d9078_0 conda-forge
libclang13 14.0.6 default_h77d9078_0 conda-forge
libcrc32c 1.1.2 h0e60522_0 conda-forge
libcurl 7.85.0 heaf79c2_0 conda-forge
libdeflate 1.14 hcfcfb64_0 conda-forge
libffi 3.4.2 h8ffe710_5 conda-forge
libglib 2.74.0 h79619a9_0 conda-forge
libgoogle-cloud 2.2.0 hc8dde07_1 conda-forge
libiconv 1.17 h8ffe710_0 conda-forge
liblapack 3.9.0 16_win64_mkl conda-forge
libnetcdf 4.8.1 nompi_h85765be_104 conda-forge
libogg 1.3.4 h8ffe710_1 conda-forge
libpng 1.6.38 h19919ed_0 conda-forge
libprotobuf 3.21.7 h12be248_0 conda-forge
libsqlite 3.39.4 hcfcfb64_0 conda-forge
libssh2 1.10.0 h680486a_3 conda-forge
libthrift 0.16.0 h9f558f2_2 conda-forge
libtiff 4.4.0 h8e97e67_4 conda-forge
libutf8proc 2.7.0 hcb41399_0 conda-forge
libvorbis 1.3.7 h0e60522_0 conda-forge
libwebp-base 1.2.4 h8ffe710_0 conda-forge
libxcb 1.13 hcd874cb_1004 conda-forge
libzip 1.9.2 hfed4ece_1 conda-forge
libzlib 1.2.12 hcfcfb64_4 conda-forge
lz4-c 1.9.3 h8ffe710_1 conda-forge
m2w64-gcc-libgfortran 5.3.0 6 conda-forge
m2w64-gcc-libs 5.3.0 7 conda-forge
m2w64-gcc-libs-core 5.3.0 7 conda-forge
m2w64-gmp 6.1.0 2 conda-forge
m2w64-libwinpthread-git 5.0.0.4634.697f757 2 conda-forge
matplotlib 3.6.0 py39hcbf5309_0 conda-forge
matplotlib-base 3.6.0 py39haf65ace_0 conda-forge
menuinst 1.4.19 py39hcbf5309_0 conda-forge
mkl 2022.1.0 h6a75c08_874 conda-forge
mplcursors 0.5.1 pyhd8ed1ab_0 conda-forge
mpldatacursor 0.7.1 pyhd8ed1ab_0 conda-forge
msgpack-python 1.0.4 py39h2e07f2f_0 conda-forge
msys2-conda-epoch 20160418 1 conda-forge
munkres 1.1.4 pyh9f0ad1d_0 conda-forge
netcdf4 1.6.1 nompi_py39h34fa13a_100 conda-forge
numcodecs 0.10.2 py39h415ef7b_0 conda-forge
numpy 1.23.3 py39h9061af7_0 conda-forge
openjpeg 2.5.0 hc9384bd_1 conda-forge
openssl 1.1.1q h8ffe710_0 conda-forge
packaging 21.3 pyhd8ed1ab_0 conda-forge
pandas 1.5.0 py39h2ba5b7c_0 conda-forge
parquet-cpp 1.5.1 2 conda-forge
pcre2 10.37 hdfff0fc_1 conda-forge
pillow 9.2.0 py39hcef8f5f_2 conda-forge
pip 22.2.2 pyhd8ed1ab_0 conda-forge
plotly 5.10.0 pyhd8ed1ab_0 conda-forge
ply 3.11 py_1 conda-forge
powershell_shortcut 0.0.1 3
proj 9.1.0 h3863b3b_0 conda-forge
pthread-stubs 0.4 hcd874cb_1001 conda-forge
pyarrow 9.0.0 py39h2c50fde_6_cpu conda-forge
pycosat 0.6.3 py39hb82d6ee_1010 conda-forge
pycparser 2.21 pyhd8ed1ab_0 conda-forge
pyopenssl 22.0.0 pyhd8ed1ab_1 conda-forge
pyparsing 3.0.9 pyhd8ed1ab_0 conda-forge
pyproj 3.4.0 py39haa55e60_1 conda-forge
pyqt 5.15.7 py39hb08f45d_0 conda-forge
pyqt5-sip 12.11.0 py39h415ef7b_0 conda-forge
pyshp 2.3.1 pyhd8ed1ab_0 conda-forge
pysocks 1.7.1 pyh0701188_6 conda-forge
python 3.9.13 h9a09f29_0_cpython conda-forge
python-dateutil 2.8.2 pyhd8ed1ab_0 conda-forge
python_abi 3.9 2_cp39 conda-forge
pytz 2022.4 pyhd8ed1ab_0 conda-forge
qt-main 5.15.6 hf0cf448_0 conda-forge
re2 2022.06.01 h0e60522_0 conda-forge
requests 2.28.1 pyhd8ed1ab_1 conda-forge
ruamel_yaml 0.15.80 py39hb82d6ee_1007 conda-forge
scipy 1.9.1 py39h316f440_0 conda-forge
setuptools 65.4.1 pyhd8ed1ab_0 conda-forge
shapely 1.8.4 py39he0923fe_0 conda-forge
sip 6.6.2 py39h415ef7b_0 conda-forge
six 1.16.0 pyh6c4a22f_0 conda-forge
snappy 1.1.9 h82413e6_1 conda-forge
sqlite 3.39.4 hcfcfb64_0 conda-forge
tbb 2021.6.0 h91493d7_0 conda-forge
tenacity 8.1.0 pyhd8ed1ab_0 conda-forge
tk 8.6.12 h8ffe710_0 conda-forge
toml 0.10.2 pyhd8ed1ab_0 conda-forge
toolz 0.12.0 pyhd8ed1ab_0 conda-forge
tornado 6.2 py39hb82d6ee_0 conda-forge
tqdm 4.64.1 pyhd8ed1ab_0 conda-forge
typing-extensions 4.3.0 hd8ed1ab_0 conda-forge
typing_extensions 4.3.0 pyha770c72_0 conda-forge
tzdata 2022d h191b570_0 conda-forge
ucrt 10.0.20348.0 h57928b3_0 conda-forge
unicodedata2 14.0.0 py39hb82d6ee_1 conda-forge
urllib3 1.26.11 pyhd8ed1ab_0 conda-forge
vc 14.2 hac3ee0b_8 conda-forge
vs2015_runtime 14.29.30139 h890b9b1_8 conda-forge
wheel 0.37.1 pyhd8ed1ab_0 conda-forge
win_inet_pton 1.1.0 py39hcbf5309_4 conda-forge
xarray 2022.9.0 pyhd8ed1ab_0 conda-forge
xorg-libxau 1.0.9 hcd874cb_0 conda-forge
xorg-libxdmcp 1.1.3 hcd874cb_0 conda-forge
xz 5.2.6 h8d14728_0 conda-forge
yaml 0.2.5 h8ffe710_2 conda-forge
zarr 2.13.2 pyhd8ed1ab_1 conda-forge
zlib 1.2.12 hcfcfb64_4 conda-forge
zstd 1.5.2 h7755175_4 conda-forge
This is a duplicate of #1558. The short answer is this is a problem with clipping using paths upstream in Matplotlib. See matplotlib/matplotlib#8270. There's nothing we can do about it in Cartopy.
I would like to point out that it worked fine until recently (I know that it worked under cartopy 0.20), so either the cartopy update or matplotlib update caused trouble for me. Here are the last changes
2022-09-14 11:56:27 (rev 19)
arrow-cpp {9.0.0 (conda-forge/win-64) -> 9.0.0 (conda-forge/win-64)}
cartopy {0.20.3 (conda-forge/win-64) -> 0.21.0 (conda-forge/win-64)}
charset-normalizer {2.1.0 (conda-forge/noarch) -> 2.1.1 (conda-forge/noarch)}
fonttools {4.34.4 (conda-forge/win-64) -> 4.37.1 (conda-forge/win-64)}
freetype {2.10.4 (conda-forge/win-64) -> 2.12.1 (conda-forge/win-64)}
grpc-cpp {1.46.4 (conda-forge/win-64) -> 1.47.1 (conda-forge/win-64)}
gst-plugins-base {1.20.3 (conda-forge/win-64) -> 1.20.3 (conda-forge/win-64)}
gstreamer {1.20.3 (conda-forge/win-64) -> 1.20.3 (conda-forge/win-64)}
hdf4 {4.2.15 (conda-forge/win-64) -> 4.2.15 (conda-forge/win-64)}
imageio {2.21.1 (conda-forge/noarch) -> 2.21.3 (conda-forge/noarch)}
libdeflate {1.13 (conda-forge/win-64) -> 1.14 (conda-forge/win-64)}
libgoogle-cloud {1.40.2 (conda-forge/win-64) -> 2.2.0 (conda-forge/win-64)}
libnetcdf {4.8.1 (conda-forge/win-64) -> 4.8.1 (conda-forge/win-64)}
libpng {1.6.37 (conda-forge/win-64) -> 1.6.37 (conda-forge/win-64)}
libprotobuf {3.20.1 (conda-forge/win-64) -> 3.21.5 (conda-forge/win-64)}
libssh2 {1.10.0 (conda-forge/win-64) -> 1.10.0 (conda-forge/win-64)}
libthrift {0.16.0 (conda-forge/win-64) -> 0.16.0 (conda-forge/win-64)}
libtiff {4.4.0 (conda-forge/win-64) -> 4.4.0 (conda-forge/win-64)}
libwebp-base {1.2.3 (conda-forge/win-64) -> 1.2.4 (conda-forge/win-64)}
libzip {1.9.2 (conda-forge/win-64) -> 1.9.2 (conda-forge/win-64)}
libzlib {1.2.12 (conda-forge/win-64) -> 1.2.12 (conda-forge/win-64)}
matplotlib {3.5.2 (conda-forge/win-64) -> 3.5.3 (conda-forge/win-64)}
matplotlib-base {3.5.2 (conda-forge/win-64) -> 3.5.3 (conda-forge/win-64)}
menuinst {1.4.18 (conda-forge/win-64) -> 1.4.19 (conda-forge/win-64)}
netcdf4 {1.6.0 (conda-forge/win-64) -> 1.6.0 (conda-forge/win-64)}
numpy {1.23.1 (conda-forge/win-64) -> 1.23.3 (conda-forge/win-64)}
openjpeg {2.4.0 (conda-forge/win-64) -> 2.5.0 (conda-forge/win-64)}
pandas {1.4.3 (conda-forge/win-64) -> 1.4.4 (conda-forge/win-64)}
pillow {9.2.0 (conda-forge/win-64) -> 9.2.0 (conda-forge/win-64)}
plotly {5.9.0 (conda-forge/noarch) -> 5.10.0 (conda-forge/noarch)}
pyarrow {9.0.0 (conda-forge/win-64) -> 9.0.0 (conda-forge/win-64)}
pyproj {3.3.1 (conda-forge/win-64) -> 3.4.0 (conda-forge/win-64)}
pysocks {1.7.1 (conda-forge/win-64) -> 1.7.1 (conda-forge/noarch)}
pytz {2022.1 (conda-forge/noarch) -> 2022.2.1 (conda-forge/noarch)}
requests {2.28.1 (conda-forge/noarch) -> 2.28.1 (conda-forge/noarch)}
scipy {1.9.0 (conda-forge/win-64) -> 1.9.1 (conda-forge/win-64)}
setuptools {64.0.0 (conda-forge/win-64) -> 65.3.0 (conda-forge/noarch)}
shapely {1.8.2 (conda-forge/win-64) -> 1.8.4 (conda-forge/win-64)}
tbb {2021.5.0 (conda-forge/win-64) -> 2021.5.0 (conda-forge/win-64)}
tqdm {4.64.0 (conda-forge/noarch) -> 4.64.1 (conda-forge/noarch)}
tzdata {2022b (conda-forge/noarch) -> 2022c (conda-forge/noarch)}
vc {14.2 (conda-forge/win-64) -> 14.2 (conda-forge/win-64)}
vs2015_runtime {14.29.30037 (conda-forge/win-64) -> 14.29.30139 (conda-forge/win-64)}
xz {5.2.5 (conda-forge/win-64) -> 5.2.6 (conda-forge/win-64)}
zlib {1.2.12 (conda-forge/win-64) -> 1.2.12 (conda-forge/win-64)}
zstd {1.5.2 (conda-forge/win-64) -> 1.5.2 (conda-forge/win-64)}
-abseil-cpp-20211102.0 (conda-forge/win-64)
-libabseil-static-20211102.0 (conda-forge/win-64)
-libwebp-1.2.3 (conda-forge/win-64)
-pywin32-303 (conda-forge/win-64)
+libabseil-20220623.0 (conda-forge/win-64)
2022-10-04 09:42:59 (rev 20)
ca-certificates {2022.6.15.2 (conda-forge/win-64) -> 2022.9.24 (conda-forge/win-64)}
certifi {2022.6.15.2 (conda-forge/noarch) -> 2022.9.24 (conda-forge/noarch)}
cftime {1.6.1 (conda-forge/win-64) -> 1.6.2 (conda-forge/win-64)}
conda {4.14.0 (conda-forge/win-64) -> 22.9.0 (conda-forge/win-64)}
conda-package-handling {1.8.1 (conda-forge/win-64) -> 1.9.0 (conda-forge/win-64)}
curl {7.83.1 (conda-forge/win-64) -> 7.85.0 (conda-forge/win-64)}
fonttools {4.37.1 (conda-forge/win-64) -> 4.37.4 (conda-forge/win-64)}
gettext {0.19.8.1 (conda-forge/win-64) -> 0.19.8.1 (conda-forge/win-64)}
glib {2.72.1 (conda-forge/win-64) -> 2.74.0 (conda-forge/win-64)}
glib-tools {2.72.1 (conda-forge/win-64) -> 2.74.0 (conda-forge/win-64)}
gst-plugins-base {1.20.3 (conda-forge/win-64) -> 1.20.3 (conda-forge/win-64)}
gstreamer {1.20.3 (conda-forge/win-64) -> 1.20.3 (conda-forge/win-64)}
idna {3.3 (conda-forge/noarch) -> 3.4 (conda-forge/noarch)}
imageio {2.21.3 (conda-forge/noarch) -> 2.22.0 (conda-forge/noarch)}
joblib {1.1.0 (conda-forge/noarch) -> 1.2.0 (conda-forge/noarch)}
libcurl {7.83.1 (conda-forge/win-64) -> 7.85.0 (conda-forge/win-64)}
libglib {2.72.1 (conda-forge/win-64) -> 2.74.0 (conda-forge/win-64)}
libgoogle-cloud {2.2.0 (conda-forge/win-64) -> 2.2.0 (conda-forge/win-64)}
libiconv {1.16 (conda-forge/win-64) -> 1.17 (conda-forge/win-64)}
libpng {1.6.37 (conda-forge/win-64) -> 1.6.38 (conda-forge/win-64)}
libprotobuf {3.21.5 (conda-forge/win-64) -> 3.21.7 (conda-forge/win-64)}
libzlib {1.2.12 (conda-forge/win-64) -> 1.2.12 (conda-forge/win-64)}
matplotlib {3.5.3 (conda-forge/win-64) -> 3.6.0 (conda-forge/win-64)}
matplotlib-base {3.5.3 (conda-forge/win-64) -> 3.6.0 (conda-forge/win-64)}
mpldatacursor {0.7.1 (bjornfjohansson/noarch) -> 0.7.1 (conda-forge/noarch)}
netcdf4 {1.6.0 (conda-forge/win-64) -> 1.6.1 (conda-forge/win-64)}
pandas {1.4.4 (conda-forge/win-64) -> 1.5.0 (conda-forge/win-64)}
proj {9.0.1 (conda-forge/win-64) -> 9.1.0 (conda-forge/win-64)}
pyopenssl {22.0.0 (conda-forge/noarch) -> 22.0.0 (conda-forge/noarch)}
pyproj {3.4.0 (conda-forge/win-64) -> 3.4.0 (conda-forge/win-64)}
pytz {2022.2.1 (conda-forge/noarch) -> 2022.4 (conda-forge/noarch)}
qt-main {5.15.4 (conda-forge/win-64) -> 5.15.6 (conda-forge/win-64)}
setuptools {65.3.0 (conda-forge/noarch) -> 65.4.1 (conda-forge/noarch)}
sqlite {3.39.2 (conda-forge/win-64) -> 3.39.4 (conda-forge/win-64)}
tbb {2021.5.0 (conda-forge/win-64) -> 2021.6.0 (conda-forge/win-64)}
tenacity {8.0.1 (conda-forge/noarch) -> 8.1.0 (conda-forge/noarch)}
tzdata {2022c (conda-forge/noarch) -> 2022d (conda-forge/noarch)}
xarray {2022.6.0 (conda-forge/noarch) -> 2022.9.0 (conda-forge/noarch)}
zarr {2.12.0 (conda-forge/noarch) -> 2.13.2 (conda-forge/noarch)}
zlib {1.2.12 (conda-forge/win-64) -> 1.2.12 (conda-forge/win-64)}
-pcre-8.45 (conda-forge/win-64)
+contourpy-1.0.5 (conda-forge/win-64)
+libsqlite-3.39.4 (conda-forge/win-64)
+pcre2-10.37 (conda-forge/win-64)
Apologies, apparently while this looks a lot like the known text clipping issue, I can confirm that the example code works properly on 0.20.3 and produces the output above with 0.21, both running with Matplotlib 3.5.3.
So my guess is that the clipping issue is still at play somehow in producing the current behavior.
The change that broke it is #2065, which added a wrapper for annotate
that handles transforms.
Clipping works fine if you avoid CartoPy's wrapper and call the Axes
method directly:
import matplotlib.pyplot as plt
from matplotlib.axes import Axes
import numpy as np
import cartopy.crs as ccrs
if __name__ == '__main__':
x = np.arange(100)
fig = plt.figure()
ax = plt.axes(projection=ccrs.PlateCarree())
scatter_plot=plt.scatter(x, x)
for item in x:
Axes.annotate(ax, str(item), (item, item))
plt.show()
I think still it's likely that there's nothing we can do here, but I'm leaving this open until I (or someone else) can confirm why just setting up the transforms is enough to trigger this. There's nothing obvious in CartoPy's annotate()
implementation that should yield this difference.
Just a quick note, the lack of clipping was present in 0.20.3 if a transform was applied. It's only clipped as expected if you relied on the semi-"broken" implementation and got lucky with a plain PlateCaree base projection.
x = np.arange(100)
fig = plt.figure()
map_proj = ccrs.PlateCarree()
ax = plt.axes(projection=map_proj)
transform = ccrs.PlateCarree()._as_mpl_transform(ax)
for item in x:
ax.annotate(f"{item}", (item, item), xycoords=transform)
ax.set_extent((0, 100, 0, 90))
ax.set_title(f"{cartopy.__version__}")
Well actually not a quick note, I did some digging and I think this is partially by design. Adding annotation_clip=True
to your ax.annotate()
will get pretty much the result you want. See the last kwarg from the matplolib.annotate docs
annotation_clipbool or None, default: None Whether to clip (i.e. not draw) the annotation when the annotation point xy is outside the axes area.
- If True, the annotation will be clipped when xy is outside the axes.
- If False, the annotation will always be drawn.
- If None, the annotation will be clipped when xy is outside the axes and xycoords is 'data'.
Transformed cartopy annotations aren't in mpl 'data' space so they are not clipped by default, but setting annotation_clip=True
with correct transformations produces the expected results:
I think there's a good argument to set annotation_clip=True
in the GeoAxes.annotate
routine for cases where the coords are in 'data' space on the cartopy side, I think that would match the mpl default behavior closer? There's a long discussion about clip_on vs annotation_clip at in the matplotlib issue #14354, so aligning with the current default and expected behaviour from that would be good.