Coverage for src/geodense/models.py: 89%
44 statements
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-11 14:11 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2024-10-11 14:11 +0000
1from pyproj import CRS as ProjCrs # noqa: N811
2from pyproj import Transformer
4DEFAULT_MAX_SEGMENT_LENGTH = 200
5DEFAULT_PRECISION_DEGREES = 9 # digits
6DEFAULT_PRECISION_METERS = 4 # digits
9class GeodenseError(Exception):
10 pass
13class DenseConfig:
14 geod_empty_exc_message = "DenseConfig.geod is None, cannot perform geodetic calculations without pyproj.Geod object"
16 def __init__(
17 self: "DenseConfig",
18 src_crs: ProjCrs,
19 max_segment_length: float | None = None,
20 in_projection: bool = False,
21 ) -> None:
22 self.src_crs = src_crs
24 if self.src_crs.is_geographic and in_projection:
25 raise GeodenseError(
26 f"in_projection can only be used with \
27projected coordinate reference systems, crs {self.src_crs} is a geographic crs"
28 )
29 self.back_transformer = None
30 if self.src_crs.is_geographic:
31 _geod = self.src_crs.get_geod()
32 self.transformer = None
33 elif self.src_crs.is_projected:
34 base_crs = self._get_base_crs()
35 self.transformer = Transformer.from_crs(src_crs, base_crs, always_xy=True)
36 _geod = self.src_crs.get_geod()
37 self.back_transformer = Transformer.from_crs(base_crs, src_crs, always_xy=True)
38 else:
39 raise GeodenseError("unexpected crs encountered, crs is neither geographic nor projected")
40 if _geod is None:
41 raise GeodenseError(self.geod_empty_exc_message)
42 self.geod = _geod
44 self.in_projection = in_projection
45 self.max_segment_length = abs(
46 max_segment_length or DEFAULT_MAX_SEGMENT_LENGTH
47 ) # when max_segment_length == None -> DEFAULT_MAX_SEGMENT_LENGTH
49 def _get_base_crs(self: "DenseConfig") -> ProjCrs:
50 if self.src_crs is None:
51 raise GeodenseError("field src_proj is None")
53 if self.src_crs.is_geographic:
54 raise GeodenseError("cannot get base_crs for geographic crs")
56 crs_dict = self.src_crs.to_json_dict()
57 if crs_dict["type"] == "ProjectedCRS":
58 base_crs_id = self.src_crs.to_json_dict()["base_crs"]["id"]
59 elif crs_dict["type"] == "CompoundCRS":
60 projected_crs = next(x for x in crs_dict["components"] if x["type"] == "ProjectedCRS")
61 base_crs_id = projected_crs["base_crs"]["id"]
62 return ProjCrs.from_authority(base_crs_id["authority"], base_crs_id["code"])
64 def get_coord_precision(self: "DenseConfig") -> int:
65 if self.src_crs is None:
66 raise GeodenseError("DensifyConfig.source_crs is None")
67 return DEFAULT_PRECISION_DEGREES if self.src_crs.is_geographic else DEFAULT_PRECISION_METERS