Skip to content

Commit

Permalink
Merged PR 2553: Refactor the Projector
Browse files Browse the repository at this point in the history
**Breaking change**

- Removed the Projector.get_transformer() method and changed the method signature to Projector.transform(from_srid, to_srid, from_east, from_north).
- Update CHANGES.md.
- Bump version number to 0.0.3.

Related work items: #5559
  • Loading branch information
josteinl committed Mar 8, 2022
2 parents 09605e9 + f68d59f commit ee1fd8b
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 113 deletions.
6 changes: 4 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# NGI Python Projector Package

_2022-03-07_
_2022-03-08_

Version 0.0.2
Version 0.0.3

- Breaking change: Removed the Projector.get_transformer() method and changed the
method signature to Projector.transform(from_srid, to_srid, from_east, from_north).
- Add two new methods ensure_tz() and datetime_to_json().

_2022-03-02_
Expand Down
3 changes: 1 addition & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ to_srid = "3857"
# Paris Lat(48.8589506) Lon(2.2768485) EPSG:4326
from_east, from_north = 2.2768485, 48.8589506

transformer = projector.get_transformer(f"{from_srid}-{to_srid}")
projected_east, projected_north = projector.transform(transformer, from_east, from_north)
projected_east, projected_north = projector.transform(from_srid, to_srid, from_east, from_north)

# Paris Lat(6250962.06) Lon(253457.62) EPSG:3857 is in metres - 2D projection
assert abs(projected_east - 253457.62) <= 0.01
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ exclude = '''

[tool.poetry]
name = "ngi-projector"
version = "0.0.2"
version = "0.0.3"
description = "Project points from one projection to another using pyproj"
authors = ["Helge Smebye", "Jostein Leira"]

Expand Down
20 changes: 10 additions & 10 deletions src/ngi_projector/datetime_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@


def ensure_tz(
dt: Optional[datetime],
longitude: Optional[float] = None,
latitude: Optional[float] = None,
srid: int = 4326,
dt: Optional[datetime],
longitude: Optional[float] = None,
latitude: Optional[float] = None,
srid: int = 4326,
) -> Optional[datetime]:
"""
Return passed datetime dt enriched with timezone.
Expand All @@ -42,8 +42,8 @@ def ensure_tz(
if longitude is not None and latitude is not None:
if srid and srid != 4326:
# TODO: Use a single common transformer for the whole library
transformer = projector.get_transformer(f"{srid}-4326")
longitude, latitude = projector.transform(transformer, longitude, latitude)

longitude, latitude = projector.transform(from_srid=srid, to_srid=4326, east=longitude, north=latitude)

# find timezone from position
input_timezone = tz.gettz(_time_zone_finder.timezone_at(lng=longitude, lat=latitude))
Expand All @@ -57,10 +57,10 @@ def ensure_tz(


def datetime_to_json(
dt: Optional[datetime],
longitude: Optional[float] = None,
latitude: Optional[float] = None,
srid: int = 4326,
dt: Optional[datetime],
longitude: Optional[float] = None,
latitude: Optional[float] = None,
srid: int = 4326,
) -> Optional[str]:
"""
Return passed datetime.datetime as json-formatted (iso-8601) string with UTC timezone. Sub-second time information
Expand Down
47 changes: 27 additions & 20 deletions src/ngi_projector/projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,39 @@
from typing import Dict, Optional
from .projections import projections

_transformers: Dict[str, Transformer] = {}

class Projector:
_transformers: Dict[str, Transformer] = {}

def get_supported_projections(self) -> Dict:
class Projector:
@staticmethod
def get_supported_projections() -> Dict:
return projections

def get_transformer(self, transformDef: str) -> Transformer:
@staticmethod
def _get_transformer(from_srid: int, to_srid: int) -> Transformer:

if transformer := self._transformers.get(transformDef):
global _transformers

if transformer := _transformers.get(f"{from_srid}-{to_srid}"):
return transformer

from_def_str, to_def_str = transformDef.split("-")
fromDefDesc: Optional[Dict] = projections.get(from_def_str)
if not fromDefDesc:
raise Exception(f"SRID: {from_def_str} is not supported")
fromDef = fromDefDesc["definition"]["data"]

toDefDesc: Optional[Dict] = projections.get(to_def_str)
if not toDefDesc:
raise Exception(f"SRID: {to_def_str} is not supported")
toDef = toDefDesc["definition"]["data"]
transformer = Transformer.from_crs(fromDef, toDef)
self._transformers[transformDef] = transformer
from_def_desc: Optional[Dict] = projections.get(str(from_srid))
if not from_def_desc:
raise Exception(f"SRID: {from_srid} is not supported")
from_definition = from_def_desc["definition"]["data"]

to_def_desc: Optional[Dict] = projections.get(str(to_srid))
if not to_def_desc:
raise Exception(f"SRID: {to_srid} is not supported")
to_definition = to_def_desc["definition"]["data"]

transformer = Transformer.from_crs(from_definition, to_definition)

_transformers[f"{from_srid}-{to_srid}"] = transformer

return transformer

def transform(self, transformer: Transformer, east: float, north: float) -> tuple[float, float]:
eastPyProj, northPyProj = transformer.transform(east, north)
return eastPyProj, northPyProj
def transform(self, from_srid: int, to_srid: int, east: float, north: float) -> tuple[float, float]:
transformer: Transformer = self._get_transformer(from_srid, to_srid)
projected_east, projected_north = transformer.transform(east, north)
return projected_east, projected_north
11 changes: 4 additions & 7 deletions tests/data/coords.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
from typing import Dict


def getNTM(ntmZone: int):
def getNTM(ntm_zone: int) -> int:
for objs in Coords:
coordSet = Coords[objs]
if coordSet["NTMZONE"] == ntmZone:
return coordSet


# def getToNTMCoords(transfID,str):
coord_set = Coords[objs]
if coord_set["NTMZONE"] == ntm_zone:
return coord_set


ProjSets = {
Expand Down
141 changes: 70 additions & 71 deletions tests/test_projector.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,85 +9,84 @@ class TestParse:
projector = Projector()

@pytest.mark.parametrize(
"transfDef",
"from_srid,to_srid",
[
("25832-5105"),
("25832-5105"),
("25832-5106"),
("25832-5107"),
("25832-5108"),
("25832-5109"),
("25832-5110"),
("25832-5111"),
("25833-5112"),
("25833-5113"),
("25833-5114"),
("25833-5115"),
("25833-5116"),
("25833-5117"),
("25834-5118"),
("25834-5119"),
("25834-5120"),
("25834-5121"),
("25834-5122"),
("25834-5123"),
("25835-5124"),
("25835-5125"),
("25835-5126"),
("25835-5127"),
("25835-5128"),
("25835-5129"),
("25836-5130"),
("25831-23031"),
("25832-23032"),
("25833-23033"),
("25834-23034"),
("25835-23035"),
("25836-23036"),
("25830-3857"),
("25831-3857"),
("25832-3857"),
("25833-3857"),
("25834-3857"),
("25835-3857"),
("25836-3857"),
("25830-4326"),
("25831-4326"),
("25832-4326"),
("25833-4326"),
("25834-4326"),
("25835-4326"),
("25836-4326"),
(25832, 5105),
(25832, 5105),
(25832, 5106),
(25832, 5107),
(25832, 5108),
(25832, 5109),
(25832, 5110),
(25832, 5111),
(25833, 5112),
(25833, 5113),
(25833, 5114),
(25833, 5115),
(25833, 5116),
(25833, 5117),
(25834, 5118),
(25834, 5119),
(25834, 5120),
(25834, 5121),
(25834, 5122),
(25834, 5123),
(25835, 5124),
(25835, 5125),
(25835, 5126),
(25835, 5127),
(25835, 5128),
(25835, 5129),
(25836, 5130),
(25831, 23031),
(25832, 23032),
(25833, 23033),
(25834, 23034),
(25835, 23035),
(25836, 23036),
(25830, 3857),
(25831, 3857),
(25832, 3857),
(25833, 3857),
(25834, 3857),
(25835, 3857),
(25836, 3857),
(25830, 4326),
(25831, 4326),
(25832, 4326),
(25833, 4326),
(25834, 4326),
(25835, 4326),
(25836, 4326),
],
)
def test_project(self, transfDef):
def test_project(self, from_srid, to_srid):
trans_def = f"{from_srid}-{to_srid}"
proj_set = ProjSets[trans_def]
id = proj_set["ID"]
fc = proj_set["fromCoord"]
tc = proj_set["toCoord"]
expected_accuracy = proj_set["expectedAccuracy"]

projSet = ProjSets[transfDef]
id = projSet["ID"]
fc = projSet["fromCoord"]
tc = projSet["toCoord"]
expectedAccuracy = projSet["expectedAccuracy"]
from_east = Coords[id][fc][0]
from_north = Coords[id][fc][1]

fromEast = Coords[id][fc][0]
fromNorth = Coords[id][fc][1]
to_east = Coords[id][tc][0]
to_north = Coords[id][tc][1]

toEast = Coords[id][tc][0]
toNorth = Coords[id][tc][1]
projected_east, projected_north = self.projector.transform(from_srid, to_srid, from_east, from_north)

transformer = self.projector.get_transformer(transfDef)
pEast, pNorth = self.projector.transform(transformer, fromEast, fromNorth)

assert pEast == pytest.approx(toEast, expectedAccuracy)
assert pNorth == pytest.approx(toNorth, expectedAccuracy)
assert projected_east == pytest.approx(to_east, expected_accuracy)
assert projected_north == pytest.approx(to_north, expected_accuracy)

# Reverse the projection - use swapped coords
p1, p2 = transfDef.split("-")
reverseTransDef = f"{p2}-{p1}"
transformer = self.projector.get_transformer(reverseTransDef)
pEast, pNorth = self.projector.transform(transformer, toEast, toNorth)
assert pEast == pytest.approx(fromEast, expectedAccuracy)
assert pNorth == pytest.approx(fromNorth, expectedAccuracy)
projected_east, projected_north = self.projector.transform(to_srid, from_srid, to_east, to_north)
assert projected_east == pytest.approx(from_east, expected_accuracy)
assert projected_north == pytest.approx(from_north, expected_accuracy)

def test_get_supported_projections(self):
supportedProjections = self.projector.get_supported_projections()
print(f"Supported projections: {supportedProjections}")
supported_projections = self.projector.get_supported_projections()
assert "23031" in supported_projections
assert "4326" in supported_projections
assert "5130" in supported_projections
assert "5105" in supported_projections

0 comments on commit ee1fd8b

Please sign in to comment.