Repository: SciTools/cartopy

Issue:
Spines form incomplete junctions with some projections
### Description
<!-- Please provide a general introduction to the issue/proposal. -->
When using certain projections such as cartopy.crs.AlbersEqualArea or cartopy.crs.Orthographic along with changing the extent such that some of the map is clipped, the spines at one corner do not form a connection or properly align. This is evident if you increase the line width of the spines. The image below shows the result in the affected corner which I have observed to be in both the top right corner as well as the lower left corner. The code below produced the plot this image was taken from.
![Example](https://github.com/SciTools/cartopy/assets/12968902/e7835cee-a936-4aa5-bdb0-9cc041f5300a)

<!--
If you are reporting a bug, attach the *entire* traceback from Python.

If you are proposing an enhancement/new feature, provide links to related articles, reference examples, etc.

If you are asking a question, please ask on StackOverflow and use the cartopy tag. All cartopy
questions on StackOverflow can be found at https://stackoverflow.com/questions/tagged/cartopy
-->

#### Code to reproduce

```
import matplotlib.pyplot as plt
import cartopy.crs as ccrs

fig = plt.figure()
ax = plt.axes(projection=ccrs.Orthographic(0, 0))
ax.set_extent([-10, -11, 10, 11])
ax.spines[:].set_linewidth(3)
plt.savefig("test.pdf")
```



<details>
 <summary>Full environment definition</summary>

<!-- fill in the following information as appropriate -->

### Operating system
macOS 11.3.1

### Versions
Cartopy: 0.21.1
matplotlib: 3.7.1


</details>




Discussion:
It looks like the GeoSpine path isn't closed. @QuLogic do you know if there was a specific reason for not closing the path? It looks like you explicitly set the capstyle to "butt" in https://github.com/SciTools/cartopy/pull/1213
I suppose non-closure makes sense for normal Left/Right/Bottom/Top Spines, but are GeoSpines always loops and therefore closed?

@kerrycobb A workaround for your specific case, but I don't think is general, is to call `ax.spines[:].set_capstyle("projecting")`.
Thanks @greglucas for the work around! That's a good solution for my current case. 

I was thinking the same thing about the path not being closed because of the effect that I see if I set the joinstyle  to round `ax.spines[:].set_joinstyle("round")`. It changes the joinstyle of just 3 corners. 
I think that was just to match previous behaviour; the former `PathPatch` uses 'butt' cap style. Matplotlib's spines use 'projecting', so that would likely be more consistent. I would think that not closing the path is an oversight, but since that's used for the background as well, I'm a bit surprised that didn't seem to cause any other issues.

Resolving patch:
diff --git a/lib/cartopy/mpl/geoaxes.py b/lib/cartopy/mpl/geoaxes.py
--- a/lib/cartopy/mpl/geoaxes.py
+++ b/lib/cartopy/mpl/geoaxes.py
@@ -262,7 +262,6 @@ def __init__(self, axes, **kwargs):
         self._original_path = mpath.Path(np.empty((0, 2)))
         kwargs.setdefault('clip_on', False)
         super().__init__(axes, 'geo', self._original_path, **kwargs)
-        self.set_capstyle('butt')
 
     def set_boundary(self, path, transform):
         self._original_path = path
@@ -272,6 +271,7 @@ def set_boundary(self, path, transform):
     def _adjust_location(self):
         if self.stale:
             self._path = self._original_path.clip_to_bbox(self.axes.viewLim)
+            self._path = mpath.Path(self._path.vertices, closed=True)
 
     def get_window_extent(self, renderer=None):
         # make sure the location is updated so that transforms etc are


Test patch:
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Aitoff.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Aitoff.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Aitoff.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Aitoff.png differ
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertV.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertV.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertV.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertV.png differ
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertVI.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertVI.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertVI.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_EckertVI.png differ
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Hammer.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Hammer.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Hammer.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_Hammer.png differ
diff --git a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_InterruptedGoodeHomolosine.png b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_InterruptedGoodeHomolosine.png
Binary files a/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_InterruptedGoodeHomolosine.png and b/lib/cartopy/tests/mpl/baseline_images/mpl/test_mpl_integration/test_global_map_InterruptedGoodeHomolosine.png differ
diff --git a/lib/cartopy/tests/mpl/test_images.py b/lib/cartopy/tests/mpl/test_images.py
--- a/lib/cartopy/tests/mpl/test_images.py
+++ b/lib/cartopy/tests/mpl/test_images.py
@@ -69,7 +69,7 @@ def test_web_tiles():
 
 @pytest.mark.natural_earth
 @pytest.mark.network
-@pytest.mark.mpl_image_compare(filename='image_merge.png', tolerance=0.01)
+@pytest.mark.mpl_image_compare(filename='image_merge.png', tolerance=0.03)
 def test_image_merge():
     # tests the basic image merging functionality
     tiles = []
diff --git a/lib/cartopy/tests/mpl/test_web_services.py b/lib/cartopy/tests/mpl/test_web_services.py
--- a/lib/cartopy/tests/mpl/test_web_services.py
+++ b/lib/cartopy/tests/mpl/test_web_services.py
@@ -14,7 +14,7 @@
 @pytest.mark.filterwarnings("ignore:TileMatrixLimits")
 @pytest.mark.network
 @pytest.mark.skipif(not _OWSLIB_AVAILABLE, reason='OWSLib is unavailable.')
-@pytest.mark.mpl_image_compare(filename='wmts.png', tolerance=0)
+@pytest.mark.mpl_image_compare(filename='wmts.png', tolerance=0.03)
 def test_wmts():
     ax = plt.axes(projection=ccrs.PlateCarree())
     url = 'https://map1c.vis.earthdata.nasa.gov/wmts-geo/wmts.cgi'