Skip to content
Open
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
241 changes: 105 additions & 136 deletions tests/_test_utils/test_coordinate.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
"""

tests._test_utils.test_coordinate
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This module provides pytest functions to tests mslib.utils.coordinate
This module provides pytest functions to test mslib.utils.coordinate

This file is part of MSS.

Expand All @@ -24,13 +23,12 @@
See the License for the specific language governing permissions and
limitations under the License.
"""
import logging
import datetime

import datetime
import numpy as np
import pytest

import mslib.utils.coordinate as coordinate
import logging
from mslib.utils import coordinate
from mslib.utils.find_location import find_location
from mslib.utils.get_projection_params import get_projection_params

Expand All @@ -39,156 +37,127 @@

class TestGetDistance:
"""
tests for distance based calculations
Tests for distance-based calculations in the `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.
"""
distance = coordinate.get_distance(lat0, lon0, lat1, lon1)
assert int(distance) == 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do not see why the old tests should be removed. They provide coverage for corner cases and have no issues with decimal points. The new test is a good addition, though.

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)),
([1.5, 0.5], 90, (-0.5, 1.5)),
([0.0, 2.5], 45, (-1.7678, 1.7678)),
])
def test_rotate_point(self, point, angle, rotated_point):
"""
Test rotating points around the origin.
"""
assert coordinate.rotate_point(point, angle) == pytest.approx(rotated_point)
Copy link
Member

@ReimarBauer ReimarBauer Jan 13, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you maybe found a bug by the ([0.0, 2.5], 45, (-1.7678, 1.7678)) example or the example has a problem.

How did you got to the -1.7678, 1.7678?



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

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]
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])
"""
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=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),
Copy link
Member

@ReimarBauer ReimarBauer Jan 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the second one had before ([0, 0], [0, 10] ,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=numpoints, connection="greatcircle"
)
assert len(lats) == numpoints
assert len(lons) == numpoints
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should also check for the expected value, like in assert all(np.asarray(lons) == [0, 5, 10])



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]

lats = [0, 10, -20]
lons = [0, 10, 20]
times = [datetime.datetime(2012, 7, 1, 10, 30),
datetime.datetime(2012, 7, 1, 10, 40),
datetime.datetime(2012, 7, 1, 10, 50)]
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]
for numpoints, connection in [(100, "linear"), (100, "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):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the description of eric:

https://pytest-with-eric.com/pytest-advanced/pytest-approx/#Example-Code-To-Test-Pytest-Approx

I would expect:

assert result[i][0] == pytest.approx(ref[i][0])
assert result[i][-1] == pytest.approx(ref[i][-1])

assert pytest.approx(result[i][0]) == ref[i][0]
assert pytest.approx(result[i][-1]) == ref[i][-1]
Loading