Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2d epoch #145

Merged
merged 5 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
37 changes: 27 additions & 10 deletions src/coordinate_transformation_api/crs_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,32 @@ def get_individual_epsg_code(compound_crs: CRS) -> tuple[str, str]:
return (f"{horizontal[0]}:{horizontal[1]}", f"{vertical[0]}:{vertical[1]}")


def build_input_coord(coord: CoordinatesType, epoch: float | None) -> CoordinatesType:

# When 2D input is given with an epoch we need to add a height. So pyproj knows to
# that the epoch is an epoch and not the height, without this intervention the epoch
# would be place in the firth position of the tuple.
if len(coord) == TWO_DIMENSIONAL and epoch is not None:
return tuple([*coord, 0.0, epoch])

# Default behaviour
# The input_coord == coord that are given. When an epoch is provided with a 3D coord
# this is added or the value None is given for any other. Note: with 2D the additional None
# is the height. But this doesn't influence the result, because it's None.
input_coord = tuple(
[
*coord,
(
float(epoch)
if len(coord) == THREE_DIMENSIONAL and epoch is not None
else None
),
]
)

return input_coord


def get_transform_crs_fun( #
source_crs: str,
target_crs: str,
Expand Down Expand Up @@ -414,16 +440,7 @@ def transform_crs(val: CoordinatesType) -> tuple[float, ...]:
# when one of the src or tgt crs has a dynamic time component
# or the transformation used has a datetime component
# for now simple check on coords length (which is not correct)
input = tuple(
[
*val,
(
float(epoch)
if len(val) == THREE_DIMENSIONAL and epoch is not None
else None
),
]
)
input = build_input_coord(val, epoch)

# GeoJSON and CityJSON by definition has coordinates always in lon-lat-height (or x-y-z) order. Transformer has been created with `always_xy=True`,
# to ensure input and output coordinates are in in lon-lat-height (or x-y-z) order.
Expand Down
8 changes: 8 additions & 0 deletions tests/data/test_2d_with_epoch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [663000.0, 5781000.0]
}
}
54 changes: 54 additions & 0 deletions tests/test_geojson_transformation.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,60 @@ def test_validate_crs_transformed_geojson(feature):
validate_crs_transformed_geojson(feature_no_exc)


def test_2d_with_epoch():
with open("tests/data/test_2d_with_epoch.json") as f:
data = json.load(f)
feature_2d_2000 = Feature(**data)
feature_2d_2020 = Feature(**data)
feature_2d_org = Feature(**data)

crs_transform(feature_2d_2000, "EPSG:3043", "EPSG:32631", 2000)
crs_transform(feature_2d_2020, "EPSG:3043", "EPSG:32631", 2020)

assert feature_2d_2000 != feature_2d_org
assert feature_2d_2020 != feature_2d_org

coords_2000 = feature_2d_2000.geometry.coordinates
coords_2020 = feature_2d_2020.geometry.coordinates
coords_org = feature_2d_org.geometry.coordinates

dif_2000_org = 0.29
dif_2020_org = 0.76

assert (
round(
math.sqrt(
(
(coords_2000[0] - coords_org[0])
* (coords_2000[0] - coords_org[0])
)
+ (
(coords_2000[1] - coords_org[1])
* (coords_2000[1] - coords_org[1])
)
),
2,
)
== dif_2000_org
)
assert (
round(
math.sqrt(
(
(coords_2020[0] - coords_org[0])
* (coords_2020[0] - coords_org[0])
)
+ (
(coords_2020[1] - coords_org[1])
* (coords_2020[1] - coords_org[1])
)
),
2,
)
== dif_2020_org
)


def test_wgs_epoch():
with open("tests/data/test_wgs_epoch.json") as f:
data = json.load(f)
Expand Down
19 changes: 18 additions & 1 deletion tests/test_transformer_selection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import pytest
from coordinate_transformation_api.crs_transform import get_transformer, needs_epoch
from coordinate_transformation_api.crs_transform import (
build_input_coord,
get_transformer,
needs_epoch,
)


# This test needs the modified proj.time.dependent.transformations.db from
Expand All @@ -17,3 +21,16 @@
)
def test_time_dependant_operation_method(source, target, epoch, expectation):
assert needs_epoch(get_transformer(source, target, epoch)) == expectation


@pytest.mark.parametrize(
("coord", "epoch", "expectation"),
[
(tuple([1000.0, 1000.0]), 2000.0, tuple([1000.0, 1000.0, 0.0, 2000.0])),
(tuple([1000.0, 1000.0]), None, tuple([1000.0, 1000.0, None])),
(tuple([1000.0, 1000.0, 10.0]), 2000.0, tuple([1000.0, 1000.0, 10.0, 2000.0])),
(tuple([1000.0, 1000.0, 10.0]), None, tuple([1000.0, 1000.0, 10.0, None])),
],
)
def test_build_input_coord(coord, epoch, expectation):
assert build_input_coord(coord, epoch) == expectation
Loading