Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -140,12 +140,14 @@ jobs:
key: ${{ runner.os }}-pip-tests-${{ hashFiles('ci/*.txt') }}
restore-keys: ${{ runner.os }}-pip-tests

# This installs the stuff needed to build and install Shapely and Cartopy from source
# This installs the stuff needed to build and install Shapely and CartoPy from source.
# Need to install numpy first to make CartoPy happy.
- name: Install dependencies
run: |
sudo apt-get install libgeos-dev libproj-dev
python -m pip install --upgrade pip setuptools
python -m pip install --no-binary :all: shapely
python -m pip install -c ci/${{ matrix.dep-versions }} numpy
python -m pip install -r ci/test_requirements.txt -r ci/extra_requirements.txt -c ci/${{ matrix.dep-versions }} ${{ matrix.git-versions }}

- name: Install
Expand Down Expand Up @@ -227,13 +229,15 @@ jobs:
key: ${{ runner.os }}-pip-docs-${{ hashFiles('ci/*.txt') }}
restore-keys: ${{ runner.os }}-pip-docs

# This installs the stuff needed to build and install Shapely and Cartopy from source
# This installs the stuff needed to build and install Shapely and CartoPy from source.
# Need to install numpy first to make CartoPy happy.
- name: Install dependencies (PyPI)
if: ${{ runner.os == 'Linux' }}
run: |
sudo apt-get install libgeos-dev libproj-dev
python -m pip install --upgrade pip setuptools
python -m pip install --no-binary :all: shapely
python -m pip install -c ci/${{ matrix.dep-versions }} numpy
python -m pip install -r ci/doc_requirements.txt -r ci/extra_requirements.txt -c ci/${{ matrix.dep-versions }} ${{ matrix.git-versions }}
python -m pip install -f https://unidata-python.s3.amazonaws.com/wheelhouse/index.html sphinx_rtd_theme==0.2.5b1.post1

Expand Down
2 changes: 1 addition & 1 deletion ci/extra_requirements.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
cartopy==0.17.0
cartopy==0.18.0
pyproj==2.6.1.post1
6 changes: 6 additions & 0 deletions src/metpy/plots/_mpl.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ def scattertext(self, x, y, texts, loc=(0, 0), **kw):

# Add it to the axes and update range
self.add_artist(text_obj)

# Matplotlib at least up to 3.2.2 does not properly clip text with paths, so
# work-around by setting to the bounding box of the Axes
# TODO: Remove when fixed in our minimum supported version of matplotlib
text_obj.clipbox = self.bbox

self.update_datalim(text_obj.get_datalim(self.transData))
self.autoscale_view()
return text_obj
Expand Down
21 changes: 16 additions & 5 deletions src/metpy/plots/cartopy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,39 @@
# SPDX-License-Identifier: BSD-3-Clause
"""Cartopy specific mapping utilities."""

import cartopy.crs as ccrs
import cartopy.feature as cfeature

from ..cbook import get_test_data


class MetPyMapFeature(cfeature.NaturalEarthFeature):
"""A simple interface to US County shapefiles."""
class MetPyMapFeature(cfeature.Feature):
"""A simple interface to MetPy-included shapefiles."""

def __init__(self, name, scale, **kwargs):
"""Create USCountiesFeature instance."""
super().__init__('', name, scale, **kwargs)
"""Create MetPyMapFeature instance."""
super().__init__(ccrs.PlateCarree(), **kwargs)
self.name = name

if isinstance(scale, str):
scale = cfeature.Scaler(scale)
self.scaler = scale

def geometries(self):
"""Return an iterator of (shapely) geometries for this feature."""
import cartopy.io.shapereader as shapereader
# Ensure that the associated files are in the cache
fname = '{}_{}'.format(self.name, self.scale)
fname = '{}_{}'.format(self.name, self.scaler.scale)
for extension in ['.dbf', '.shx']:
get_test_data(fname + extension)
path = get_test_data(fname + '.shp', as_file_obj=False)
return iter(tuple(shapereader.Reader(path).geometries()))

def intersecting_geometries(self, extent):
"""Return geometries that intersect the extent."""
self.scaler.scale_from_extent(extent)
return super().intersecting_geometries(extent)

def with_scale(self, new_scale):
"""
Return a copy of the feature with a new scale.
Expand Down
Binary file modified tests/plots/baseline/test_arrow_projection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_barb_projection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_colorfill.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_colorfill_horiz_colorbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_colorfill_no_colorbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_declarative_contour_convert_units.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified tests/plots/baseline/test_declarative_contour_options.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 6 additions & 3 deletions tests/plots/test_cartopy_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
MPL_VERSION = matplotlib.__version__[:3]


@pytest.mark.mpl_image_compare(tolerance=0.053, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance={'2.1': 0.161}.get(MPL_VERSION, 0.053),
remove_text=True)
def test_us_county_defaults():
"""Test the default US county plotting."""
proj = ccrs.LambertConformal(central_longitude=-85.0, central_latitude=45.0)
Expand All @@ -27,7 +28,8 @@ def test_us_county_defaults():
return fig


@pytest.mark.mpl_image_compare(tolerance=0.092, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance={'2.1': 0.1994}.get(MPL_VERSION, 0.092),
remove_text=True)
def test_us_county_scales():
"""Test US county plotting with all scales."""
proj = ccrs.LambertConformal(central_longitude=-85.0, central_latitude=45.0)
Expand Down Expand Up @@ -55,7 +57,8 @@ def test_us_states_defaults():
return fig


@pytest.mark.mpl_image_compare(tolerance=0.092, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance={'2.1': 0.991}.get(MPL_VERSION, 0.092),
remove_text=True)
def test_us_states_scales():
"""Test the default US States plotting with all scales."""
proj = ccrs.LambertConformal(central_longitude=-85.0, central_latitude=45.0)
Expand Down
52 changes: 38 additions & 14 deletions tests/plots/test_declarative.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import matplotlib
import numpy as np
import pandas as pd
import pytest
from traitlets import TraitError
Expand All @@ -27,8 +28,7 @@
MPL_VERSION = matplotlib.__version__[:3]


@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.0': 3.09}.get(MPL_VERSION, 0.005))
@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.005)
def test_declarative_image():
"""Test making an image plot."""
data = xr.open_dataset(GiniFile(get_test_data('NHEM-MULTICOMP_1km_IR_20151208_2100.gini')))
Expand All @@ -50,7 +50,8 @@ def test_declarative_image():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.022)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.256}.get(MPL_VERSION, 0.022))
def test_declarative_contour():
"""Test making a contour plot."""
data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False))
Expand All @@ -77,8 +78,23 @@ def test_declarative_contour():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.035)
def test_declarative_contour_options():
@pytest.fixture
def fix_is_closed_polygon(monkeypatch):
"""Fix matplotlib.contour._is_closed_polygons for tests.

Needed because for Matplotlib<3.3, the internal matplotlib.contour._is_closed_polygon
uses strict floating point equality. This causes the test below to yield different
results for macOS vs. Linux/Windows.

"""
monkeypatch.setattr(matplotlib.contour, '_is_closed_polygon',
lambda X: np.allclose(X[0], X[-1], rtol=1e-10, atol=1e-13),
raising=False)


@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 5.477}.get(MPL_VERSION, 0.035))
def test_declarative_contour_options(fix_is_closed_polygon):
"""Test making a contour plot."""
data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False))

Expand Down Expand Up @@ -106,8 +122,9 @@ def test_declarative_contour_options():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.035)
def test_declarative_contour_convert_units():
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 2.007}.get(MPL_VERSION, 0.035))
def test_declarative_contour_convert_units(fix_is_closed_polygon):
"""Test making a contour plot."""
data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False))

Expand Down Expand Up @@ -275,7 +292,8 @@ def test_colorfill_horiz_colorbar():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.016)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.355}.get(MPL_VERSION, 0.016))
def test_colorfill_no_colorbar():
"""Test that we can use ContourFillPlot."""
data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False))
Expand Down Expand Up @@ -352,7 +370,8 @@ def test_latlon():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.37)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.418}.get(MPL_VERSION, 0.37))
def test_declarative_barb_options():
"""Test making a contour plot."""
data = xr.open_dataset(get_test_data('narr_example.nc', as_file_obj=False))
Expand Down Expand Up @@ -380,7 +399,8 @@ def test_declarative_barb_options():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.612)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.819}.get(MPL_VERSION, 0.612))
def test_declarative_barb_earth_relative():
"""Test making a contour plot."""
import numpy as np
Expand Down Expand Up @@ -474,7 +494,8 @@ def test_declarative_barb_gfs_knots():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.022)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.407}.get(MPL_VERSION, 0.022))
def test_declarative_sfc_obs():
"""Test making a surface observation plot."""
data = pd.read_csv(get_test_data('SFC_obs.csv', as_file_obj=False),
Expand Down Expand Up @@ -506,7 +527,8 @@ def test_declarative_sfc_obs():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0.022)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.407}.get(MPL_VERSION, 0.022))
def test_declarative_sfc_obs_changes():
"""Test making a surface observation plot, changing the field."""
data = pd.read_csv(get_test_data('SFC_obs.csv', as_file_obj=False),
Expand Down Expand Up @@ -542,7 +564,8 @@ def test_declarative_sfc_obs_changes():
return pc.figure


@pytest.mark.mpl_image_compare(remove_text=True, tolerance=0)
@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'2.1': 0.378}.get(MPL_VERSION, 0.00586))
def test_declarative_colored_barbs():
"""Test making a surface plot with a colored barb (gh-1274)."""
data = pd.read_csv(get_test_data('SFC_obs.csv', as_file_obj=False),
Expand Down Expand Up @@ -575,7 +598,8 @@ def test_declarative_colored_barbs():


@pytest.mark.mpl_image_compare(remove_text=True,
tolerance={'3.1': 9.771, '2.1': 9.771}.get(MPL_VERSION, 0.))
tolerance={'3.1': 9.771,
'2.1': 9.785}.get(MPL_VERSION, 0.00651))
def test_declarative_sfc_obs_full():
"""Test making a full surface observation plot."""
data = pd.read_csv(get_test_data('SFC_obs.csv', as_file_obj=False),
Expand Down
13 changes: 9 additions & 4 deletions tests/plots/test_station_plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
from metpy.units import units


MPL_VERSION = matplotlib.__version__[:3]


@pytest.mark.mpl_image_compare(tolerance=2.444, savefig_kwargs={'dpi': 300}, remove_text=True)
def test_stationplot_api():
"""Test the StationPlot API."""
Expand Down Expand Up @@ -279,30 +282,32 @@ def wind_plot():
return u, v, x, y


@pytest.mark.mpl_image_compare(tolerance=0.00323, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance={'2.1': 0.0423}.get(MPL_VERSION, 0.00434),
remove_text=True)
def test_barb_projection(wind_plot):
"""Test that barbs are properly projected (#598)."""
u, v, x, y = wind_plot

# Plot and check barbs (they should align with grid lines)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.LambertConformal())
ax.gridlines(xlocs=[-135, -120, -105, -90, -75, -60, -45])
ax.gridlines(xlocs=[-120, -105, -90, -75, -60], ylocs=np.arange(24, 55, 6))
sp = StationPlot(ax, x, y, transform=ccrs.PlateCarree())
sp.plot_barb(u, v)

return fig


@pytest.mark.mpl_image_compare(tolerance=0.00205, remove_text=True)
@pytest.mark.mpl_image_compare(tolerance={'2.1': 0.0693}.get(MPL_VERSION, 0.00382),
remove_text=True)
def test_arrow_projection(wind_plot):
"""Test that arrows are properly projected."""
u, v, x, y = wind_plot

# Plot and check barbs (they should align with grid lines)
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1, projection=ccrs.LambertConformal())
ax.gridlines(xlocs=[-135, -120, -105, -90, -75, -60, -45])
ax.gridlines(xlocs=[-120, -105, -90, -75, -60], ylocs=np.arange(24, 55, 6))
sp = StationPlot(ax, x, y, transform=ccrs.PlateCarree())
sp.plot_arrow(u, v)
sp.plot_arrow(u, v) # plot_arrow used twice to hit removal if statement
Expand Down