From e396d95fddc8edcc4be6ae8ace6f09af9f49e31c Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Fri, 15 Oct 2021 22:15:30 +0200 Subject: [PATCH 1/9] minor fix for appveyor --- .appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.appveyor.yml b/.appveyor.yml index 3c825cc..b958416 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -43,7 +43,7 @@ after_test: deploy_script: - cmd: " IF NOT DEFINED APPVEYOR_REPO_TAG_NAME ( - deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH% -py %PYTHON_VERSION% ci/conda-recipe + deploy-conda-recipe -l %APPVEYOR_REPO_BRANCH:/=-% -py %PYTHON_VERSION% ci/conda-recipe ) ELSE ( deploy-conda-recipe -py %PYTHON_VERSION% ci/conda-recipe )" From 95d68d3cf377b52e465a44e4520c05e6c3852058 Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Fri, 15 Oct 2021 22:15:48 +0200 Subject: [PATCH 2/9] make false_easting and false_northing optional --- psy_maps/plotters.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/psy_maps/plotters.py b/psy_maps/plotters.py index 314fb2c..eb59393 100755 --- a/psy_maps/plotters.py +++ b/psy_maps/plotters.py @@ -369,9 +369,9 @@ def lambert_conformal_conic_from_cf(self, crs): iter(kwargs['standard_parallels']) except TypeError: kwargs['standard_parallels'] = [kwargs['standard_parallels']] - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.LambertConformal(**kwargs) @@ -439,9 +439,9 @@ def stereographic_from_cf(self, crs): central_longitude=crs.longitude_of_projection_origin, scale_factor=crs.scale_factor_at_projection_origin ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.Stereographic(**kwargs) From b89e4dc7584a23c862c0f9d1b146a334e8b8c7e2 Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Fri, 15 Oct 2021 22:21:46 +0200 Subject: [PATCH 3/9] make false_easting optional everywhere --- psy_maps/plotters.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/psy_maps/plotters.py b/psy_maps/plotters.py index eb59393..f460e87 100755 --- a/psy_maps/plotters.py +++ b/psy_maps/plotters.py @@ -319,9 +319,9 @@ def albers_conical_equal_area_from_cf(self, crs): iter(kwargs['standard_parallels']) except TypeError: kwargs['standard_parallels'] = [kwargs['standard_parallels']] - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.AlbersEqualArea(**kwargs) @@ -330,9 +330,9 @@ def azimuthal_equidistant_from_cf(self, crs): central_longitude=crs.longitude_of_projection_origin, central_latitude=crs.latitude_of_projection_origin, ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.AzimuthalEquidistant(**kwargs) @@ -342,9 +342,9 @@ def geostationary_from_cf(self, crs): satellite_height=crs.perspective_point_height, sweep_axis=crs.sweep_angle_axis, ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.Geostationary(**kwargs) @@ -353,9 +353,9 @@ def lambert_azimuthal_equal_area_from_cf(self, crs): central_longitude=crs.longitude_of_projection_origin, central_latitude=crs.latitude_of_projection_origin, ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.LambertAzimuthalEqualArea(**kwargs) @@ -390,9 +390,9 @@ def mercator_from_cf(self, crs): ) if hasattr(crs, 'scale_factor_at_projection_origin'): kwargs['scale_factor'] = crs.scale_factor_at_projection_origin - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.Mercator(**kwargs) @@ -427,9 +427,9 @@ def sinusoidal_from_cf(self, crs): kwargs = dict( central_longitude=crs.longitude_of_central_meridian, ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.Sinusoidal(**kwargs) @@ -451,9 +451,9 @@ def transverse_mercator_from_cf(self, crs): central_latitude=crs.latitude_of_projection_origin, scale_factor=crs.scale_factor_at_central_meridian, ) - if getattr(crs, 'false_easting'): + if getattr(crs, 'false_easting', None): kwargs['false_easting'] = crs.false_easting - if getattr(crs, 'false_northing'): + if getattr(crs, 'false_northing', None): kwargs['false_northing'] = crs.false_northing return ccrs.TransverseMercator(**kwargs) From a4562309d7436c55acf34038444d0b955d1ebfb3 Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Fri, 15 Oct 2021 22:28:13 +0200 Subject: [PATCH 4/9] add tests for false_easting and false_northing --- tests/test_projections.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_projections.py b/tests/test_projections.py index bc1208c..8036874 100644 --- a/tests/test_projections.py +++ b/tests/test_projections.py @@ -123,3 +123,36 @@ def test_rotated_pole_extent(open_grid_ds): assert isinstance(plotter.ax.projection, ccrs.RotatedPole) lonmin, lonmax = plotter.ax.get_extent()[:2] assert lonmax - lonmin < 200 + + +def test_false_easting(open_grid_ds, grid, grid_projection): + grid_ds = open_grid_ds(grid) + grid_var = grid_ds["Band1"].grid_mapping + if "false_easting" not in grid_ds[grid_var].attrs: + pytest.skip(f"No false_easting parameter for {grid_var} grid.") + return + del grid_ds[grid_var].attrs["false_easting"] + with grid_ds.psy.plot.mapplot() as sp: + assert len(sp) == 1 + plotter = sp.plotters[0] + assert isinstance(plotter.transform.projection, grid_projection) + assert plotter.plot._kwargs.get('transform') is \ + plotter.transform.projection + assert isinstance(plotter.projection.projection, grid_projection) + + +def test_false_northing(open_grid_ds, grid, grid_projection): + grid_ds = open_grid_ds(grid) + grid_var = grid_ds["Band1"].grid_mapping + if "false_northing" not in grid_ds[grid_var].attrs: + pytest.skip(f"No false_northing parameter for {grid_var} grid.") + return + del grid_ds[grid_var].attrs["false_northing"] + with grid_ds.psy.plot.mapplot() as sp: + assert len(sp) == 1 + plotter = sp.plotters[0] + assert isinstance(plotter.transform.projection, grid_projection) + assert plotter.plot._kwargs.get('transform') is \ + plotter.transform.projection + assert isinstance(plotter.projection.projection, grid_projection) + From 37121cf2d655451f608f574393353c1d0e643cbb Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Wed, 20 Oct 2021 00:29:27 +0200 Subject: [PATCH 5/9] transform coordinates to meters if they are in kilometers for certain projections --- psy_maps/plotters.py | 50 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/psy_maps/plotters.py b/psy_maps/plotters.py index f460e87..1446599 100755 --- a/psy_maps/plotters.py +++ b/psy_maps/plotters.py @@ -1121,7 +1121,16 @@ def cf_projection(self): xcoord = data.psy.get_coord('x') if xcoord.attrs.get('standard_name') == 'longitude': return ccrs.PlateCarree() - return super().cf_projection + crs = super().cf_projection + # eventually transform to meters if in kilometers + self.transform_to_meter(crs) + return crs + + def set_projection(self, value, *args, **kwargs): + crs = super().set_projection(value, *args, **kwargs) + # eventually transform to meters if in kilometers + self.transform_to_meter(crs) + return crs def update(self, value): if value == 'cf': @@ -1138,6 +1147,45 @@ def update(self, value): except AttributeError: pass + def transform_to_meter(self, crs: ccrs.CRS): + """Transform x- and y-coordinates to meter. + + This method checks the plotted data and transforms the coordinates to + meter if they are in units of kilometer (i.e. ``km``). + + Notes + ----- + The returned data array is a copy of the original `da` if and only if + the coordinates have been transformed + """ + in_meters = ( + ccrs.AlbersEqualArea, + ccrs.AzimuthalEquidistant, + ccrs.Geostationary, + ccrs.LambertAzimuthalEqualArea, + ccrs.LambertConformal, + ccrs.LambertCylindrical, + ccrs.Mercator, + ccrs.Orthographic, + ccrs.SouthPolarStereo, + ccrs.NorthPolarStereo, + ccrs.Sinusoidal, + ccrs.Stereographic, + ccrs.TransverseMercator, + ) + if isinstance(crs, in_meters): + for i, da in enumerate(self.iter_data): + xcoord = da.psy.get_coord("x") + if xcoord.attrs.get("units") == "km": + da = da.psy.copy() + da[xcoord.name] = xcoord * 1000. + self.set_data(da, i) + ycoord = da.psy.get_coord("y") + if ycoord.attrs.get("units") == "km": + da = da.psy.copy() + da[ycoord.name] = ycoord * 1000. + self.set_data(da, i) + class LSM(Formatoption): """ From a29549bf7eef88170dda67464517408dbd040b94 Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Thu, 21 Oct 2021 16:18:35 +0200 Subject: [PATCH 6/9] Revert "transform coordinates to meters" This reverts commit 37121cf2d655451f608f574393353c1d0e643cbb. --- psy_maps/plotters.py | 50 +------------------------------------------- 1 file changed, 1 insertion(+), 49 deletions(-) diff --git a/psy_maps/plotters.py b/psy_maps/plotters.py index 1446599..f460e87 100755 --- a/psy_maps/plotters.py +++ b/psy_maps/plotters.py @@ -1121,16 +1121,7 @@ def cf_projection(self): xcoord = data.psy.get_coord('x') if xcoord.attrs.get('standard_name') == 'longitude': return ccrs.PlateCarree() - crs = super().cf_projection - # eventually transform to meters if in kilometers - self.transform_to_meter(crs) - return crs - - def set_projection(self, value, *args, **kwargs): - crs = super().set_projection(value, *args, **kwargs) - # eventually transform to meters if in kilometers - self.transform_to_meter(crs) - return crs + return super().cf_projection def update(self, value): if value == 'cf': @@ -1147,45 +1138,6 @@ def update(self, value): except AttributeError: pass - def transform_to_meter(self, crs: ccrs.CRS): - """Transform x- and y-coordinates to meter. - - This method checks the plotted data and transforms the coordinates to - meter if they are in units of kilometer (i.e. ``km``). - - Notes - ----- - The returned data array is a copy of the original `da` if and only if - the coordinates have been transformed - """ - in_meters = ( - ccrs.AlbersEqualArea, - ccrs.AzimuthalEquidistant, - ccrs.Geostationary, - ccrs.LambertAzimuthalEqualArea, - ccrs.LambertConformal, - ccrs.LambertCylindrical, - ccrs.Mercator, - ccrs.Orthographic, - ccrs.SouthPolarStereo, - ccrs.NorthPolarStereo, - ccrs.Sinusoidal, - ccrs.Stereographic, - ccrs.TransverseMercator, - ) - if isinstance(crs, in_meters): - for i, da in enumerate(self.iter_data): - xcoord = da.psy.get_coord("x") - if xcoord.attrs.get("units") == "km": - da = da.psy.copy() - da[xcoord.name] = xcoord * 1000. - self.set_data(da, i) - ycoord = da.psy.get_coord("y") - if ycoord.attrs.get("units") == "km": - da = da.psy.copy() - da[ycoord.name] = ycoord * 1000. - self.set_data(da, i) - class LSM(Formatoption): """ From 44908555519515ec7a15f3ee415bb067e6297dac Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Thu, 21 Oct 2021 16:21:35 +0200 Subject: [PATCH 7/9] use the convert_coordinate method to transform plots --- psy_maps/plotters.py | 35 +++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/psy_maps/plotters.py b/psy_maps/plotters.py index f460e87..96d5de5 100755 --- a/psy_maps/plotters.py +++ b/psy_maps/plotters.py @@ -2047,10 +2047,6 @@ class MapPlotter(psyps.Base2D): """Base plotter for visualizing data on a map """ - #: Boolean that is True if coordinates with units in radian should be - #: converted to degrees - convert_radian = True - _rcparams_string = ['plotter.maps.'] background = MapBackground('background') @@ -2093,6 +2089,37 @@ def ax(self): def ax(self, value): self._ax = value + @docstrings.dedent + def convert_coordinate(self, coord, *variables): + """Convert a coordinate from radian to degree. + + This method checks if the coordinate or one of the given variables has + units in radian. If yes, the given `coord` is converted to degree. + + Parameters + ---------- + %(Formatoption.convert_coordinate.parameters)s + + Returns + ------- + %(Formatoption.convert_coordinate.returns)s + """ + + def in_rad(var): + return var.attrs.get('units', '').startswith('radian') + + def in_km(var): + return var.attrs.get('units', '') == "km" + + if any(map(in_rad, chain([coord], variables))): + coord = coord.copy(data=coord * 180. / np.pi) + coord.attrs["units"] = "degrees" + elif any(map(in_km, chain([coord], variables))): + coord = coord.copy(data=coord * 1000) + coord.attrs["units"] = "m" + + return coord + class FieldPlotter(psyps.Simple2DBase, MapPlotter, psyps.BasePlotter): """Plotter for 2D scalar fields on a map From af0401efcbbd24f33b2f2ac033a4739318e29305 Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Sat, 23 Oct 2021 15:00:31 +0200 Subject: [PATCH 8/9] use psyplot-ci-orb@1.5.27 --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 92856ef..d772a9b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,7 +1,7 @@ version: 2.1 orbs: - psyplot: psyplot/psyplot-ci-orb@1.5.24 + psyplot: psyplot/psyplot-ci-orb@1.5.27 mattermost-plugin-notify: nathanaelhoun/mattermost-plugin-notify@1.2.0 executors: From 486a6c0b994176f2df972167b7c228fe083d538e Mon Sep 17 00:00:00 2001 From: Philipp Sommer Date: Sun, 21 Nov 2021 23:30:35 +0100 Subject: [PATCH 9/9] [skip ci] update CHANGELOG --- CHANGELOG.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 5b35024..0057c02 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,3 +1,21 @@ +v1.4.1 +====== +Fix projection issues + +Fixed +----- +- ``false_easting`` and ``false_northing`` are now expected to be optional + arguments for most projections (see + `#41 `__) + +Changed +------- +- we now use the ``convert_coordinate`` method that has been introduced in + `psyplot/psyplot#39 `__ and + `psyplot/psy-simple#30 `__. + See `#41 `__ + + v1.4.0 ====== Compatibility fixes and LGPL license