tests/plots/test_mapping.py
# Copyright (c) 2018 MetPy Developers.
# Distributed under the terms of the BSD 3-Clause License.
# SPDX-License-Identifier: BSD-3-Clause
"""Test the handling of various mapping tasks."""
import pytest
from metpy.testing import version_check
ccrs = pytest.importorskip('cartopy.crs')
from metpy.plots.mapping import CFProjection # noqa: E402
def test_inverse_flattening_0():
"""Test new code for dealing the case where inverse_flattening = 0."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'semi_major_axis': 6367000,
'semi_minor_axis': 6367000, 'inverse_flattening': 0}
proj = CFProjection(attrs)
crs = proj.to_cartopy()
globe_params = crs.globe.to_proj4_params()
assert globe_params['ellps'] == 'sphere'
assert globe_params['a'] == 6367000
assert globe_params['b'] == 6367000
def test_cfprojection_arg_mapping():
"""Test the projection mapping arguments."""
source = {'source': 'a', 'longitude_of_projection_origin': -100}
# 'dest' should be the argument in the output, with the value from source
mapping = [('dest', 'source')]
kwargs = CFProjection.build_projection_kwargs(source, mapping)
assert kwargs == {'dest': 'a', 'central_longitude': -100}
def test_cfprojection_api():
"""Test the basic API of the projection interface."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000}
proj = CFProjection(attrs)
assert proj['earth_radius'] == 6367000
assert proj.to_dict() == attrs
assert str(proj) == 'Projection: lambert_conformal_conic'
def test_bad_projection_raises():
"""Test behavior when given an unknown projection."""
attrs = {'grid_mapping_name': 'unknown'}
with pytest.raises(ValueError) as exc:
CFProjection(attrs).to_cartopy()
assert 'Unhandled projection' in str(exc.value)
def test_unhandled_projection():
"""Test behavior when given a projection with no CF equivalent from PROJ."""
attrs = {
'crs_wkt': 'PROJCRS["unknown",BASEGEOGCRS["unknown",DATUM["unknown",ELLIPSOID['
'"unknown",6371200,0,LENGTHUNIT["metre",1,ID["EPSG",9001]]]],'
'PRIMEM["Greenwich",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",'
'8901]]],CONVERSION["unknown",METHOD["Equidistant Cylindrical ('
'Spherical)",ID["EPSG",1029]],PARAMETER["Latitude of 1st standard '
'parallel",0,ANGLEUNIT["degree",0.0174532925199433],ID["EPSG",8823]],'
'PARAMETER["Longitude of natural origin",0,ANGLEUNIT["degree",'
'0.0174532925199433],ID["EPSG",8802]],PARAMETER["False easting",0,'
'LENGTHUNIT["metre",1],ID["EPSG",8806]],PARAMETER["False northing",0,'
'LENGTHUNIT["metre",1],ID["EPSG",8807]]],CS[Cartesian,2],AXIS["(E)",east,'
'ORDER[1],LENGTHUNIT["metre",1,ID["EPSG",9001]]],AXIS["(N)",north,'
'ORDER[2],LENGTHUNIT["metre",1,ID["EPSG",9001]]]]'}
cfproj = CFProjection(attrs)
assert str(cfproj) == 'Projection: unknown'
with pytest.raises(ValueError) as exc:
cfproj.to_cartopy()
assert 'Unhandled projection' in str(exc.value)
def test_globe():
"""Test handling building a cartopy globe."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
'standard_parallel': 25}
proj = CFProjection(attrs)
crs = proj.to_cartopy()
globe_params = crs.globe.to_proj4_params()
assert globe_params['ellps'] == 'sphere'
assert globe_params['a'] == 6367000
assert globe_params['b'] == 6367000
def test_globe_spheroid():
"""Test handling building a cartopy globe that is not spherical."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'semi_major_axis': 6367000,
'semi_minor_axis': 6360000}
proj = CFProjection(attrs)
crs = proj.to_cartopy()
globe_params = crs.globe.to_proj4_params()
assert 'ellps' not in globe_params
assert globe_params['a'] == 6367000
assert globe_params['b'] == 6360000
def test_aea():
"""Test handling albers equal area projection."""
attrs = {'grid_mapping_name': 'albers_conical_equal_area', 'earth_radius': 6367000,
'standard_parallel': [20, 50]}
proj = CFProjection(attrs)
crs = proj.to_cartopy()
assert isinstance(crs, ccrs.AlbersEqualArea)
assert crs.proj4_params['lat_1'] == 20
assert crs.proj4_params['lat_2'] == 50
assert crs.globe.to_proj4_params()['ellps'] == 'sphere'
def test_aea_minimal():
"""Test handling albers equal area projection with minimal attributes."""
attrs = {'grid_mapping_name': 'albers_conical_equal_area'}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.AlbersEqualArea)
def test_aea_single_std_parallel():
"""Test albers equal area with one standard parallel."""
attrs = {'grid_mapping_name': 'albers_conical_equal_area', 'standard_parallel': 20}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.AlbersEqualArea)
assert crs.proj4_params['lat_1'] == 20
def test_lcc():
"""Test handling lambert conformal conic projection."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'earth_radius': 6367000,
'standard_parallel': [25, 30]}
proj = CFProjection(attrs)
crs = proj.to_cartopy()
assert isinstance(crs, ccrs.LambertConformal)
assert crs.proj4_params['lat_1'] == 25
assert crs.proj4_params['lat_2'] == 30
assert crs.globe.to_proj4_params()['ellps'] == 'sphere'
@pytest.mark.xfail(version_check('cartopy==0.23.0'), reason='SciTools/cartopy#2377')
def test_lcc_minimal():
"""Test handling lambert conformal conic projection with minimal attributes."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic'}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.LambertConformal)
@pytest.mark.xfail(version_check('cartopy==0.23.0'), reason='SciTools/cartopy#2377')
def test_lcc_single_std_parallel():
"""Test lambert conformal projection with one standard parallel."""
attrs = {'grid_mapping_name': 'lambert_conformal_conic', 'standard_parallel': 25}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.LambertConformal)
assert crs.proj4_params['lat_1'] == 25
def test_mercator():
"""Test handling a mercator projection."""
attrs = {'grid_mapping_name': 'mercator', 'standard_parallel': 25,
'longitude_of_projection_origin': -100, 'false_easting': 0, 'false_westing': 0,
'central_latitude': 0}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Mercator)
assert crs.proj4_params['lat_ts'] == 25
assert crs.proj4_params['lon_0'] == -100
def test_mercator_scale_factor():
"""Test handling a mercator projection with a scale factor."""
attrs = {'grid_mapping_name': 'mercator', 'scale_factor_at_projection_origin': 0.9}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Mercator)
assert crs.proj4_params['k_0'] == 0.9
def test_geostationary():
"""Test handling a geostationary projection."""
attrs = {'grid_mapping_name': 'geostationary', 'perspective_point_height': 35000000,
'longitude_of_projection_origin': -100, 'sweep_angle_axis': 'x',
'latitude_of_projection_origin': 0}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Geostationary)
assert crs.proj4_params['h'] == 35000000
assert crs.proj4_params['lon_0'] == -100
assert crs.proj4_params['sweep'] == 'x'
def test_geostationary_fixed_angle():
"""Test handling geostationary information that gives fixed angle instead of sweep."""
attrs = {'grid_mapping_name': 'geostationary', 'fixed_angle_axis': 'y'}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Geostationary)
assert crs.proj4_params['sweep'] == 'x'
def test_stereographic():
"""Test handling a stereographic projection."""
attrs = {'grid_mapping_name': 'stereographic', 'scale_factor_at_projection_origin': 0.9,
'longitude_of_projection_origin': -100, 'latitude_of_projection_origin': 60}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Stereographic)
assert crs.proj4_params['lon_0'] == -100
assert crs.proj4_params['lat_0'] == 60
assert crs.proj4_params['k_0'] == 0.9
def test_polar_stereographic():
"""Test handling a polar stereographic projection."""
attrs = {'grid_mapping_name': 'polar_stereographic', 'latitude_of_projection_origin': 90,
'scale_factor_at_projection_origin': 0.9,
'straight_vertical_longitude_from_pole': -100, }
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Stereographic)
assert crs.proj4_params['lon_0'] == -100
assert crs.proj4_params['lat_0'] == 90
assert crs.proj4_params['k_0'] == 0.9
def test_polar_stereographic_std_parallel():
"""Test handling a polar stereographic projection that gives a standard parallel."""
attrs = {'grid_mapping_name': 'polar_stereographic', 'latitude_of_projection_origin': -90,
'standard_parallel': 60}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.Stereographic)
assert crs.proj4_params['lat_0'] == -90
assert crs.proj4_params['lat_ts'] == 60
def test_rotated_latitude_longitude():
"""Test handling a rotated latitude longitude projection."""
attrs = {
'grid_mapping_name': 'rotated_latitude_longitude',
'grid_north_pole_latitude': 36,
'grid_north_pole_longitude': 65,
'north_pole_grid_longitude': 0.0,
}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.RotatedPole)
assert crs.proj4_params['o_lon_p'] == 0
assert crs.proj4_params['o_lat_p'] == 36
assert crs.proj4_params['lon_0'] == 180 + 65
def test_lat_lon():
"""Test handling basic lat/lon projection."""
attrs = {'grid_mapping_name': 'latitude_longitude'}
crs = CFProjection(attrs).to_cartopy()
assert isinstance(crs, ccrs.PlateCarree)
def test_eq():
"""Test that two CFProjection instances are equal given that they have the same attrs."""
attrs = {'grid_mapping_name': 'latitude_longitude'}
cf_proj_1 = CFProjection(attrs)
cf_proj_2 = CFProjection(attrs.copy())
assert cf_proj_1 == cf_proj_2
def test_ne():
"""Test that two CFProjection instances are not equal when attrs differs."""
cf_proj_1 = CFProjection({'grid_mapping_name': 'latitude_longitude'})
cf_proj_2 = CFProjection({'grid_mapping_name': 'lambert_conformal_conic'})
assert cf_proj_1 != cf_proj_2