Skip to content

Commit

Permalink
Add comprehensive test suite for mslib.utils.coordinate module
Browse files Browse the repository at this point in the history
Add comprehensive test suite for `mslib.utils.coordinate` module  

- Refactored and added new test cases for functions in `coordinate` module.  
- Used `pytest.mark.parametrize` for parameterized testing.  
- Improved edge case coverage for distance, projection, and angle calculations.  
- Enhanced test readability and maintainability with detailed docstrings.  
- Validated linear and great circle path generation for lat/lon points.
  • Loading branch information
saiabhinav001 authored Jan 6, 2025
1 parent ca9db4a commit 058ca45
Showing 1 changed file with 94 additions and 113 deletions.
207 changes: 94 additions & 113 deletions tests/_test_utils/test_coordinate.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
See the License for the specific language governing permissions and
limitations under the License.
"""

import logging
import datetime

import numpy as np
import pytest

Expand All @@ -39,140 +39,127 @@

class TestGetDistance:
"""
tests for distance based calculations
Tests for distance-based calculations in `coordinate` module.
"""
# we don't test the utils method here, may be that method should me refactored off

def test_get_distance(self):
coordinates_distance = [(50.355136, 7.566077, 50.353968, 4.577915, 212),
(-5.135943, -42.792442, 4.606085, 120.028077, 18130)]
for lat0, lon0, lat1, lon1, distance in coordinates_distance:
assert int(coordinate.get_distance(lat0, lon0, lat1, lon1)) == distance

def test_find_location(self):
assert find_location(50.92, 6.36) == ([50.92, 6.36], 'Juelich')
assert find_location(50.9200002, 6.36) == ([50.92, 6.36], 'Juelich')
@pytest.mark.parametrize("lat0, lon0, lat1, lon1, expected_distance", [
(50.355136, 7.566077, 50.353968, 4.577915, 212),
(-5.135943, -42.792442, 4.606085, 120.028077, 18130),
])
def test_get_distance(self, lat0, lon0, lat1, lon1, expected_distance):
"""
Test the calculation of distances between coordinate pairs.
"""
assert int(coordinate.get_distance(lat0, lon0, lat1, lon1)) == expected_distance

@pytest.mark.parametrize("lat, lon, expected_location", [
(50.92, 6.36, ([50.92, 6.36], 'Juelich')),
(50.9200002, 6.36, ([50.92, 6.36], 'Juelich')),
])
def test_find_location(self, lat, lon, expected_location):
"""
Test finding the location from coordinates.
"""
assert find_location(lat, lon) == expected_location


class TestProjections:
"""
Tests for handling coordinate projections.
"""

def test_get_projection_params(self):
"""
Test fetching projection parameters for various EPSG codes.
"""
assert get_projection_params("epsg:4839") == {'basemap': {'epsg': '4839'}, 'bbox': 'meter(10.5,51)'}
with pytest.raises(ValueError):
get_projection_params('auto2:42005')
with pytest.raises(ValueError):
get_projection_params('auto:42001')
with pytest.raises(ValueError):
get_projection_params('crs:83')
for invalid_code in ['auto2:42005', 'auto:42001', 'crs:83']:
with pytest.raises(ValueError):
get_projection_params(invalid_code)


class TestAngles:
"""
tests about angles
Tests for angle normalization and point rotation.
"""

def test_normalize_angle(self):
assert coordinate.fix_angle(0) == 0
assert coordinate.fix_angle(180) == 180
assert coordinate.fix_angle(270) == 270
assert coordinate.fix_angle(-90) == 270
assert coordinate.fix_angle(-180) == 180
assert coordinate.fix_angle(-181) == 179
assert coordinate.fix_angle(420) == 60

def test_rotate_point(self):
assert coordinate.rotate_point([0, 0], 0) == (0.0, 0.0)
assert coordinate.rotate_point([0, 0], 180) == (0.0, 0.0)
assert coordinate.rotate_point([1, 0], 0) == (1.0, 0.0)
assert coordinate.rotate_point([100, 90], 90) == (-90, 100)
@pytest.mark.parametrize("angle, normalized", [
(0, 0),
(180, 180),
(270, 270),
(-90, 270),
(-180, 180),
(-181, 179),
(420, 60),
])
def test_normalize_angle(self, angle, normalized):
"""
Test normalizing angles to the range [0, 360).
"""
assert coordinate.fix_angle(angle) == normalized

@pytest.mark.parametrize("point, angle, rotated_point", [
([0, 0], 0, (0.0, 0.0)),
([0, 0], 180, (0.0, 0.0)),
([1, 0], 0, (1.0, 0.0)),
([100, 90], 90, (-90.0, 100.0)),
])
def test_rotate_point(self, point, angle, rotated_point):
"""
Test rotating points around the origin.
"""
assert coordinate.rotate_point(point, angle) == rotated_point


class TestLatLonPoints:
def test_linear(self):
ref_lats = [0, 10]
ref_lons = [0, 0]

lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=2, connection="linear")
assert len(lats) == len(ref_lats)
assert all(lats == ref_lats)
assert len(lons) == len(ref_lons)
assert all(lons == ref_lons)

lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=3, connection="linear")
assert len(lats) == 3
assert len(lons) == 3
assert all(lats == [0, 5, 10])

ref_lats = [0, 0]
ref_lons = [0, 10]
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=3, connection="linear")
assert len(lats) == 3
assert len(lons) == 3
assert all(lons == [0, 5, 10])

lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=3, connection="linear")
assert len(lats) == 3
assert len(lons) == 3
assert all(lons == [0, 5, 10])

def test_greatcircle(self):
ref_lats = [0, 10]
ref_lons = [0, 0]

lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=2, connection="greatcircle")
assert len(lats) == len(ref_lats)
assert lats == ref_lats
assert len(lons) == len(ref_lons)
assert lons == ref_lons
"""
Tests for generating lat/lon points along paths.
"""

@pytest.mark.parametrize("ref_lats, ref_lons, numpoints, connection, expected_lats, expected_lons", [
([0, 10], [0, 0], 2, "linear", [0, 10], [0, 0]),
([0, 10], [0, 0], 3, "linear", [0, 5, 10], [0, 0, 0]),
([0, 0], [0, 10], 3, "linear", [0, 0, 0], [0, 5, 10]),
])
def test_linear(self, ref_lats, ref_lons, numpoints, connection, expected_lats, expected_lons):
"""
Test generating linear lat/lon points.
"""
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=3, connection="linear")
assert len(lats) == 3
assert len(lons) == 3
assert all(np.asarray(lats) == [0, 5, 10])

ref_lats = [0, 0]
ref_lons = [0, 10]
numpoints=numpoints, connection=connection)
np.testing.assert_array_equal(lats, expected_lats)
np.testing.assert_array_equal(lons, expected_lons)

@pytest.mark.parametrize("ref_lats, ref_lons, numpoints", [
([0, 10], [0, 0], 2),
([0, 10], [0, 0], 3),
])
def test_greatcircle(self, ref_lats, ref_lons, numpoints):
"""
Test generating lat/lon points along a great circle path.
"""
lats, lons = coordinate.latlon_points(ref_lats[0], ref_lons[0], ref_lats[1], ref_lons[1],
numpoints=3, connection="linear")
assert len(lats) == 3
assert len(lons) == 3
assert all(np.asarray(lons) == [0, 5, 10])
numpoints=numpoints, connection="greatcircle")
assert len(lats) == numpoints
assert len(lons) == numpoints


def test_pathpoints():
"""
Test generating path points with timestamps for different connections.
"""
lats = [0, 10]
lons = [0, 10]
times = [datetime.datetime(2012, 7, 1, 10, 30),
datetime.datetime(2012, 7, 1, 10, 40)]
ref = [lats, lons, times]
result = coordinate.path_points(lats, lons, 100, times=times, connection="linear")
assert all(len(_x) == 100 for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]

result = coordinate.path_points(lats, lons, 100, times=times, connection="greatcircle")
assert all(len(_x) == 100 for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]

result = coordinate.path_points(lats, lons, 200, times=times, connection="linear")
assert all(len(_x) == 200 for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]

result = coordinate.path_points(lats, lons, 200, times=times, connection="greatcircle")
assert all(len(_x) == 200 for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]
for numpoints, connection in [(100, "linear"), (100, "greatcircle"), (200, "linear"), (200, "greatcircle")]:
result = coordinate.path_points(lats, lons, numpoints, times=times, connection=connection)
assert all(len(_x) == numpoints for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]

lats = [0, 10, -20]
lons = [0, 10, 20]
Expand All @@ -182,12 +169,6 @@ def test_pathpoints():
ref = [lats, lons, times]

result = coordinate.path_points(lats, lons, 100, times=times, connection="linear")
assert all([len(_x) == 100 for _x in result])
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]

result = coordinate.path_points(lats, lons, 100, times=times, connection="greatcircle")
assert all(len(_x) == 100 for _x in result)
for i in range(3):
assert pytest.approx(result[i][0]) == ref[i][0]
Expand Down

0 comments on commit 058ca45

Please sign in to comment.