From 9f9262b7ed551a950e072463cadbb3d4b1a84617 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2019 12:13:56 -0300 Subject: [PATCH 01/49] Add latlon option to inside() function --- verde/coordinates.py | 41 +++++++++++++++++- verde/tests/test_coordinates.py | 74 +++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index b4a2d1e28..f6fba36eb 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -40,6 +40,38 @@ def check_region(region): ) +def latlon_continuity(region, easting): + """ + Modify geographic coordinates to ensure continuity around the globe. + + The longitude coordinates of `region` and `easting` will be modified either to the + [0, 360] degrees interval or to the [0, 180] as convenient in order to ensure the + west coordinate of the region is lower than the east one with unwrapped phases. + + Parameters + ---------- + region: list = [W, E, S, N] + The boundaries of a given geographic coordinates in degrees. + easting : array + Array with the longitude coordinates in degrees. + """ + w, e, s, n = region[:] + # Unwrap phases from region and easting coordinates + w, e = np.degrees(np.unwrap(np.radians([w, e]))) + easting = np.degrees(np.unwrap(np.radians(easting))) + # Move region and easting coordinates to [0, 360] + w %= 360 + e %= 360 + easting %= 360 + # Check if region boundaries are around the zero meridian (e.g. [350, 0]) + if w > e: + w = ((w + 180) % 360) - 180 + e = ((w + 180) % 360) - 180 + easting = ((easting + 180) % 360) - 180 + region = [w, e, s, n] + return region, easting + + def get_region(coordinates): """ Get the bounding region of the given coordinates. @@ -560,7 +592,7 @@ def profile_coordinates(point1, point2, size, extra_coords=None): return tuple(coordinates), distances -def inside(coordinates, region): +def inside(coordinates, region, latlon=False): """ Determine which points fall inside a given region. @@ -575,6 +607,9 @@ def inside(coordinates, region): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. + latlon : bool (optional) + If True both `region` and `coordinates` will be assumed to be geographic + coordinates in degrees. Returns ------- @@ -606,9 +641,11 @@ def inside(coordinates, region): [False False False]] """ + easting, northing = coordinates[:2] + if latlon: + region, easting = latlon_continuity(region, easting) check_region(region) w, e, s, n = region - easting, northing = coordinates[:2] # Allocate temporary arrays to minimize memory allocation overhead out = np.empty_like(easting, dtype=np.bool) tmp = tuple(np.empty_like(easting, dtype=np.bool) for i in range(4)) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index d62b4e8ca..ec6ad93fc 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -1,6 +1,7 @@ """ Test the coordinate generation functions """ +import numpy as np import numpy.testing as npt import pytest @@ -9,6 +10,7 @@ spacing_to_shape, profile_coordinates, grid_coordinates, + inside, ) @@ -92,3 +94,75 @@ def test_profile_coordiantes_fails(): profile_coordinates((0, 1), (1, 2), size=0) with pytest.raises(ValueError): profile_coordinates((0, 1), (1, 2), size=-10) + + +def test_inside_latlon_0_360(): + "Check if inside gets points properly with geographic coordinates on [0, 360]" + # Define longitude coordinates on 0, 360 + longitude = np.linspace(0, 350, 36) + latitude = np.linspace(-90, 90, 19) + longitude, latitude = np.meshgrid(longitude, latitude) + # Check region longitudes in 0, 360 + region = 20, 40, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([20, 30, 40]) + assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes in -180, 180 + region = 170, -170, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([170, 180, 190]) + assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes greater than 360 + region = 380, 400, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([20, 30, 40]) + assert set(latitude_cut) == set([-10, 0, 10]) + + +def test_inside_latlon_180_180(): + "Check if inside gets points properly with geographic coordinates on [-180, 180]" + # Define longitude coordinates on -180, 180 + longitude = np.linspace(-170, 180, 36) + latitude = np.linspace(-90, 90, 19) + longitude, latitude = np.meshgrid(longitude, latitude) + # Check region longitudes in 0, 360 + region = 20, 40, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([20, 30, 40]) + assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes in 0, 360 around 180 + region = 170, 190, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([170, 180, -170]) + assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes in -180, 180 + region = 170, -170, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([170, 180, -170]) + assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes greater than 360 + region = 380, 400, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([20, 30, 40]) + assert set(latitude_cut) == set([-10, 0, 10]) From 81530a24ebe47b254ee4083e658741d1376aa27b Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2019 12:34:38 -0300 Subject: [PATCH 02/49] Fix wrong coordinate variable in latlon_continuity --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index f6fba36eb..eebf1a3a4 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -66,7 +66,7 @@ def latlon_continuity(region, easting): # Check if region boundaries are around the zero meridian (e.g. [350, 0]) if w > e: w = ((w + 180) % 360) - 180 - e = ((w + 180) % 360) - 180 + e = ((e + 180) % 360) - 180 easting = ((easting + 180) % 360) - 180 region = [w, e, s, n] return region, easting From 94a5e8e8485ba6df88326dfe9b5747b5662442ad Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2019 12:35:22 -0300 Subject: [PATCH 03/49] Add test case around zero meridian --- verde/tests/test_coordinates.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index ec6ad93fc..e0c8ed541 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -118,6 +118,14 @@ def test_inside_latlon_0_360(): assert latitude_cut.size == 9 assert set(longitude_cut) == set([170, 180, 190]) assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes around zero meridian + region = -10, 10, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([0, 10, 350]) + assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes greater than 360 region = 380, 400, -10, 10 are_inside = inside([longitude, latitude], region, latlon=True) @@ -158,6 +166,14 @@ def test_inside_latlon_180_180(): assert latitude_cut.size == 9 assert set(longitude_cut) == set([170, 180, -170]) assert set(latitude_cut) == set([-10, 0, 10]) + # Check region longitudes around zero meridian + region = -10, 10, -10, 10 + are_inside = inside([longitude, latitude], region, latlon=True) + longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] + assert longitude_cut.size == 9 + assert latitude_cut.size == 9 + assert set(longitude_cut) == set([-10, 0, 10]) + assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes greater than 360 region = 380, 400, -10, 10 are_inside = inside([longitude, latitude], region, latlon=True) From db1e81685eb4350c18c9403ddf2ae286af71719a Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2019 14:29:51 -0300 Subject: [PATCH 04/49] Remove unwrapping from latlon_continuity function --- verde/coordinates.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index eebf1a3a4..2d2c57002 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -56,13 +56,10 @@ def latlon_continuity(region, easting): Array with the longitude coordinates in degrees. """ w, e, s, n = region[:] - # Unwrap phases from region and easting coordinates - w, e = np.degrees(np.unwrap(np.radians([w, e]))) - easting = np.degrees(np.unwrap(np.radians(easting))) # Move region and easting coordinates to [0, 360] - w %= 360 - e %= 360 - easting %= 360 + w = w % 360 + e = e % 360 + easting = easting % 360 # Check if region boundaries are around the zero meridian (e.g. [350, 0]) if w > e: w = ((w + 180) % 360) - 180 From a43b415f2fefdd0b19c006b7147d1a5fefbc7978 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 16 Apr 2019 15:22:51 -0300 Subject: [PATCH 05/49] Refactor latlon_continuity function --- verde/coordinates.py | 50 ++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 2d2c57002..c98be725a 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -40,33 +40,36 @@ def check_region(region): ) -def latlon_continuity(region, easting): +def latlon_continuity(longitude, extra_coords=None): """ - Modify geographic coordinates to ensure continuity around the globe. + Modify longitudinal geographic coordinates to ensure continuity around the globe. - The longitude coordinates of `region` and `easting` will be modified either to the - [0, 360] degrees interval or to the [0, 180] as convenient in order to ensure the - west coordinate of the region is lower than the east one with unwrapped phases. + The longitude coordinates of will be modified either to the [0, 360] degrees + interval or to the [-180, 180] as convenient. + If `extra_coordinates` are passed, they will be modified but not taken into account + when deciding the best interval. Parameters ---------- - region: list = [W, E, S, N] - The boundaries of a given geographic coordinates in degrees. - easting : array - Array with the longitude coordinates in degrees. + longitude : array + Longitudinal coordinates that will be put on the same degrees interval. + extra_coords : array (optional) + Additional longitudinal coordinates that will be modified but not taken into + account to decide the best interval. """ - w, e, s, n = region[:] - # Move region and easting coordinates to [0, 360] - w = w % 360 - e = e % 360 - easting = easting % 360 - # Check if region boundaries are around the zero meridian (e.g. [350, 0]) - if w > e: - w = ((w + 180) % 360) - 180 - e = ((e + 180) % 360) - 180 - easting = ((easting + 180) % 360) - 180 - region = [w, e, s, n] - return region, easting + # Move coordinates to [0, 360] + longitude = longitude % 360 + # Check if the [-180, 180] interval is better suited + change_interval = longitude.max() - longitude.min() > 180 + if change_interval: + longitude = ((longitude + 180) % 360) - 180 + # Perform the same tasks in case of extra_coordinates are defined + if extra_coords is not None: + extra_coords = extra_coords % 360 + if change_interval: + extra_coords = ((extra_coords + 180) % 360) - 180 + return longitude, extra_coords + return longitude def get_region(coordinates): @@ -638,11 +641,12 @@ def inside(coordinates, region, latlon=False): [False False False]] """ + w, e, s, n = region easting, northing = coordinates[:2] if latlon: - region, easting = latlon_continuity(region, easting) + [w, e], easting = latlon_continuity(np.array([w, e]), extra_coords=easting) + region = [w, e, s, n] check_region(region) - w, e, s, n = region # Allocate temporary arrays to minimize memory allocation overhead out = np.empty_like(easting, dtype=np.bool) tmp = tuple(np.empty_like(easting, dtype=np.bool) for i in range(4)) From 836fef28f5296a9e058aafd67f413089f5b8928b Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 10:53:36 -0300 Subject: [PATCH 06/49] Add tests for latlon_continuity --- verde/tests/test_coordinates.py | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index e0c8ed541..c021f78c7 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -11,6 +11,7 @@ profile_coordinates, grid_coordinates, inside, + latlon_continuity, ) @@ -96,6 +97,54 @@ def test_profile_coordiantes_fails(): profile_coordinates((0, 1), (1, 2), size=-10) +def test_latlon_continuity(): + "Test continuous boundary conditions in geographic coordinates." + # Two longitude coordinates in [0, 360] + longitude = np.array([10, 170]) + npt.assert_allclose(latlon_continuity(longitude), longitude) + # Two longitude coordinates in [0, 360] with difference > 180 degrees + longitude = np.array([10, 350]) + expected_longitude = np.array([10, -10]) + npt.assert_allclose(latlon_continuity(longitude), expected_longitude) + # Two longitude coordinates in [-180, 180] + longitude = np.array([-50, 50]) + npt.assert_allclose(latlon_continuity(longitude), longitude) + # Two longitude coordinates in [-180, 180] with difference > 180 degrees + longitude = np.array([170, -170]) + expected_longitude = np.array([170, 190]) + npt.assert_allclose(latlon_continuity(longitude), expected_longitude) + # Extra coordinates in [0, 360] + longitude = np.array([10, 170]) + extra = np.linspace(0, 350, 36) + for result, expected in zip( + latlon_continuity(longitude, extra_coords=extra), [longitude, extra] + ): + npt.assert_allclose(result, expected) + # Extra coordinates in [0, 360] with difference > 180 degrees + longitude = np.array([10, 350]) + extra = np.linspace(0, 350, 36) + expected_arrays = [np.array([10, -10]), np.hstack((extra[:18], extra[18:] - 360))] + for result, expected in zip( + latlon_continuity(longitude, extra_coords=extra), expected_arrays + ): + npt.assert_allclose(result, expected) + # Extra coordinates in [-180, 180] + longitude = np.array([-50, 50]) + extra = np.linspace(-180, 170, 36) + for result, expected in zip( + latlon_continuity(longitude, extra_coords=extra), [longitude, extra] + ): + npt.assert_allclose(result, expected) + # Extra coordinates in [-180, 180] with difference > 180 degrees + longitude = np.array([170, -170]) + extra = np.linspace(-180, 170, 36) + expected_arrays = [np.array([170, 190]), np.hstack((extra[:18] + 360, extra[18:]))] + for result, expected in zip( + latlon_continuity(longitude, extra_coords=extra), expected_arrays + ): + npt.assert_allclose(result, expected) + + def test_inside_latlon_0_360(): "Check if inside gets points properly with geographic coordinates on [0, 360]" # Define longitude coordinates on 0, 360 From eea0d9b204d3d3124de1ab9cc78e4c7b317f0b9c Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 11:08:48 -0300 Subject: [PATCH 07/49] Add latlon_continuity to doc and __init__.py --- doc/api/index.rst | 1 + verde/__init__.py | 1 + 2 files changed, 2 insertions(+) diff --git a/doc/api/index.rst b/doc/api/index.rst index b2a99d241..27ccd75a4 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -59,6 +59,7 @@ Coordinate Manipulation project_region inside block_split + latlon_continuity Utilities --------- diff --git a/verde/__init__.py b/verde/__init__.py index 2ff1b8961..af4685ba8 100644 --- a/verde/__init__.py +++ b/verde/__init__.py @@ -11,6 +11,7 @@ get_region, pad_region, project_region, + latlon_continuity, ) from .mask import distance_mask from .utils import variance_to_weights, maxabs, grid_to_table From 457f1923be6ba78034817b6be578a96fcc3ef061 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 11:11:44 -0300 Subject: [PATCH 08/49] Improve docstring of latlon_continuity --- verde/coordinates.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/verde/coordinates.py b/verde/coordinates.py index c98be725a..8e55d59ee 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -56,6 +56,14 @@ def latlon_continuity(longitude, extra_coords=None): extra_coords : array (optional) Additional longitudinal coordinates that will be modified but not taken into account to decide the best interval. + + Returns + ------- + longitude : array + Longitudinal coordinates moved to the best suited degrees interval. + extra_coords : array (optional) + Additional longitudinal coordinates moved to the best suited degrees interval. + It is returned only if `extra_coords` argument is passed. """ # Move coordinates to [0, 360] longitude = longitude % 360 From 11f691f48db2888e5fcb7b1fd03f2086e82cb00d Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 11:15:00 -0300 Subject: [PATCH 09/49] Add example for latlon_continuity --- verde/coordinates.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/verde/coordinates.py b/verde/coordinates.py index 8e55d59ee..79aa1c5c1 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -64,6 +64,14 @@ def latlon_continuity(longitude, extra_coords=None): extra_coords : array (optional) Additional longitudinal coordinates moved to the best suited degrees interval. It is returned only if `extra_coords` argument is passed. + + Examples + -------- + + >>> longitude = np.array([0, 10, 340, 350]) + >>> print(latlon_continuity(longitude)) + [ 0 10 -20 -10] + """ # Move coordinates to [0, 360] longitude = longitude % 360 From aa2ce523788970d20905b1983ff89c7248b8eedd Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 11:46:13 -0300 Subject: [PATCH 10/49] Add example to inside function with latlon=True --- verde/coordinates.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/verde/coordinates.py b/verde/coordinates.py index 79aa1c5c1..09dd96658 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -655,6 +655,14 @@ def inside(coordinates, region, latlon=False): [[False True True] [False True True] [False False False]] + >>> # Geographic coordinates are also supported + >>> east, north = grid_coordinates([0, 350, -20, 20], spacing=10) + >>> region = [-10, 10, -10, 10] + >>> are_inside = inside([east, north], region, latlon=True) + >>> print(east[are_inside]) + [ 0. 10. 350. 0. 10. 350. 0. 10. 350.] + >>> print(north[are_inside]) + [-10. -10. -10. 0. 0. 0. 10. 10. 10.] """ w, e, s, n = region From 123607e0f1a70fd1aaa9449a21566bcc70e06286 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 17 Apr 2019 11:56:05 -0300 Subject: [PATCH 11/49] Fix typo in docstring --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 09dd96658..9220158cb 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -44,7 +44,7 @@ def latlon_continuity(longitude, extra_coords=None): """ Modify longitudinal geographic coordinates to ensure continuity around the globe. - The longitude coordinates of will be modified either to the [0, 360] degrees + The longitude coordinates will be modified either to the [0, 360] degrees interval or to the [-180, 180] as convenient. If `extra_coordinates` are passed, they will be modified but not taken into account when deciding the best interval. From ed9410f4eba2102235595d1bd157db1eea9e4086 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 15:15:11 -0300 Subject: [PATCH 12/49] Make latlon_continuity() a private function --- verde/__init__.py | 2 +- verde/coordinates.py | 90 +++++++++++++++------------------ verde/tests/test_coordinates.py | 18 +++---- 3 files changed, 51 insertions(+), 59 deletions(-) diff --git a/verde/__init__.py b/verde/__init__.py index af4685ba8..43f9cb7a0 100644 --- a/verde/__init__.py +++ b/verde/__init__.py @@ -11,7 +11,7 @@ get_region, pad_region, project_region, - latlon_continuity, + _latlon_continuity, ) from .mask import distance_mask from .utils import variance_to_weights, maxabs, grid_to_table diff --git a/verde/coordinates.py b/verde/coordinates.py index 9220158cb..6f9470e53 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -40,54 +40,6 @@ def check_region(region): ) -def latlon_continuity(longitude, extra_coords=None): - """ - Modify longitudinal geographic coordinates to ensure continuity around the globe. - - The longitude coordinates will be modified either to the [0, 360] degrees - interval or to the [-180, 180] as convenient. - If `extra_coordinates` are passed, they will be modified but not taken into account - when deciding the best interval. - - Parameters - ---------- - longitude : array - Longitudinal coordinates that will be put on the same degrees interval. - extra_coords : array (optional) - Additional longitudinal coordinates that will be modified but not taken into - account to decide the best interval. - - Returns - ------- - longitude : array - Longitudinal coordinates moved to the best suited degrees interval. - extra_coords : array (optional) - Additional longitudinal coordinates moved to the best suited degrees interval. - It is returned only if `extra_coords` argument is passed. - - Examples - -------- - - >>> longitude = np.array([0, 10, 340, 350]) - >>> print(latlon_continuity(longitude)) - [ 0 10 -20 -10] - - """ - # Move coordinates to [0, 360] - longitude = longitude % 360 - # Check if the [-180, 180] interval is better suited - change_interval = longitude.max() - longitude.min() > 180 - if change_interval: - longitude = ((longitude + 180) % 360) - 180 - # Perform the same tasks in case of extra_coordinates are defined - if extra_coords is not None: - extra_coords = extra_coords % 360 - if change_interval: - extra_coords = ((extra_coords + 180) % 360) - 180 - return longitude, extra_coords - return longitude - - def get_region(coordinates): """ Get the bounding region of the given coordinates. @@ -668,7 +620,7 @@ def inside(coordinates, region, latlon=False): w, e, s, n = region easting, northing = coordinates[:2] if latlon: - [w, e], easting = latlon_continuity(np.array([w, e]), extra_coords=easting) + [w, e], easting = _latlon_continuity(np.array([w, e]), extra_coords=easting) region = [w, e, s, n] check_region(region) # Allocate temporary arrays to minimize memory allocation overhead @@ -764,3 +716,43 @@ def block_split(coordinates, spacing, adjust="spacing", region=None): tree = kdtree(block_coords) labels = tree.query(np.transpose(n_1d_arrays(coordinates, 2)))[1] return block_coords, labels + + +def _latlon_continuity(longitude, extra_coords=None): + """ + Modify longitudinal geographic coordinates to ensure continuity around the globe. + + The longitude coordinates will be modified either to the [0, 360] degrees + interval or to the [-180, 180] as convenient. + If `extra_coordinates` are passed, they will be modified but not taken into account + when deciding the best interval. + + Parameters + ---------- + longitude : array + Longitudinal coordinates that will be put on the same degrees interval. + extra_coords : array (optional) + Additional longitudinal coordinates that will be modified but not taken into + account to decide the best interval. + + Returns + ------- + longitude : array + Longitudinal coordinates moved to the best suited degrees interval. + extra_coords : array (optional) + Additional longitudinal coordinates moved to the best suited degrees interval. + It is returned only if `extra_coords` argument is passed. + """ + # Move coordinates to [0, 360] + longitude = longitude % 360 + # Check if the [-180, 180] interval is better suited + change_interval = longitude.max() - longitude.min() > 180 + if change_interval: + longitude = ((longitude + 180) % 360) - 180 + # Perform the same tasks in case of extra_coordinates are defined + if extra_coords is not None: + extra_coords = extra_coords % 360 + if change_interval: + extra_coords = ((extra_coords + 180) % 360) - 180 + return longitude, extra_coords + return longitude diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index c021f78c7..0fc36256d 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -11,7 +11,7 @@ profile_coordinates, grid_coordinates, inside, - latlon_continuity, + _latlon_continuity, ) @@ -101,23 +101,23 @@ def test_latlon_continuity(): "Test continuous boundary conditions in geographic coordinates." # Two longitude coordinates in [0, 360] longitude = np.array([10, 170]) - npt.assert_allclose(latlon_continuity(longitude), longitude) + npt.assert_allclose(_latlon_continuity(longitude), longitude) # Two longitude coordinates in [0, 360] with difference > 180 degrees longitude = np.array([10, 350]) expected_longitude = np.array([10, -10]) - npt.assert_allclose(latlon_continuity(longitude), expected_longitude) + npt.assert_allclose(_latlon_continuity(longitude), expected_longitude) # Two longitude coordinates in [-180, 180] longitude = np.array([-50, 50]) - npt.assert_allclose(latlon_continuity(longitude), longitude) + npt.assert_allclose(_latlon_continuity(longitude), longitude) # Two longitude coordinates in [-180, 180] with difference > 180 degrees longitude = np.array([170, -170]) expected_longitude = np.array([170, 190]) - npt.assert_allclose(latlon_continuity(longitude), expected_longitude) + npt.assert_allclose(_latlon_continuity(longitude), expected_longitude) # Extra coordinates in [0, 360] longitude = np.array([10, 170]) extra = np.linspace(0, 350, 36) for result, expected in zip( - latlon_continuity(longitude, extra_coords=extra), [longitude, extra] + _latlon_continuity(longitude, extra_coords=extra), [longitude, extra] ): npt.assert_allclose(result, expected) # Extra coordinates in [0, 360] with difference > 180 degrees @@ -125,14 +125,14 @@ def test_latlon_continuity(): extra = np.linspace(0, 350, 36) expected_arrays = [np.array([10, -10]), np.hstack((extra[:18], extra[18:] - 360))] for result, expected in zip( - latlon_continuity(longitude, extra_coords=extra), expected_arrays + _latlon_continuity(longitude, extra_coords=extra), expected_arrays ): npt.assert_allclose(result, expected) # Extra coordinates in [-180, 180] longitude = np.array([-50, 50]) extra = np.linspace(-180, 170, 36) for result, expected in zip( - latlon_continuity(longitude, extra_coords=extra), [longitude, extra] + _latlon_continuity(longitude, extra_coords=extra), [longitude, extra] ): npt.assert_allclose(result, expected) # Extra coordinates in [-180, 180] with difference > 180 degrees @@ -140,7 +140,7 @@ def test_latlon_continuity(): extra = np.linspace(-180, 170, 36) expected_arrays = [np.array([170, 190]), np.hstack((extra[:18] + 360, extra[18:]))] for result, expected in zip( - latlon_continuity(longitude, extra_coords=extra), expected_arrays + _latlon_continuity(longitude, extra_coords=extra), expected_arrays ): npt.assert_allclose(result, expected) From bdbc6dcf4beb41c4c56e65159ac36c30ffad314f Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 17:23:40 -0300 Subject: [PATCH 13/49] Rewrite _latlon_continuity function Make latlon_continuity a private function intended to work only on inside() function (at least for now). Simplify its logic and solved big issues regarding overlapping regions. --- verde/coordinates.py | 44 ++++------------- verde/tests/test_coordinates.py | 87 ++++++++++++++++----------------- 2 files changed, 53 insertions(+), 78 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 6f9470e53..a554550ab 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -620,7 +620,7 @@ def inside(coordinates, region, latlon=False): w, e, s, n = region easting, northing = coordinates[:2] if latlon: - [w, e], easting = _latlon_continuity(np.array([w, e]), extra_coords=easting) + w, e, easting = _latlon_continuity(w, e, easting) region = [w, e, s, n] check_region(region) # Allocate temporary arrays to minimize memory allocation overhead @@ -718,41 +718,17 @@ def block_split(coordinates, spacing, adjust="spacing", region=None): return block_coords, labels -def _latlon_continuity(longitude, extra_coords=None): +def _latlon_continuity(west, east, longitude_coords): """ Modify longitudinal geographic coordinates to ensure continuity around the globe. - - The longitude coordinates will be modified either to the [0, 360] degrees - interval or to the [-180, 180] as convenient. - If `extra_coordinates` are passed, they will be modified but not taken into account - when deciding the best interval. - - Parameters - ---------- - longitude : array - Longitudinal coordinates that will be put on the same degrees interval. - extra_coords : array (optional) - Additional longitudinal coordinates that will be modified but not taken into - account to decide the best interval. - - Returns - ------- - longitude : array - Longitudinal coordinates moved to the best suited degrees interval. - extra_coords : array (optional) - Additional longitudinal coordinates moved to the best suited degrees interval. - It is returned only if `extra_coords` argument is passed. """ # Move coordinates to [0, 360] - longitude = longitude % 360 + west = west % 360 + east = east % 360 + longitude_coords = longitude_coords % 360 # Check if the [-180, 180] interval is better suited - change_interval = longitude.max() - longitude.min() > 180 - if change_interval: - longitude = ((longitude + 180) % 360) - 180 - # Perform the same tasks in case of extra_coordinates are defined - if extra_coords is not None: - extra_coords = extra_coords % 360 - if change_interval: - extra_coords = ((extra_coords + 180) % 360) - 180 - return longitude, extra_coords - return longitude + if west > east: + east = ((east + 180) % 360) - 180 + west = ((west + 180) % 360) - 180 + longitude_coords = ((longitude_coords + 180) % 360) - 180 + return west, east, longitude_coords diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 0fc36256d..14f83cfe3 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -99,50 +99,49 @@ def test_profile_coordiantes_fails(): def test_latlon_continuity(): "Test continuous boundary conditions in geographic coordinates." - # Two longitude coordinates in [0, 360] - longitude = np.array([10, 170]) - npt.assert_allclose(_latlon_continuity(longitude), longitude) - # Two longitude coordinates in [0, 360] with difference > 180 degrees - longitude = np.array([10, 350]) - expected_longitude = np.array([10, -10]) - npt.assert_allclose(_latlon_continuity(longitude), expected_longitude) - # Two longitude coordinates in [-180, 180] - longitude = np.array([-50, 50]) - npt.assert_allclose(_latlon_continuity(longitude), longitude) - # Two longitude coordinates in [-180, 180] with difference > 180 degrees - longitude = np.array([170, -170]) - expected_longitude = np.array([170, 190]) - npt.assert_allclose(_latlon_continuity(longitude), expected_longitude) - # Extra coordinates in [0, 360] - longitude = np.array([10, 170]) - extra = np.linspace(0, 350, 36) - for result, expected in zip( - _latlon_continuity(longitude, extra_coords=extra), [longitude, extra] - ): - npt.assert_allclose(result, expected) - # Extra coordinates in [0, 360] with difference > 180 degrees - longitude = np.array([10, 350]) - extra = np.linspace(0, 350, 36) - expected_arrays = [np.array([10, -10]), np.hstack((extra[:18], extra[18:] - 360))] - for result, expected in zip( - _latlon_continuity(longitude, extra_coords=extra), expected_arrays - ): - npt.assert_allclose(result, expected) - # Extra coordinates in [-180, 180] - longitude = np.array([-50, 50]) - extra = np.linspace(-180, 170, 36) - for result, expected in zip( - _latlon_continuity(longitude, extra_coords=extra), [longitude, extra] - ): - npt.assert_allclose(result, expected) - # Extra coordinates in [-180, 180] with difference > 180 degrees - longitude = np.array([170, -170]) - extra = np.linspace(-180, 170, 36) - expected_arrays = [np.array([170, 190]), np.hstack((extra[:18] + 360, extra[18:]))] - for result, expected in zip( - _latlon_continuity(longitude, extra_coords=extra), expected_arrays - ): - npt.assert_allclose(result, expected) + # Define longitude coordinates around the globe for [0, 360) and [-180, 180) + longitude_360 = np.linspace(0, 350, 36) + longitude_180 = np.hstack((longitude_360[:18], longitude_360[18:] - 360)) + # Check w, e in [0, 360) + w, e = 10, 20 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 10 + assert e_new == 20 + npt.assert_allclose(longitude_new, longitude_360) + # Check w, e in [-180, 180) + w, e = -20, 20 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == -20 + assert e_new == 20 + npt.assert_allclose(longitude_new, longitude_180) + # Check angle greater than 180 + w, e = 0, 200 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 0 + assert e_new == 200 + npt.assert_allclose(longitude_new, longitude_360) + w, e = -160, 160 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == -160 + assert e_new == 160 + npt.assert_allclose(longitude_new, longitude_180) + # Check overlapping regions + w, e = -200, 200 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 160 + assert e_new == 200 + npt.assert_allclose(longitude_new, longitude_360) + w, e = 200, -200 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == -160 + assert e_new == 160 + npt.assert_allclose(longitude_new, longitude_180) def test_inside_latlon_0_360(): From 49d96cdbc7349afa212a8e19f6c99e4a390cb9a1 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 17:26:07 -0300 Subject: [PATCH 14/49] Remove latlon_continuity from docs index --- doc/api/index.rst | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/api/index.rst b/doc/api/index.rst index 27ccd75a4..b2a99d241 100644 --- a/doc/api/index.rst +++ b/doc/api/index.rst @@ -59,7 +59,6 @@ Coordinate Manipulation project_region inside block_split - latlon_continuity Utilities --------- From 0fc747f392b40cc6eff3bd031ca0ac0f76253ae3 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 17:27:18 -0300 Subject: [PATCH 15/49] Remove _latlon_continuity from __init__.py --- verde/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/verde/__init__.py b/verde/__init__.py index 43f9cb7a0..2ff1b8961 100644 --- a/verde/__init__.py +++ b/verde/__init__.py @@ -11,7 +11,6 @@ get_region, pad_region, project_region, - _latlon_continuity, ) from .mask import distance_mask from .utils import variance_to_weights, maxabs, grid_to_table From 5fb3c0eefc189c1c6dfdd05b0c7f3c2b3da2d56c Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 17:59:43 -0300 Subject: [PATCH 16/49] Add case where region goes around the globe --- verde/coordinates.py | 5 +++++ verde/tests/test_coordinates.py | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/verde/coordinates.py b/verde/coordinates.py index a554550ab..ac843e348 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -722,6 +722,8 @@ def _latlon_continuity(west, east, longitude_coords): """ Modify longitudinal geographic coordinates to ensure continuity around the globe. """ + # Check if region is defined all around the globe + all_globe = bool(east - west == 360) # Move coordinates to [0, 360] west = west % 360 east = east % 360 @@ -731,4 +733,7 @@ def _latlon_continuity(west, east, longitude_coords): east = ((east + 180) % 360) - 180 west = ((west + 180) % 360) - 180 longitude_coords = ((longitude_coords + 180) % 360) - 180 + # Move west=0 and east=360 if region longitudes goes all around the globe + if all_globe: + west, east = 0, 360 return west, east, longitude_coords diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 14f83cfe3..862aa2ffc 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -116,6 +116,13 @@ def test_latlon_continuity(): assert w_new == -20 assert e_new == 20 npt.assert_allclose(longitude_new, longitude_180) + # Check region around the globe + w, e = 20, 380 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 0 + assert e_new == 360 + npt.assert_allclose(longitude_new, longitude_360) # Check angle greater than 180 w, e = 0, 200 for longitude in [longitude_360, longitude_180]: From 88dab931d7a030b9d6f8d9e7845dc7547cd411a5 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 18:14:45 -0300 Subject: [PATCH 17/49] Add more test around the globe --- verde/tests/test_coordinates.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 862aa2ffc..d128e6d2b 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -117,12 +117,12 @@ def test_latlon_continuity(): assert e_new == 20 npt.assert_allclose(longitude_new, longitude_180) # Check region around the globe - w, e = 20, 380 - for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) - assert w_new == 0 - assert e_new == 360 - npt.assert_allclose(longitude_new, longitude_360) + for w, e in [[0, 360], [-180, 180], [20, 380]]: + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 0 + assert e_new == 360 + npt.assert_allclose(longitude_new, longitude_360) # Check angle greater than 180 w, e = 0, 200 for longitude in [longitude_360, longitude_180]: From d0b83e580c95a7f6da9e4ac540b4b3bccfa59252 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 18:17:18 -0300 Subject: [PATCH 18/49] Add test around the poles --- verde/tests/test_coordinates.py | 47 +++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index d128e6d2b..95f2ad9e1 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -237,3 +237,50 @@ def test_inside_latlon_180_180(): assert latitude_cut.size == 9 assert set(longitude_cut) == set([20, 30, 40]) assert set(latitude_cut) == set([-10, 0, 10]) + + +def test_inside_latlon_around_poles(): + "Test inside function when region is around the poles" + longitude, latitude = grid_coordinates([0, 350, -90, 90], spacing=10.0) + # North Pole all around the globe + regions = [[0, 360, 70, 90], [-180, 180, 70, 90]] + for region in regions: + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 36 + assert latitude[are_inside].size == 3 * 36 + assert set(longitude[are_inside]) == set(np.unique(longitude)) + assert set(latitude[are_inside]) == set([70, 80, 90]) + # South Pole all around the globe + regions = [[0, 360, -90, -70], [-180, 180, -90, -70]] + for region in regions: + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 36 + assert latitude[are_inside].size == 3 * 36 + assert set(longitude[are_inside]) == set(np.unique(longitude)) + assert set(latitude[are_inside]) == set([-90, -80, -70]) + # Section at the North Pole + region = [40, 90, 70, 90] + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 6 + assert latitude[are_inside].size == 3 * 6 + assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) + assert set(latitude[are_inside]) == set([70, 80, 90]) + region = [-90, -40, 70, 90] + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 6 + assert latitude[are_inside].size == 3 * 6 + assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) + assert set(latitude[are_inside]) == set([70, 80, 90]) + # Section at the South Pole + region = [40, 90, -90, -70] + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 6 + assert latitude[are_inside].size == 3 * 6 + assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) + assert set(latitude[are_inside]) == set([-90, -80, -70]) + region = [-90, -40, -90, -70] + are_inside = inside([longitude, latitude], region, latlon=True) + assert longitude[are_inside].size == 3 * 6 + assert latitude[are_inside].size == 3 * 6 + assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) + assert set(latitude[are_inside]) == set([-90, -80, -70]) From 2327294892b8f6b6693ec43eeee0bbe4f6bf1dcf Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Mon, 22 Apr 2019 18:22:33 -0300 Subject: [PATCH 19/49] Add latlon_continuity test when w == e --- verde/tests/test_coordinates.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 95f2ad9e1..4e869c460 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -123,6 +123,13 @@ def test_latlon_continuity(): assert w_new == 0 assert e_new == 360 npt.assert_allclose(longitude_new, longitude_360) + # Check w == e + w, e = 20, 20 + for longitude in [longitude_360, longitude_180]: + w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + assert w_new == 20 + assert e_new == 20 + npt.assert_allclose(longitude_new, longitude_360) # Check angle greater than 180 w, e = 0, 200 for longitude in [longitude_360, longitude_180]: From 7ad90e9d9ff4262d017ea211ff889ff78798ac63 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 23 Apr 2019 09:50:42 -0300 Subject: [PATCH 20/49] Improve check for around the globe region --- verde/coordinates.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index ac843e348..e88f93a48 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -723,17 +723,17 @@ def _latlon_continuity(west, east, longitude_coords): Modify longitudinal geographic coordinates to ensure continuity around the globe. """ # Check if region is defined all around the globe - all_globe = bool(east - west == 360) + all_globe = bool((east - west) % 360 == 0 and east != west) # Move coordinates to [0, 360] west = west % 360 east = east % 360 longitude_coords = longitude_coords % 360 + # Move west=0 and east=360 if region longitudes goes all around the globe + if all_globe: + west, east = 0, 360 # Check if the [-180, 180] interval is better suited if west > east: east = ((east + 180) % 360) - 180 west = ((west + 180) % 360) - 180 longitude_coords = ((longitude_coords + 180) % 360) - 180 - # Move west=0 and east=360 if region longitudes goes all around the globe - if all_globe: - west, east = 0, 360 return west, east, longitude_coords From 48558828a7806abc30bee54f6ace83ad9d9eccb5 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Tue, 23 Apr 2019 09:52:26 -0300 Subject: [PATCH 21/49] Add test for two rounds to the globe --- verde/tests/test_coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 4e869c460..f815f823d 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -117,7 +117,7 @@ def test_latlon_continuity(): assert e_new == 20 npt.assert_allclose(longitude_new, longitude_180) # Check region around the globe - for w, e in [[0, 360], [-180, 180], [20, 380]]: + for w, e in [[0, 360], [-180, 180], [20, 380], [0, 360 * 2]]: for longitude in [longitude_360, longitude_180]: w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) assert w_new == 0 From d3b86b2c29566ae7ef77f73ec90a24cd49a23922 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 14:08:51 -0300 Subject: [PATCH 22/49] Add latlon option to check_region() Now check_region raise errors if latitudes < -90 or > 90 and longitudes < -180 or > 360. --- verde/coordinates.py | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index e88f93a48..b26195063 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -8,7 +8,7 @@ from .utils import kdtree -def check_region(region): +def check_region(region, latlon=False): """ Check that the given region dimensions are valid. @@ -30,14 +30,30 @@ def check_region(region): if len(region) != 4: raise ValueError("Invalid region '{}'. Only 4 values allowed.".format(region)) w, e, s, n = region - if w > e: - raise ValueError( - "Invalid region '{}' (W, E, S, N). Must have W =< E.".format(region) - ) - if s > n: - raise ValueError( - "Invalid region '{}' (W, E, S, N). Must have S =< N.".format(region) - ) + if latlon: + for lon in (w, e): + if lon > 360 or lon < -180: + raise ValueError( + "Invalid longitude coordinate '{}'. ".format(lon) + + "Longitudes must be > -180 degrees and < 360 degrees." + ) + for lat in (s, n): + if lat > 90 or lat < -90: + raise ValueError( + "Invalid latitude coordinate '{}'. ".format(lat) + + "Latitudes must be > -90 degrees and < 90 degrees." + ) + else: + if w > e: + raise ValueError( + "Invalid region '{}' (W, E, S, N).Must have W =< E.".format(region) + + "If working with geographic coordinates, don't forget to add the " + + "latlon=True argument." + ) + if s > n: + raise ValueError( + "Invalid region '{}' (W, E, S, N). Must have S =< N.".format(region) + ) def get_region(coordinates): @@ -622,7 +638,7 @@ def inside(coordinates, region, latlon=False): if latlon: w, e, easting = _latlon_continuity(w, e, easting) region = [w, e, s, n] - check_region(region) + check_region(region, latlon=latlon) # Allocate temporary arrays to minimize memory allocation overhead out = np.empty_like(easting, dtype=np.bool) tmp = tuple(np.empty_like(easting, dtype=np.bool) for i in range(4)) From 7493f1dfaaaccabff3319c97678556978c928642 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 14:26:23 -0300 Subject: [PATCH 23/49] Change the way geographic region is checked --- verde/coordinates.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index b26195063..9a413376f 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -31,18 +31,22 @@ def check_region(region, latlon=False): raise ValueError("Invalid region '{}'. Only 4 values allowed.".format(region)) w, e, s, n = region if latlon: - for lon in (w, e): - if lon > 360 or lon < -180: - raise ValueError( - "Invalid longitude coordinate '{}'. ".format(lon) - + "Longitudes must be > -180 degrees and < 360 degrees." - ) - for lat in (s, n): - if lat > 90 or lat < -90: - raise ValueError( - "Invalid latitude coordinate '{}'. ".format(lat) - + "Latitudes must be > -90 degrees and < 90 degrees." - ) + if w > 360 or w < -180 or e > 360 or e < -180: + raise ValueError( + "Invalid region '{}'. ".format(region) + + "Longitudes must be > -180 degrees and < 360 degrees." + ) + if s > 90 or s < -90 or n > 90 or n < -90: + raise ValueError( + "Invalid region '{}'. ".format(region) + + "Latitudes must be > -90 degrees and < 90 degrees." + ) + if abs(e - w) > 360: + raise ValueError( + "Invalid region '{}' (W, E, S, N). ".format(region) + + "East and West boundaries must not be separated by an angle greater" + + "than 360 degrees." + ) else: if w > e: raise ValueError( From 7e527e9c7e81a92dca69e76b311861cf4b0e04d1 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 14:37:02 -0300 Subject: [PATCH 24/49] Add function to check geographic coordinates --- verde/coordinates.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 9a413376f..c735d9535 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -31,16 +31,7 @@ def check_region(region, latlon=False): raise ValueError("Invalid region '{}'. Only 4 values allowed.".format(region)) w, e, s, n = region if latlon: - if w > 360 or w < -180 or e > 360 or e < -180: - raise ValueError( - "Invalid region '{}'. ".format(region) - + "Longitudes must be > -180 degrees and < 360 degrees." - ) - if s > 90 or s < -90 or n > 90 or n < -90: - raise ValueError( - "Invalid region '{}'. ".format(region) - + "Latitudes must be > -90 degrees and < 90 degrees." - ) + _check_geographic_coordinates([np.array([w, e]), np.array([s, n])]) if abs(e - w) > 360: raise ValueError( "Invalid region '{}' (W, E, S, N). ".format(region) @@ -757,3 +748,16 @@ def _latlon_continuity(west, east, longitude_coords): west = ((west + 180) % 360) - 180 longitude_coords = ((longitude_coords + 180) % 360) - 180 return west, east, longitude_coords + + +def _check_geographic_coordinates(coordinates): + "Check if geographic coordinates are within accepted degrees intervals" + longitude, latitude = coordinates[:2] + if (longitude > 360).all() or (longitude < -180).all(): + raise ValueError( + "Invalid longitude coordinates. They should be < 360 and > -180 degrees" + ) + if (latitude > 90).all() or (latitude < -90).all(): + raise ValueError( + "Invalid latitude coordinates. They should be < 90 and > -9 degrees" + ) From 9b1021d36c310addee9de2e852c5c6b9b7db4393 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:13:52 -0300 Subject: [PATCH 25/49] Replace all() for any() --- verde/coordinates.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index c735d9535..b73d8397b 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -753,11 +753,11 @@ def _latlon_continuity(west, east, longitude_coords): def _check_geographic_coordinates(coordinates): "Check if geographic coordinates are within accepted degrees intervals" longitude, latitude = coordinates[:2] - if (longitude > 360).all() or (longitude < -180).all(): + if (longitude > 360).any() or (longitude < -180).any(): raise ValueError( - "Invalid longitude coordinates. They should be < 360 and > -180 degrees" + "Invalid longitude coordinates. They should be < 360 and > -180 degrees." ) - if (latitude > 90).all() or (latitude < -90).all(): + if (latitude > 90).any() or (latitude < -90).any(): raise ValueError( - "Invalid latitude coordinates. They should be < 90 and > -9 degrees" + "Invalid latitude coordinates. They should be < 90 and > -90 degrees." ) From 0c3a0670fdc78da4826be59d7cfc23b406ec6b2b Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:17:00 -0300 Subject: [PATCH 26/49] Modify _latlon_continuity function --- verde/coordinates.py | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index b73d8397b..0bc660324 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -628,12 +628,12 @@ def inside(coordinates, region, latlon=False): [-10. -10. -10. 0. 0. 0. 10. 10. 10.] """ + check_region(region, latlon=latlon) + if latlon: + _check_geographic_coordinates(coordinates) + coordinates, region = _latlon_continuity(coordinates, region) w, e, s, n = region easting, northing = coordinates[:2] - if latlon: - w, e, easting = _latlon_continuity(w, e, easting) - region = [w, e, s, n] - check_region(region, latlon=latlon) # Allocate temporary arrays to minimize memory allocation overhead out = np.empty_like(easting, dtype=np.bool) tmp = tuple(np.empty_like(easting, dtype=np.bool) for i in range(4)) @@ -729,25 +729,29 @@ def block_split(coordinates, spacing, adjust="spacing", region=None): return block_coords, labels -def _latlon_continuity(west, east, longitude_coords): +def _latlon_continuity(coordinates, region): """ Modify longitudinal geographic coordinates to ensure continuity around the globe. """ + w, e, s, n = region[:] + longitude, latitude = coordinates[:2] # Check if region is defined all around the globe - all_globe = bool((east - west) % 360 == 0 and east != west) - # Move coordinates to [0, 360] - west = west % 360 - east = east % 360 - longitude_coords = longitude_coords % 360 + all_globe = np.allclose(abs(e - w), 360) + # Move coordinates to [0, 360) + w = w % 360 + e = e % 360 + longitude = longitude % 360 # Move west=0 and east=360 if region longitudes goes all around the globe if all_globe: - west, east = 0, 360 - # Check if the [-180, 180] interval is better suited - if west > east: - east = ((east + 180) % 360) - 180 - west = ((west + 180) % 360) - 180 - longitude_coords = ((longitude_coords + 180) % 360) - 180 - return west, east, longitude_coords + w, e = 0, 360 + # Check if the [-180, 180) interval is better suited + if w > e: + e = ((e + 180) % 360) - 180 + w = ((w + 180) % 360) - 180 + longitude = ((longitude + 180) % 360) - 180 + region = [w, e, s, n] + coordinates = [longitude, latitude] + return coordinates, region def _check_geographic_coordinates(coordinates): From 76fc0b9e5812646fb1d9df6effe4e965bc844ff7 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:24:38 -0300 Subject: [PATCH 27/49] Add docstring for latlon on check_region --- verde/coordinates.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/verde/coordinates.py b/verde/coordinates.py index 0bc660324..d23184910 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -20,6 +20,8 @@ def check_region(region, latlon=False): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. + latlon : bool (optional) + If True the `region` will be assumed to be geographic coordinates in degrees. Raises ------ From b6dfa90e1fadb5f0a1c882c796c593c10f753722 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:28:14 -0300 Subject: [PATCH 28/49] Add s > n error for both latlon True or False --- verde/coordinates.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index d23184910..9d08fd41e 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -47,10 +47,10 @@ def check_region(region, latlon=False): + "If working with geographic coordinates, don't forget to add the " + "latlon=True argument." ) - if s > n: - raise ValueError( - "Invalid region '{}' (W, E, S, N). Must have S =< N.".format(region) - ) + if s > n: + raise ValueError( + "Invalid region '{}' (W, E, S, N). Must have S =< N.".format(region) + ) def get_region(coordinates): From 98cf7fc22405ea97fe3ac30cb7b7f8b89aa929d0 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:29:44 -0300 Subject: [PATCH 29/49] Extended tests for check_region when latlon=True --- verde/tests/test_coordinates.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index f815f823d..90fc60898 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -87,6 +87,21 @@ def test_check_region(): check_region([-1, -2, -4, -3]) with pytest.raises(ValueError): check_region([-2, -1, -2, -3]) + check_region([0, 360, -90, 90], latlon=True) + with pytest.raises(ValueError): + check_region([-200, 0, -10, 10], latlon=True) + with pytest.raises(ValueError): + check_region([0, 400, -10, 10], latlon=True) + with pytest.raises(ValueError): + check_region([-200, -190, -10, 10], latlon=True) + with pytest.raises(ValueError): + check_region([-45, 45, -100, 0], latlon=True) + with pytest.raises(ValueError): + check_region([-45, 45, -100, 0], latlon=True) + with pytest.raises(ValueError): + check_region([-45, 45, 0, 100], latlon=True) + with pytest.raises(ValueError): + check_region([0, 360.5, -30, 30], latlon=True) def test_profile_coordiantes_fails(): From 00fddb2a59be973f94cc946e1d361379cbf96520 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:39:02 -0300 Subject: [PATCH 30/49] Update tests for the new code --- verde/tests/test_coordinates.py | 87 ++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 39 deletions(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 90fc60898..0afa0babc 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -117,57 +117,82 @@ def test_latlon_continuity(): # Define longitude coordinates around the globe for [0, 360) and [-180, 180) longitude_360 = np.linspace(0, 350, 36) longitude_180 = np.hstack((longitude_360[:18], longitude_360[18:] - 360)) + latitude = np.linspace(-90, 90, 36) + s, n = -90, 90 # Check w, e in [0, 360) - w, e = 10, 20 + w, e = 10.5, 20.3 for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) - assert w_new == 10 - assert e_new == 20 + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n + assert w_new == w + assert e_new == e npt.assert_allclose(longitude_new, longitude_360) # Check w, e in [-180, 180) w, e = -20, 20 for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n assert w_new == -20 assert e_new == 20 npt.assert_allclose(longitude_new, longitude_180) # Check region around the globe - for w, e in [[0, 360], [-180, 180], [20, 380], [0, 360 * 2]]: + for w, e in [[0, 360], [-180, 180], [-20, 340]]: for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n assert w_new == 0 assert e_new == 360 npt.assert_allclose(longitude_new, longitude_360) # Check w == e w, e = 20, 20 for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n assert w_new == 20 assert e_new == 20 npt.assert_allclose(longitude_new, longitude_360) # Check angle greater than 180 w, e = 0, 200 for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n assert w_new == 0 assert e_new == 200 npt.assert_allclose(longitude_new, longitude_360) w, e = -160, 160 for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) - assert w_new == -160 - assert e_new == 160 - npt.assert_allclose(longitude_new, longitude_180) - # Check overlapping regions - w, e = -200, 200 - for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) - assert w_new == 160 - assert e_new == 200 - npt.assert_allclose(longitude_new, longitude_360) - w, e = 200, -200 - for longitude in [longitude_360, longitude_180]: - w_new, e_new, longitude_new = _latlon_continuity(w, e, longitude) + coordinates = [longitude, latitude] + region = (w, e, s, n) + coordinates_new, region_new = _latlon_continuity(coordinates, region) + longitude_new, latitude_new = coordinates_new[:] + w_new, e_new, s_new, n_new = region_new[:] + assert s_new == s + assert n_new == n assert w_new == -160 assert e_new == 160 npt.assert_allclose(longitude_new, longitude_180) @@ -203,14 +228,6 @@ def test_inside_latlon_0_360(): assert latitude_cut.size == 9 assert set(longitude_cut) == set([0, 10, 350]) assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes greater than 360 - region = 380, 400, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([20, 30, 40]) - assert set(latitude_cut) == set([-10, 0, 10]) def test_inside_latlon_180_180(): @@ -251,14 +268,6 @@ def test_inside_latlon_180_180(): assert latitude_cut.size == 9 assert set(longitude_cut) == set([-10, 0, 10]) assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes greater than 360 - region = 380, 400, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([20, 30, 40]) - assert set(latitude_cut) == set([-10, 0, 10]) def test_inside_latlon_around_poles(): From 7c5b9c3aea0c7e310225532853f1763c28f68239 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 15:40:33 -0300 Subject: [PATCH 31/49] Add test in case abs(e - w) > 360 --- verde/tests/test_coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 0afa0babc..83afac158 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -101,7 +101,7 @@ def test_check_region(): with pytest.raises(ValueError): check_region([-45, 45, 0, 100], latlon=True) with pytest.raises(ValueError): - check_region([0, 360.5, -30, 30], latlon=True) + check_region([-100, 260.5, -30, 30], latlon=True) def test_profile_coordiantes_fails(): From 5b90b513b3c03ace40d3f89e5b4eb7aeaab4458a Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 24 Apr 2019 16:12:23 -0300 Subject: [PATCH 32/49] Improve styling of tests --- verde/tests/test_coordinates.py | 60 ++++++++++----------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 83afac158..919a73a1d 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -123,79 +123,55 @@ def test_latlon_continuity(): w, e = 10.5, 20.3 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == w assert e_new == e - npt.assert_allclose(longitude_new, longitude_360) + npt.assert_allclose(coordinates_new[0], longitude_360) # Check w, e in [-180, 180) w, e = -20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == -20 assert e_new == 20 - npt.assert_allclose(longitude_new, longitude_180) + npt.assert_allclose(coordinates_new[0], longitude_180) # Check region around the globe for w, e in [[0, 360], [-180, 180], [-20, 340]]: for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == 0 assert e_new == 360 - npt.assert_allclose(longitude_new, longitude_360) + npt.assert_allclose(coordinates_new[0], longitude_360) # Check w == e w, e = 20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == 20 assert e_new == 20 - npt.assert_allclose(longitude_new, longitude_360) + npt.assert_allclose(coordinates_new[0], longitude_360) # Check angle greater than 180 w, e = 0, 200 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == 0 assert e_new == 200 - npt.assert_allclose(longitude_new, longitude_360) + npt.assert_allclose(coordinates_new[0], longitude_360) w, e = -160, 160 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region = (w, e, s, n) - coordinates_new, region_new = _latlon_continuity(coordinates, region) - longitude_new, latitude_new = coordinates_new[:] - w_new, e_new, s_new, n_new = region_new[:] - assert s_new == s - assert n_new == n + coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + w_new, e_new = region_new[:2] assert w_new == -160 assert e_new == 160 - npt.assert_allclose(longitude_new, longitude_180) + npt.assert_allclose(coordinates_new[0], longitude_180) def test_inside_latlon_0_360(): From 25de3665f1bb812ed97e7cf3f9e7220dbfeb8f30 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Thu, 9 May 2019 10:37:53 -0300 Subject: [PATCH 33/49] Make latlon_continuity public --- verde/__init__.py | 1 + verde/coordinates.py | 74 ++++++++++++++++++++++++++++----- verde/tests/test_coordinates.py | 26 ++++++++---- 3 files changed, 84 insertions(+), 17 deletions(-) diff --git a/verde/__init__.py b/verde/__init__.py index 2ff1b8961..af4685ba8 100644 --- a/verde/__init__.py +++ b/verde/__init__.py @@ -11,6 +11,7 @@ get_region, pad_region, project_region, + latlon_continuity, ) from .mask import distance_mask from .utils import variance_to_weights, maxabs, grid_to_table diff --git a/verde/coordinates.py b/verde/coordinates.py index 9d08fd41e..d2d993aa2 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -633,7 +633,7 @@ def inside(coordinates, region, latlon=False): check_region(region, latlon=latlon) if latlon: _check_geographic_coordinates(coordinates) - coordinates, region = _latlon_continuity(coordinates, region) + region, coordinates = latlon_continuity(region, coordinates=coordinates) w, e, s, n = region easting, northing = coordinates[:2] # Allocate temporary arrays to minimize memory allocation overhead @@ -731,18 +731,66 @@ def block_split(coordinates, spacing, adjust="spacing", region=None): return block_coords, labels -def _latlon_continuity(coordinates, region): +def latlon_continuity(region, coordinates=None): """ - Modify longitudinal geographic coordinates to ensure continuity around the globe. + Modify geographic region boundaries to ensure continuity around the globe. + + Longitudinal boundaries of the region are moved to the `[0, 360)` or `[-180, 180)` + degrees interval depending which one is better suited for that specific region. + + Parameters + ---------- + region : list or array + List or array containing the boundary coordinates `w, `e`, `s`, `n` of the + region in degrees. + coordinates : list or array (optional) + Extra set of geographic coordinates that will be moved to the same degrees + interval as the one of the modified region. + + Returns + ------- + modified_region : array + List containing the modified boundary coordinates `w, `e`, `s`, `n` of the + region. + modified_coordinates : array (optional) + Modified set of extra geographic coordinates. + + Examples + -------- + + >>> from verde import latlon_continuity + >>> # Modify region with west > east + >>> w, e, s, n = 350, 10, -10, 10 + >>> print(latlon_continuity([w, e, s, n])) + [-10 10 -10 10] + >>> # Modify region and extra coordinates + >>> from verde import grid_coordinates + >>> region = [-70, -60, -40, -30] + >>> coordinates = grid_coordinates([270, 320, -50, -20], spacing=5) + >>> region, [longitude, latitude] = latlon_continuity(region, coordinates) + >>> print(region) + [290 300 -40 -30] + >>> print(longitude.min(), longitude.max()) + 270.0 320.0 + >>> # Another example + >>> region = [-20, 20, -20, 20] + >>> coordinates = grid_coordinates([0, 350, -90, 90], spacing=10) + >>> region, [longitude, latitude] = latlon_continuity(region, coordinates) + >>> print(region) + [-20 20 -20 20] + >>> print(longitude.min(), longitude.max()) + -180.0 170.0 """ - w, e, s, n = region[:] - longitude, latitude = coordinates[:2] + # Get longitudinal boundaries + w, e, = region[:2] # Check if region is defined all around the globe all_globe = np.allclose(abs(e - w), 360) # Move coordinates to [0, 360) w = w % 360 e = e % 360 - longitude = longitude % 360 + if coordinates: + longitude = coordinates[0] + longitude = longitude % 360 # Move west=0 and east=360 if region longitudes goes all around the globe if all_globe: w, e = 0, 360 @@ -750,10 +798,16 @@ def _latlon_continuity(coordinates, region): if w > e: e = ((e + 180) % 360) - 180 w = ((w + 180) % 360) - 180 - longitude = ((longitude + 180) % 360) - 180 - region = [w, e, s, n] - coordinates = [longitude, latitude] - return coordinates, region + if coordinates: + longitude = ((longitude + 180) % 360) - 180 + region = np.array(region) + region[:2] = w, e + if coordinates: + coordinates = np.array(coordinates) + coordinates[0] = longitude + return region, coordinates + else: + return region def _check_geographic_coordinates(coordinates): diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 919a73a1d..34342ccff 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -11,7 +11,7 @@ profile_coordinates, grid_coordinates, inside, - _latlon_continuity, + latlon_continuity, ) @@ -123,7 +123,9 @@ def test_latlon_continuity(): w, e = 10.5, 20.3 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == w assert e_new == e @@ -132,7 +134,9 @@ def test_latlon_continuity(): w, e = -20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == -20 assert e_new == 20 @@ -141,7 +145,9 @@ def test_latlon_continuity(): for w, e in [[0, 360], [-180, 180], [-20, 340]]: for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == 0 assert e_new == 360 @@ -150,7 +156,9 @@ def test_latlon_continuity(): w, e = 20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == 20 assert e_new == 20 @@ -159,7 +167,9 @@ def test_latlon_continuity(): w, e = 0, 200 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == 0 assert e_new == 200 @@ -167,7 +177,9 @@ def test_latlon_continuity(): w, e = -160, 160 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - coordinates_new, region_new = _latlon_continuity(coordinates, (w, e, s, n)) + region_new, coordinates_new = latlon_continuity( + (w, e, s, n), coordinates=coordinates + ) w_new, e_new = region_new[:2] assert w_new == -160 assert e_new == 160 From 956b083195454d9c018c18ae61a8b164c4b8dab4 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Thu, 9 May 2019 10:44:49 -0300 Subject: [PATCH 34/49] Refactor latlon_continuity to improve readability --- verde/coordinates.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index d2d993aa2..cc2c3dfca 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -786,28 +786,30 @@ def latlon_continuity(region, coordinates=None): # Check if region is defined all around the globe all_globe = np.allclose(abs(e - w), 360) # Move coordinates to [0, 360) + interval_360 = True w = w % 360 e = e % 360 - if coordinates: - longitude = coordinates[0] - longitude = longitude % 360 # Move west=0 and east=360 if region longitudes goes all around the globe if all_globe: w, e = 0, 360 # Check if the [-180, 180) interval is better suited if w > e: + interval_360 = False e = ((e + 180) % 360) - 180 w = ((w + 180) % 360) - 180 - if coordinates: - longitude = ((longitude + 180) % 360) - 180 region = np.array(region) region[:2] = w, e + # Modify extra coordinates if passed if coordinates: + longitude = coordinates[0] + if interval_360: + longitude = longitude % 360 + else: + longitude = ((longitude + 180) % 360) - 180 coordinates = np.array(coordinates) coordinates[0] = longitude return region, coordinates - else: - return region + return region def _check_geographic_coordinates(coordinates): From f7ad34d919379487b2deb2c8dcfc729584c5aa14 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Thu, 9 May 2019 10:52:07 -0300 Subject: [PATCH 35/49] Add checks inside latlon_continuity --- verde/coordinates.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index cc2c3dfca..ff505b19c 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -632,7 +632,6 @@ def inside(coordinates, region, latlon=False): """ check_region(region, latlon=latlon) if latlon: - _check_geographic_coordinates(coordinates) region, coordinates = latlon_continuity(region, coordinates=coordinates) w, e, s, n = region easting, northing = coordinates[:2] @@ -781,8 +780,9 @@ def latlon_continuity(region, coordinates=None): >>> print(longitude.min(), longitude.max()) -180.0 170.0 """ - # Get longitudinal boundaries - w, e, = region[:2] + # Get longitudinal boundaries and check region + w, e, s, n = region[:4] + check_region([w, e, s, n], latlon=True) # Check if region is defined all around the globe all_globe = np.allclose(abs(e - w), 360) # Move coordinates to [0, 360) @@ -801,6 +801,7 @@ def latlon_continuity(region, coordinates=None): region[:2] = w, e # Modify extra coordinates if passed if coordinates: + _check_geographic_coordinates(coordinates) longitude = coordinates[0] if interval_360: longitude = longitude % 360 From 4f8c502f1d3fd61d9cf06b5061eeccda0ea4d5b2 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Thu, 9 May 2019 10:53:24 -0300 Subject: [PATCH 36/49] Replace .any() with np.any() --- verde/coordinates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index ff505b19c..d3817ce8e 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -816,11 +816,11 @@ def latlon_continuity(region, coordinates=None): def _check_geographic_coordinates(coordinates): "Check if geographic coordinates are within accepted degrees intervals" longitude, latitude = coordinates[:2] - if (longitude > 360).any() or (longitude < -180).any(): + if np.any(longitude > 360) or np.any(longitude < -180): raise ValueError( "Invalid longitude coordinates. They should be < 360 and > -180 degrees." ) - if (latitude > 90).any() or (latitude < -90).any(): + if np.any(latitude > 90) or np.any(latitude < -90): raise ValueError( "Invalid latitude coordinates. They should be < 90 and > -90 degrees." ) From 9dc288a72c35b2876330e6176c6d22b897bfb0d5 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 15 May 2019 15:03:28 -0300 Subject: [PATCH 37/49] Rename latlon_continuity to longitude_continuity --- verde/__init__.py | 2 +- verde/coordinates.py | 12 ++++++------ verde/tests/test_coordinates.py | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/verde/__init__.py b/verde/__init__.py index 646a36d71..b11cac4c8 100644 --- a/verde/__init__.py +++ b/verde/__init__.py @@ -11,7 +11,7 @@ get_region, pad_region, project_region, - latlon_continuity, + longitude_continuity, ) from .mask import distance_mask from .utils import variance_to_weights, maxabs, grid_to_table diff --git a/verde/coordinates.py b/verde/coordinates.py index d6af2ebf1..b7617a19b 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -662,7 +662,7 @@ def inside(coordinates, region, latlon=False): """ check_region(region, latlon=latlon) if latlon: - region, coordinates = latlon_continuity(region, coordinates=coordinates) + region, coordinates = longitude_continuity(region, coordinates=coordinates) w, e, s, n = region easting, northing = coordinates[:2] # Allocate temporary arrays to minimize memory allocation overhead @@ -779,7 +779,7 @@ def block_split(coordinates, spacing=None, adjust="spacing", region=None, shape= return block_coords, labels -def latlon_continuity(region, coordinates=None): +def longitude_continuity(region, coordinates=None): """ Modify geographic region boundaries to ensure continuity around the globe. @@ -806,16 +806,16 @@ def latlon_continuity(region, coordinates=None): Examples -------- - >>> from verde import latlon_continuity + >>> from verde import longitude_continuity >>> # Modify region with west > east >>> w, e, s, n = 350, 10, -10, 10 - >>> print(latlon_continuity([w, e, s, n])) + >>> print(longitude_continuity([w, e, s, n])) [-10 10 -10 10] >>> # Modify region and extra coordinates >>> from verde import grid_coordinates >>> region = [-70, -60, -40, -30] >>> coordinates = grid_coordinates([270, 320, -50, -20], spacing=5) - >>> region, [longitude, latitude] = latlon_continuity(region, coordinates) + >>> region, [longitude, latitude] = longitude_continuity(region, coordinates) >>> print(region) [290 300 -40 -30] >>> print(longitude.min(), longitude.max()) @@ -823,7 +823,7 @@ def latlon_continuity(region, coordinates=None): >>> # Another example >>> region = [-20, 20, -20, 20] >>> coordinates = grid_coordinates([0, 350, -90, 90], spacing=10) - >>> region, [longitude, latitude] = latlon_continuity(region, coordinates) + >>> region, [longitude, latitude] = longitude_continuity(region, coordinates) >>> print(region) [-20 20 -20 20] >>> print(longitude.min(), longitude.max()) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 34342ccff..88b526381 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -11,7 +11,7 @@ profile_coordinates, grid_coordinates, inside, - latlon_continuity, + longitude_continuity, ) @@ -112,7 +112,7 @@ def test_profile_coordiantes_fails(): profile_coordinates((0, 1), (1, 2), size=-10) -def test_latlon_continuity(): +def test_longitude_continuity(): "Test continuous boundary conditions in geographic coordinates." # Define longitude coordinates around the globe for [0, 360) and [-180, 180) longitude_360 = np.linspace(0, 350, 36) @@ -123,7 +123,7 @@ def test_latlon_continuity(): w, e = 10.5, 20.3 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] @@ -134,7 +134,7 @@ def test_latlon_continuity(): w, e = -20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] @@ -145,7 +145,7 @@ def test_latlon_continuity(): for w, e in [[0, 360], [-180, 180], [-20, 340]]: for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] @@ -156,7 +156,7 @@ def test_latlon_continuity(): w, e = 20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] @@ -167,7 +167,7 @@ def test_latlon_continuity(): w, e = 0, 200 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] @@ -177,7 +177,7 @@ def test_latlon_continuity(): w, e = -160, 160 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = latlon_continuity( + region_new, coordinates_new = longitude_continuity( (w, e, s, n), coordinates=coordinates ) w_new, e_new = region_new[:2] From 3c45a6cd16c53f94c6c0769519dd588340d0e071 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 15 May 2019 15:09:36 -0300 Subject: [PATCH 38/49] Rename latlon to geographic --- verde/coordinates.py | 20 +++++++------- verde/tests/test_coordinates.py | 48 ++++++++++++++++----------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index b7617a19b..8818a5721 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -8,7 +8,7 @@ from .utils import kdtree -def check_region(region, latlon=False): +def check_region(region, geographic=False): """ Check that the given region dimensions are valid. @@ -20,7 +20,7 @@ def check_region(region, latlon=False): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. - latlon : bool (optional) + geographic : bool (optional) If True the `region` will be assumed to be geographic coordinates in degrees. Raises @@ -32,7 +32,7 @@ def check_region(region, latlon=False): if len(region) != 4: raise ValueError("Invalid region '{}'. Only 4 values allowed.".format(region)) w, e, s, n = region - if latlon: + if geographic: _check_geographic_coordinates([np.array([w, e]), np.array([s, n])]) if abs(e - w) > 360: raise ValueError( @@ -45,7 +45,7 @@ def check_region(region, latlon=False): raise ValueError( "Invalid region '{}' (W, E, S, N).Must have W =< E.".format(region) + "If working with geographic coordinates, don't forget to add the " - + "latlon=True argument." + + "geographic=True argument." ) if s > n: raise ValueError( @@ -603,7 +603,7 @@ def profile_coordinates(point1, point2, size, extra_coords=None): return tuple(coordinates), distances -def inside(coordinates, region, latlon=False): +def inside(coordinates, region, geographic=False): """ Determine which points fall inside a given region. @@ -618,7 +618,7 @@ def inside(coordinates, region, latlon=False): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. - latlon : bool (optional) + geographic : bool (optional) If True both `region` and `coordinates` will be assumed to be geographic coordinates in degrees. @@ -653,15 +653,15 @@ def inside(coordinates, region, latlon=False): >>> # Geographic coordinates are also supported >>> east, north = grid_coordinates([0, 350, -20, 20], spacing=10) >>> region = [-10, 10, -10, 10] - >>> are_inside = inside([east, north], region, latlon=True) + >>> are_inside = inside([east, north], region, geographic=True) >>> print(east[are_inside]) [ 0. 10. 350. 0. 10. 350. 0. 10. 350.] >>> print(north[are_inside]) [-10. -10. -10. 0. 0. 0. 10. 10. 10.] """ - check_region(region, latlon=latlon) - if latlon: + check_region(region, geographic=geographic) + if geographic: region, coordinates = longitude_continuity(region, coordinates=coordinates) w, e, s, n = region easting, northing = coordinates[:2] @@ -831,7 +831,7 @@ def longitude_continuity(region, coordinates=None): """ # Get longitudinal boundaries and check region w, e, s, n = region[:4] - check_region([w, e, s, n], latlon=True) + check_region([w, e, s, n], geographic=True) # Check if region is defined all around the globe all_globe = np.allclose(abs(e - w), 360) # Move coordinates to [0, 360) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 88b526381..041205b05 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -87,21 +87,21 @@ def test_check_region(): check_region([-1, -2, -4, -3]) with pytest.raises(ValueError): check_region([-2, -1, -2, -3]) - check_region([0, 360, -90, 90], latlon=True) + check_region([0, 360, -90, 90], geographic=True) with pytest.raises(ValueError): - check_region([-200, 0, -10, 10], latlon=True) + check_region([-200, 0, -10, 10], geographic=True) with pytest.raises(ValueError): - check_region([0, 400, -10, 10], latlon=True) + check_region([0, 400, -10, 10], geographic=True) with pytest.raises(ValueError): - check_region([-200, -190, -10, 10], latlon=True) + check_region([-200, -190, -10, 10], geographic=True) with pytest.raises(ValueError): - check_region([-45, 45, -100, 0], latlon=True) + check_region([-45, 45, -100, 0], geographic=True) with pytest.raises(ValueError): - check_region([-45, 45, -100, 0], latlon=True) + check_region([-45, 45, -100, 0], geographic=True) with pytest.raises(ValueError): - check_region([-45, 45, 0, 100], latlon=True) + check_region([-45, 45, 0, 100], geographic=True) with pytest.raises(ValueError): - check_region([-100, 260.5, -30, 30], latlon=True) + check_region([-100, 260.5, -30, 30], geographic=True) def test_profile_coordiantes_fails(): @@ -186,7 +186,7 @@ def test_longitude_continuity(): npt.assert_allclose(coordinates_new[0], longitude_180) -def test_inside_latlon_0_360(): +def test_inside_geographic_0_360(): "Check if inside gets points properly with geographic coordinates on [0, 360]" # Define longitude coordinates on 0, 360 longitude = np.linspace(0, 350, 36) @@ -194,7 +194,7 @@ def test_inside_latlon_0_360(): longitude, latitude = np.meshgrid(longitude, latitude) # Check region longitudes in 0, 360 region = 20, 40, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -202,7 +202,7 @@ def test_inside_latlon_0_360(): assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes in -180, 180 region = 170, -170, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -210,7 +210,7 @@ def test_inside_latlon_0_360(): assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes around zero meridian region = -10, 10, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -218,7 +218,7 @@ def test_inside_latlon_0_360(): assert set(latitude_cut) == set([-10, 0, 10]) -def test_inside_latlon_180_180(): +def test_inside_geographic_180_180(): "Check if inside gets points properly with geographic coordinates on [-180, 180]" # Define longitude coordinates on -180, 180 longitude = np.linspace(-170, 180, 36) @@ -226,7 +226,7 @@ def test_inside_latlon_180_180(): longitude, latitude = np.meshgrid(longitude, latitude) # Check region longitudes in 0, 360 region = 20, 40, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -234,7 +234,7 @@ def test_inside_latlon_180_180(): assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes in 0, 360 around 180 region = 170, 190, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -242,7 +242,7 @@ def test_inside_latlon_180_180(): assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes in -180, 180 region = 170, -170, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -250,7 +250,7 @@ def test_inside_latlon_180_180(): assert set(latitude_cut) == set([-10, 0, 10]) # Check region longitudes around zero meridian region = -10, 10, -10, 10 - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] assert longitude_cut.size == 9 assert latitude_cut.size == 9 @@ -258,13 +258,13 @@ def test_inside_latlon_180_180(): assert set(latitude_cut) == set([-10, 0, 10]) -def test_inside_latlon_around_poles(): +def test_inside_geographic_around_poles(): "Test inside function when region is around the poles" longitude, latitude = grid_coordinates([0, 350, -90, 90], spacing=10.0) # North Pole all around the globe regions = [[0, 360, 70, 90], [-180, 180, 70, 90]] for region in regions: - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 36 assert latitude[are_inside].size == 3 * 36 assert set(longitude[are_inside]) == set(np.unique(longitude)) @@ -272,33 +272,33 @@ def test_inside_latlon_around_poles(): # South Pole all around the globe regions = [[0, 360, -90, -70], [-180, 180, -90, -70]] for region in regions: - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 36 assert latitude[are_inside].size == 3 * 36 assert set(longitude[are_inside]) == set(np.unique(longitude)) assert set(latitude[are_inside]) == set([-90, -80, -70]) # Section at the North Pole region = [40, 90, 70, 90] - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 6 assert latitude[are_inside].size == 3 * 6 assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) assert set(latitude[are_inside]) == set([70, 80, 90]) region = [-90, -40, 70, 90] - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 6 assert latitude[are_inside].size == 3 * 6 assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) assert set(latitude[are_inside]) == set([70, 80, 90]) # Section at the South Pole region = [40, 90, -90, -70] - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 6 assert latitude[are_inside].size == 3 * 6 assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) assert set(latitude[are_inside]) == set([-90, -80, -70]) region = [-90, -40, -90, -70] - are_inside = inside([longitude, latitude], region, latlon=True) + are_inside = inside([longitude, latitude], region, geographic=True) assert longitude[are_inside].size == 3 * 6 assert latitude[are_inside].size == 3 * 6 assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) From 9906366e23420df7adc011373ea95148d708e9c1 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 15 May 2019 16:31:58 -0300 Subject: [PATCH 39/49] Remove longitude_continuity from inside function Make longitude_continuity a completely separated function from inside and change check functions for region and coordinates. Both inside and check_region functions were returned to its versions in master. --- verde/coordinates.py | 81 ++++++++-------- verde/tests/test_coordinates.py | 158 ++------------------------------ 2 files changed, 52 insertions(+), 187 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 8818a5721..3e54a6832 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -8,7 +8,7 @@ from .utils import kdtree -def check_region(region, geographic=False): +def check_region(region): """ Check that the given region dimensions are valid. @@ -20,8 +20,6 @@ def check_region(region, geographic=False): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. - geographic : bool (optional) - If True the `region` will be assumed to be geographic coordinates in degrees. Raises ------ @@ -32,21 +30,12 @@ def check_region(region, geographic=False): if len(region) != 4: raise ValueError("Invalid region '{}'. Only 4 values allowed.".format(region)) w, e, s, n = region - if geographic: - _check_geographic_coordinates([np.array([w, e]), np.array([s, n])]) - if abs(e - w) > 360: - raise ValueError( - "Invalid region '{}' (W, E, S, N). ".format(region) - + "East and West boundaries must not be separated by an angle greater" - + "than 360 degrees." - ) - else: - if w > e: - raise ValueError( - "Invalid region '{}' (W, E, S, N).Must have W =< E.".format(region) - + "If working with geographic coordinates, don't forget to add the " - + "geographic=True argument." - ) + if w > e: + raise ValueError( + "Invalid region '{}' (W, E, S, N). Must have W =< E. ".format(region) + + "If working with geographic coordinates, don't forget to match geographic" + + " region with coordinates using `longitude_continuity` function." + ) if s > n: raise ValueError( "Invalid region '{}' (W, E, S, N). Must have S =< N.".format(region) @@ -603,7 +592,7 @@ def profile_coordinates(point1, point2, size, extra_coords=None): return tuple(coordinates), distances -def inside(coordinates, region, geographic=False): +def inside(coordinates, region): """ Determine which points fall inside a given region. @@ -618,9 +607,6 @@ def inside(coordinates, region, geographic=False): region : list = [W, E, S, N] The boundaries of a given region in Cartesian or geographic coordinates. - geographic : bool (optional) - If True both `region` and `coordinates` will be assumed to be geographic - coordinates in degrees. Returns ------- @@ -651,18 +637,17 @@ def inside(coordinates, region, geographic=False): [False True True] [False False False]] >>> # Geographic coordinates are also supported + >>> from verde import longitude_continuity >>> east, north = grid_coordinates([0, 350, -20, 20], spacing=10) >>> region = [-10, 10, -10, 10] - >>> are_inside = inside([east, north], region, geographic=True) + >>> are_inside = inside(*longitude_continuity([east, north], region)) >>> print(east[are_inside]) [ 0. 10. 350. 0. 10. 350. 0. 10. 350.] >>> print(north[are_inside]) [-10. -10. -10. 0. 0. 0. 10. 10. 10.] """ - check_region(region, geographic=geographic) - if geographic: - region, coordinates = longitude_continuity(region, coordinates=coordinates) + check_region(region) w, e, s, n = region easting, northing = coordinates[:2] # Allocate temporary arrays to minimize memory allocation overhead @@ -779,7 +764,7 @@ def block_split(coordinates, spacing=None, adjust="spacing", region=None, shape= return block_coords, labels -def longitude_continuity(region, coordinates=None): +def longitude_continuity(coordinates, region): """ Modify geographic region boundaries to ensure continuity around the globe. @@ -788,12 +773,12 @@ def longitude_continuity(region, coordinates=None): Parameters ---------- + coordinates : list or array + Set of geographic coordinates that will be moved to the same degrees + interval as the one of the modified region. region : list or array List or array containing the boundary coordinates `w, `e`, `s`, `n` of the region in degrees. - coordinates : list or array (optional) - Extra set of geographic coordinates that will be moved to the same degrees - interval as the one of the modified region. Returns ------- @@ -806,16 +791,15 @@ def longitude_continuity(region, coordinates=None): Examples -------- - >>> from verde import longitude_continuity >>> # Modify region with west > east >>> w, e, s, n = 350, 10, -10, 10 - >>> print(longitude_continuity([w, e, s, n])) + >>> print(longitude_continuity(coordinates=None, region=[w, e, s, n])) [-10 10 -10 10] >>> # Modify region and extra coordinates >>> from verde import grid_coordinates >>> region = [-70, -60, -40, -30] >>> coordinates = grid_coordinates([270, 320, -50, -20], spacing=5) - >>> region, [longitude, latitude] = longitude_continuity(region, coordinates) + >>> [longitude, latitude], region = longitude_continuity(coordinates, region) >>> print(region) [290 300 -40 -30] >>> print(longitude.min(), longitude.max()) @@ -823,7 +807,7 @@ def longitude_continuity(region, coordinates=None): >>> # Another example >>> region = [-20, 20, -20, 20] >>> coordinates = grid_coordinates([0, 350, -90, 90], spacing=10) - >>> region, [longitude, latitude] = longitude_continuity(region, coordinates) + >>> [longitude, latitude], region = longitude_continuity(coordinates, region) >>> print(region) [-20 20 -20 20] >>> print(longitude.min(), longitude.max()) @@ -831,7 +815,8 @@ def longitude_continuity(region, coordinates=None): """ # Get longitudinal boundaries and check region w, e, s, n = region[:4] - check_region([w, e, s, n], geographic=True) + # Run sanity checks for region + _check_geographic_region([w, e, s, n]) # Check if region is defined all around the globe all_globe = np.allclose(abs(e - w), 360) # Move coordinates to [0, 360) @@ -850,6 +835,7 @@ def longitude_continuity(region, coordinates=None): region[:2] = w, e # Modify extra coordinates if passed if coordinates: + # Run sanity checks for coordinates _check_geographic_coordinates(coordinates) longitude = coordinates[0] if interval_360: @@ -858,7 +844,7 @@ def longitude_continuity(region, coordinates=None): longitude = ((longitude + 180) % 360) - 180 coordinates = np.array(coordinates) coordinates[0] = longitude - return region, coordinates + return coordinates, region return region @@ -873,3 +859,26 @@ def _check_geographic_coordinates(coordinates): raise ValueError( "Invalid latitude coordinates. They should be < 90 and > -90 degrees." ) + + +def _check_geographic_region(region): + "Check if region in geographic coordinates are within accepted degree intervals" + w, e, s, n = region[:4] + # Check if coordinates are within accepted degrees intervals + if np.any(np.array([w, e]) > 360) or np.any(np.array([w, e]) < -180): + raise ValueError( + "Invalid region '{}' (W, E, S, N). ".format(region) + + "Longitudinal coordinates should be < 360 and > -180 degrees." + ) + if np.any(np.array([s, n]) > 90) or np.any(np.array([s, n]) < -90): + raise ValueError( + "Invalid region '{}' (W, E, S, N). ".format(region) + + "Latitudinal coordinates should be < 90 and > -90 degrees." + ) + # Check if longitude boundaries do not involve more than one spin around the globe + if abs(e - w) > 360: + raise ValueError( + "Invalid region '{}' (W, E, S, N). ".format(region) + + "East and West boundaries must not be separated by an angle greater " + + "than 360 degrees." + ) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 041205b05..535e11c52 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -87,21 +87,6 @@ def test_check_region(): check_region([-1, -2, -4, -3]) with pytest.raises(ValueError): check_region([-2, -1, -2, -3]) - check_region([0, 360, -90, 90], geographic=True) - with pytest.raises(ValueError): - check_region([-200, 0, -10, 10], geographic=True) - with pytest.raises(ValueError): - check_region([0, 400, -10, 10], geographic=True) - with pytest.raises(ValueError): - check_region([-200, -190, -10, 10], geographic=True) - with pytest.raises(ValueError): - check_region([-45, 45, -100, 0], geographic=True) - with pytest.raises(ValueError): - check_region([-45, 45, -100, 0], geographic=True) - with pytest.raises(ValueError): - check_region([-45, 45, 0, 100], geographic=True) - with pytest.raises(ValueError): - check_region([-100, 260.5, -30, 30], geographic=True) def test_profile_coordiantes_fails(): @@ -123,9 +108,7 @@ def test_longitude_continuity(): w, e = 10.5, 20.3 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates - ) + coordinates_new, region_new = longitude_continuity(coordinates, (w, e, s, n)) w_new, e_new = region_new[:2] assert w_new == w assert e_new == e @@ -134,9 +117,7 @@ def test_longitude_continuity(): w, e = -20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates - ) + coordinates_new, region_new = longitude_continuity(coordinates, (w, e, s, n)) w_new, e_new = region_new[:2] assert w_new == -20 assert e_new == 20 @@ -145,8 +126,8 @@ def test_longitude_continuity(): for w, e in [[0, 360], [-180, 180], [-20, 340]]: for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates + coordinates_new, region_new = longitude_continuity( + coordinates, (w, e, s, n) ) w_new, e_new = region_new[:2] assert w_new == 0 @@ -156,9 +137,7 @@ def test_longitude_continuity(): w, e = 20, 20 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates - ) + coordinates_new, region_new = longitude_continuity(coordinates, (w, e, s, n)) w_new, e_new = region_new[:2] assert w_new == 20 assert e_new == 20 @@ -167,9 +146,7 @@ def test_longitude_continuity(): w, e = 0, 200 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates - ) + coordinates_new, region_new = longitude_continuity(coordinates, (w, e, s, n)) w_new, e_new = region_new[:2] assert w_new == 0 assert e_new == 200 @@ -177,129 +154,8 @@ def test_longitude_continuity(): w, e = -160, 160 for longitude in [longitude_360, longitude_180]: coordinates = [longitude, latitude] - region_new, coordinates_new = longitude_continuity( - (w, e, s, n), coordinates=coordinates - ) + coordinates_new, region_new = longitude_continuity(coordinates, (w, e, s, n)) w_new, e_new = region_new[:2] assert w_new == -160 assert e_new == 160 npt.assert_allclose(coordinates_new[0], longitude_180) - - -def test_inside_geographic_0_360(): - "Check if inside gets points properly with geographic coordinates on [0, 360]" - # Define longitude coordinates on 0, 360 - longitude = np.linspace(0, 350, 36) - latitude = np.linspace(-90, 90, 19) - longitude, latitude = np.meshgrid(longitude, latitude) - # Check region longitudes in 0, 360 - region = 20, 40, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([20, 30, 40]) - assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes in -180, 180 - region = 170, -170, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([170, 180, 190]) - assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes around zero meridian - region = -10, 10, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([0, 10, 350]) - assert set(latitude_cut) == set([-10, 0, 10]) - - -def test_inside_geographic_180_180(): - "Check if inside gets points properly with geographic coordinates on [-180, 180]" - # Define longitude coordinates on -180, 180 - longitude = np.linspace(-170, 180, 36) - latitude = np.linspace(-90, 90, 19) - longitude, latitude = np.meshgrid(longitude, latitude) - # Check region longitudes in 0, 360 - region = 20, 40, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([20, 30, 40]) - assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes in 0, 360 around 180 - region = 170, 190, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([170, 180, -170]) - assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes in -180, 180 - region = 170, -170, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([170, 180, -170]) - assert set(latitude_cut) == set([-10, 0, 10]) - # Check region longitudes around zero meridian - region = -10, 10, -10, 10 - are_inside = inside([longitude, latitude], region, geographic=True) - longitude_cut, latitude_cut = longitude[are_inside], latitude[are_inside] - assert longitude_cut.size == 9 - assert latitude_cut.size == 9 - assert set(longitude_cut) == set([-10, 0, 10]) - assert set(latitude_cut) == set([-10, 0, 10]) - - -def test_inside_geographic_around_poles(): - "Test inside function when region is around the poles" - longitude, latitude = grid_coordinates([0, 350, -90, 90], spacing=10.0) - # North Pole all around the globe - regions = [[0, 360, 70, 90], [-180, 180, 70, 90]] - for region in regions: - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 36 - assert latitude[are_inside].size == 3 * 36 - assert set(longitude[are_inside]) == set(np.unique(longitude)) - assert set(latitude[are_inside]) == set([70, 80, 90]) - # South Pole all around the globe - regions = [[0, 360, -90, -70], [-180, 180, -90, -70]] - for region in regions: - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 36 - assert latitude[are_inside].size == 3 * 36 - assert set(longitude[are_inside]) == set(np.unique(longitude)) - assert set(latitude[are_inside]) == set([-90, -80, -70]) - # Section at the North Pole - region = [40, 90, 70, 90] - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 6 - assert latitude[are_inside].size == 3 * 6 - assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) - assert set(latitude[are_inside]) == set([70, 80, 90]) - region = [-90, -40, 70, 90] - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 6 - assert latitude[are_inside].size == 3 * 6 - assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) - assert set(latitude[are_inside]) == set([70, 80, 90]) - # Section at the South Pole - region = [40, 90, -90, -70] - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 6 - assert latitude[are_inside].size == 3 * 6 - assert set(longitude[are_inside]) == set([40, 50, 60, 70, 80, 90]) - assert set(latitude[are_inside]) == set([-90, -80, -70]) - region = [-90, -40, -90, -70] - are_inside = inside([longitude, latitude], region, geographic=True) - assert longitude[are_inside].size == 3 * 6 - assert latitude[are_inside].size == 3 * 6 - assert set(longitude[are_inside]) == set([270, 280, 290, 300, 310, 320]) - assert set(latitude[are_inside]) == set([-90, -80, -70]) From 130c96e3865248adb6d4c93770a25ba34ca455b0 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 15 May 2019 16:43:45 -0300 Subject: [PATCH 40/49] Fix order of returns on docstring --- verde/coordinates.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 3e54a6832..943f3f757 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -782,11 +782,11 @@ def longitude_continuity(coordinates, region): Returns ------- + modified_coordinates : array + Modified set of extra geographic coordinates. modified_region : array List containing the modified boundary coordinates `w, `e`, `s`, `n` of the region. - modified_coordinates : array (optional) - Modified set of extra geographic coordinates. Examples -------- From 614b9e2913080825407d88b1dae2c32f571ed388 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 22 May 2019 09:37:54 -0300 Subject: [PATCH 41/49] Fix reference to longitude_continuity function Co-Authored-By: Leonardo Uieda --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 943f3f757..83530bba8 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -34,7 +34,7 @@ def check_region(region): raise ValueError( "Invalid region '{}' (W, E, S, N). Must have W =< E. ".format(region) + "If working with geographic coordinates, don't forget to match geographic" - + " region with coordinates using `longitude_continuity` function." + + " region with coordinates using 'verde.longitude_continuity'." ) if s > n: raise ValueError( From 3ac377336fc3a6e508b8cb7bed7e6bad4ae27bd2 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 22 May 2019 09:38:19 -0300 Subject: [PATCH 42/49] Improve comment on example Co-Authored-By: Leonardo Uieda --- verde/coordinates.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 83530bba8..c5b911f02 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -636,7 +636,9 @@ def inside(coordinates, region): [[False True True] [False True True] [False False False]] - >>> # Geographic coordinates are also supported + + Geographic coordinates are also supported using :func:`verde.longitude_continuity`: + >>> from verde import longitude_continuity >>> east, north = grid_coordinates([0, 350, -20, 20], spacing=10) >>> region = [-10, 10, -10, 10] From 5235234ad7f589fff7b443d728de700213936579 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Wed, 22 May 2019 09:38:52 -0300 Subject: [PATCH 43/49] Improve longitude_continuity docstring Co-Authored-By: Leonardo Uieda --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index c5b911f02..cf7527a67 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -768,7 +768,7 @@ def block_split(coordinates, spacing=None, adjust="spacing", region=None, shape= def longitude_continuity(coordinates, region): """ - Modify geographic region boundaries to ensure continuity around the globe. + Modify coordinates and region boundaries to ensure longitude continuity. Longitudinal boundaries of the region are moved to the `[0, 360)` or `[-180, 180)` degrees interval depending which one is better suited for that specific region. From fdd174867a7c8dbbfabe217f424627b0eda3bc3a Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 10:23:08 -0300 Subject: [PATCH 44/49] Double bracketing for intervals in docstring Co-Authored-By: Leonardo Uieda --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index cf7527a67..f68d5e92e 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -770,7 +770,7 @@ def longitude_continuity(coordinates, region): """ Modify coordinates and region boundaries to ensure longitude continuity. - Longitudinal boundaries of the region are moved to the `[0, 360)` or `[-180, 180)` + Longitudinal boundaries of the region are moved to the ``[0, 360)`` or ``[-180, 180)`` degrees interval depending which one is better suited for that specific region. Parameters From 3e1f495904a298ea1eca172982620af4ecf9ace6 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 10:26:50 -0300 Subject: [PATCH 45/49] Remove trailing space on empty line --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index f68d5e92e..2838708b6 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -638,7 +638,7 @@ def inside(coordinates, region): [False False False]] Geographic coordinates are also supported using :func:`verde.longitude_continuity`: - + >>> from verde import longitude_continuity >>> east, north = grid_coordinates([0, 350, -20, 20], spacing=10) >>> region = [-10, 10, -10, 10] From ca1f440b67f538d0a569066283625d75e40ad509 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 10:27:35 -0300 Subject: [PATCH 46/49] Add missing ` on docstring --- verde/coordinates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/verde/coordinates.py b/verde/coordinates.py index 2838708b6..945b79ea8 100644 --- a/verde/coordinates.py +++ b/verde/coordinates.py @@ -779,7 +779,7 @@ def longitude_continuity(coordinates, region): Set of geographic coordinates that will be moved to the same degrees interval as the one of the modified region. region : list or array - List or array containing the boundary coordinates `w, `e`, `s`, `n` of the + List or array containing the boundary coordinates `w`, `e`, `s`, `n` of the region in degrees. Returns From d822d50654a71339da31e62a013d76b9782bde3d Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 11:11:04 -0300 Subject: [PATCH 47/49] Add test function for invalid geographic region --- verde/tests/test_coordinates.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 535e11c52..956803318 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -159,3 +159,21 @@ def test_longitude_continuity(): assert w_new == -160 assert e_new == 160 npt.assert_allclose(coordinates_new[0], longitude_180) + + +def test_invalid_geographic_region(): + "Check if passing invalid region to longitude_continuity raises a ValueError" + # Region with latitude over boundaries + w, e = -10, 10 + for s, n in [[-200, 90], [-90, 200]]: + with pytest.raises(ValueError): + longitude_continuity(None, [w, e, s, n]) + # Region with longitude over boundaries + s, n = -10, 10 + for w, e in [[-200, 0], [0, 380]]: + with pytest.raises(ValueError): + longitude_continuity(None, [w, e, s, n]) + # Region with longitudinal difference greater than 360 + w, e, s, n = -180, 200, -10, 10 + with pytest.raises(ValueError): + longitude_continuity(None, [w, e, s, n]) From 9f3fd5f54bd8b8e7a247d4549c9f8d6faef14fe7 Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 11:17:51 -0300 Subject: [PATCH 48/49] Add test func for invalid geographic coordinates --- verde/tests/test_coordinates.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 956803318..8f8922f94 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -177,3 +177,26 @@ def test_invalid_geographic_region(): w, e, s, n = -180, 200, -10, 10 with pytest.raises(ValueError): longitude_continuity(None, [w, e, s, n]) + + +def test_invalid_geographic_coordinates(): + "Check if passing invalid coordinates to longitude_continuity raises a ValueError" + boundaries = [0, 360, -90, 90] + spacing = 10 + region = [-20, 20, -20, 20] + # Region with longitude point over boundaries + longitude, latitude = grid_coordinates(boundaries, spacing=spacing) + longitude[0] = -200 + with pytest.raises(ValueError): + longitude_continuity([longitude, latitude], region) + longitude[0] = 400 + with pytest.raises(ValueError): + longitude_continuity([longitude, latitude], region) + # Region with latitude point over boundaries + longitude, latitude = grid_coordinates(boundaries, spacing=spacing) + latitude[0] = -100 + with pytest.raises(ValueError): + longitude_continuity([longitude, latitude], region) + latitude[0] = 100 + with pytest.raises(ValueError): + longitude_continuity([longitude, latitude], region) From a16b01c188e5ce829603aac4bf597ab85e208c1e Mon Sep 17 00:00:00 2001 From: Santiago Soler Date: Fri, 21 Jun 2019 11:20:21 -0300 Subject: [PATCH 49/49] Remove importing unused inside on test_coordinates --- verde/tests/test_coordinates.py | 1 - 1 file changed, 1 deletion(-) diff --git a/verde/tests/test_coordinates.py b/verde/tests/test_coordinates.py index 8f8922f94..33cffdad6 100644 --- a/verde/tests/test_coordinates.py +++ b/verde/tests/test_coordinates.py @@ -10,7 +10,6 @@ spacing_to_shape, profile_coordinates, grid_coordinates, - inside, longitude_continuity, )