1# Copyright Cartopy Contributors
2#
3# This file is part of Cartopy and is released under the LGPL license.
4# See COPYING and COPYING.LESSER in the root of the repository for full
5# licensing details.
6
7from unittest import mock
8
9from matplotlib.testing.decorators import cleanup
10import matplotlib.path as mpath
11import matplotlib.pyplot as plt
12import numpy as np
13import pytest
14
15import cartopy.crs as ccrs
16from cartopy.mpl.geoaxes import InterProjectionTransform, GeoAxes
17from cartopy.tests.mpl import ImageTesting
18from cartopy.tests.mpl.test_caching import CallCounter
19
20
21class TestNoSpherical:
22    def setup_method(self):
23        self.ax = plt.axes(projection=ccrs.PlateCarree())
24        self.data = np.arange(12).reshape((3, 4))
25
26    def teardown_method(self):
27        plt.clf()
28        plt.close()
29
30    def test_contour(self):
31        with pytest.raises(ValueError):
32            self.ax.contour(self.data, transform=ccrs.Geodetic())
33
34    def test_contourf(self):
35        with pytest.raises(ValueError):
36            self.ax.contourf(self.data, transform=ccrs.Geodetic())
37
38    def test_pcolor(self):
39        with pytest.raises(ValueError):
40            self.ax.pcolor(self.data, transform=ccrs.Geodetic())
41
42    def test_pcolormesh(self):
43        with pytest.raises(ValueError):
44            self.ax.pcolormesh(self.data, transform=ccrs.Geodetic())
45
46
47def test_transform_PlateCarree_shortcut():
48    src = ccrs.PlateCarree(central_longitude=0)
49    target = ccrs.PlateCarree(central_longitude=180)
50
51    # of the 3 paths, 2 of them cannot be short-cutted.
52    pth1 = mpath.Path([[0.5, 0], [10, 10]])
53    pth2 = mpath.Path([[0.5, 91], [10, 10]])
54    pth3 = mpath.Path([[-0.5, 0], [10, 10]])
55
56    trans = InterProjectionTransform(src, target)
57
58    counter = CallCounter(target, 'project_geometry')
59
60    with counter:
61        trans.transform_path(pth1)
62        # pth1 should allow a short-cut.
63        assert counter.count == 0
64
65    with counter:
66        trans.transform_path(pth2)
67        assert counter.count == 1
68
69    with counter:
70        trans.transform_path(pth3)
71        assert counter.count == 2
72
73
74class Test_InterProjectionTransform:
75    def pc_2_pc(self):
76        return InterProjectionTransform(
77            ccrs.PlateCarree(), ccrs.PlateCarree())
78
79    def pc_2_rob(self):
80        return InterProjectionTransform(ccrs.PlateCarree(), ccrs.Robinson())
81
82    def rob_2_rob_shifted(self):
83        return InterProjectionTransform(
84            ccrs.Robinson(), ccrs.Robinson(central_longitude=0))
85
86    def test_eq(self):
87        assert self.pc_2_pc() == self.pc_2_pc()
88        assert self.pc_2_rob() == self.pc_2_rob()
89        assert self.rob_2_rob_shifted() == self.rob_2_rob_shifted()
90
91        assert not self.pc_2_rob() == self.rob_2_rob_shifted()
92        assert not self.pc_2_pc() == 'not a transform obj'
93
94    def test_ne(self):
95        assert not self.pc_2_pc() != self.pc_2_pc()
96        print(self.pc_2_pc() != self.pc_2_rob())
97        assert self.pc_2_pc() != self.pc_2_rob()
98
99
100class Test_Axes_add_geometries:
101    def teardown_method(self):
102        plt.close()
103
104    @mock.patch('cartopy.mpl.geoaxes.GeoAxes.add_feature')
105    @mock.patch('cartopy.feature.ShapelyFeature')
106    def test_styler_kwarg(self, ShapelyFeature, add_feature_method):
107        ax = GeoAxes(plt.figure(), [0, 0, 1, 1],
108                     map_projection=ccrs.Robinson())
109        ax.add_geometries(mock.sentinel.geometries, mock.sentinel.crs,
110                          styler=mock.sentinel.styler, wibble='wobble')
111
112        ShapelyFeature.assert_called_once_with(
113            mock.sentinel.geometries, mock.sentinel.crs, wibble='wobble')
114
115        add_feature_method.assert_called_once_with(
116            ShapelyFeature(), styler=mock.sentinel.styler)
117
118
119@cleanup
120def test_geoaxes_subplot():
121    ax = plt.subplot(1, 1, 1, projection=ccrs.PlateCarree())
122    assert str(ax.__class__) == "<class 'cartopy.mpl.geoaxes.GeoAxesSubplot'>"
123
124
125@ImageTesting(['geoaxes_subslice'])
126def test_geoaxes_no_subslice():
127    """Test that we do not trigger matplotlib's line subslice optimization."""
128    # This behavior caused lines with > 1000 points and
129    # sorted data to disappear
130
131    fig, axes = plt.subplots(1, 2, subplot_kw={'projection': ccrs.Mercator()})
132    for ax, num_points in zip(axes, [1000, 1001]):
133        lats = np.linspace(35, 37, num_points)
134        lons = np.linspace(-117, -115, num_points)
135        ax.plot(lons, lats, transform=ccrs.PlateCarree())
136
137
138@ImageTesting(['geoaxes_set_boundary_clipping'])
139def test_geoaxes_set_boundary_clipping():
140    """Test that setting the boundary works properly for clipping #1620."""
141    lon, lat = np.meshgrid(np.linspace(-180., 180., 361),
142                           np.linspace(-90., -60., 31))
143    fig = plt.figure()
144    ax1 = fig.add_subplot(1, 1, 1, projection=ccrs.SouthPolarStereo())
145
146    # Limit the map to -60 degrees latitude and below.
147    ax1.set_extent([-180, 180, -90, -60], ccrs.PlateCarree())
148    ax1.gridlines()
149
150    ax1.contourf(lon, lat, lat, transform=ccrs.PlateCarree())
151
152    ax1.set_boundary(mpath.Path.circle(center=(0.5, 0.5), radius=0.5),
153                     transform=ax1.transAxes)
154