Skip to content
Open
Changes from 1 commit
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
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

@ReimarBauer ReimarBauer Jan 15, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this type: ignore is new here. What is it doing, just my interests in it. What is the reason for this?

the linter will likly want two blanks to seperate the comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think that is not good to add it here. When we have type related issues, we won't see that now, correnct?

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)

Copy link
Copy Markdown
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)),
Comment thread
saiabhinav001 marked this conversation as resolved.
Outdated
])
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),

@ReimarBauer ReimarBauer Jan 8, 2025

Copy link
Copy Markdown
Member

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=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

Copy link
Copy Markdown
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]
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):

Copy link
Copy Markdown
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]

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