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

1from pyproj import CRS as ProjCrs # noqa: N811 

2from pyproj import Transformer 

3 

4DEFAULT_MAX_SEGMENT_LENGTH = 200 

5DEFAULT_PRECISION_DEGREES = 9 # digits 

6DEFAULT_PRECISION_METERS = 4 # digits 

7 

8 

9class GeodenseError(Exception): 

10 pass 

11 

12 

13class DenseConfig: 

14 geod_empty_exc_message = "DenseConfig.geod is None, cannot perform geodetic calculations without pyproj.Geod object" 

15 

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 

23 

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 

43 

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 

48 

49 def _get_base_crs(self: "DenseConfig") -> ProjCrs: 

50 if self.src_crs is None: 

51 raise GeodenseError("field src_proj is None") 

52 

53 if self.src_crs.is_geographic: 

54 raise GeodenseError("cannot get base_crs for geographic crs") 

55 

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"]) 

63 

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