Coverage for C: \ Users \ peaco \ OneDrive \ Documents \ GitHub \ mt_metadata \ mt_metadata \ utils \ location_helpers.py: 77%
66 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-10 00:11 -0800
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-10 00:11 -0800
1# ===============================================================
2# imports
3# ===============================================================
4import numpy as np
5from loguru import logger
8# ===============================================================
11def convert_position_float2str(position: float | int) -> str:
12 """
13 Convert position float to a string in the format of DD:MM:SS.ms
15 Parameters
16 ----------
17 position : float | int
18 decimal degrees of latitude or longitude
20 Returns
21 -------
22 str
23 latitude or longitude in format of DD:MM:SS.ms
24 """
25 if not isinstance(position, (float, int)):
26 msg = f"position must be a float or int, not {type(position)}"
27 logger.error(msg)
28 raise TypeError(msg)
30 deg = int(position)
31 sign = 1
32 if deg < 0:
33 sign = -1
35 deg = abs(deg)
36 minutes = (abs(position) - deg) * 60.0
37 # need to round seconds to 4 decimal places otherwise machine precision
38 # keeps the 60 second roll over and the string is incorrect.
39 sec = np.round((minutes - int(minutes)) * 60.0, 4)
40 if sec >= 60.0:
41 minutes += 1
42 sec = 0
44 if int(minutes) == 60:
45 deg += 1
46 minutes = 0
48 position_str = f"{sign * int(deg)}:{int(minutes):02.0f}:{sec:05.6f}"
49 logger.debug(f"Converted {position} to {position_str}")
51 return position_str
54def convert_position_str2float(position_str: str) -> float:
55 """
56 Convert a position string in the format of DD:MM:SS to decimal degrees
58 :param position: latitude or longitude om DD:MM:SS.ms
59 :type position: float
61 :returns: latitude or longitude as a float
63 Parameters
64 ----------
65 position_str : str
66 latitude or longitude om DD:MM:SS.ms
68 Returns
69 -------
70 float
71 latitude or longitude as a float
73 Raises
74 ------
75 ValueError
76 If position string cannot be converted to a float or
77 if the format is incorrect.
78 """
80 if position_str in [None, "None"]:
81 return 0.0
83 p_list = position_str.split(":")
84 if len(p_list) != 3:
85 msg = f"{position_str} not correct format, should be DD:MM:SS"
86 logger.error(msg)
87 raise ValueError(msg)
89 deg = float(p_list[0])
90 minutes = _assert_minutes(float(p_list[1]))
91 sec = _assert_seconds(float(p_list[2]))
93 # get the sign of the position so that when all are added together the
94 # position is in the correct place
95 sign = 1
96 if deg < 0:
97 sign = -1
99 position_value = sign * (abs(deg) + minutes / 60.0 + sec / 3600.0)
101 logger.debug(f"Converted {position_str} to {position_value}")
103 return position_value
106def _assert_minutes(minutes: float | int) -> float:
107 """
108 Assert minutes are between 0 and 60
110 Parameters
111 ----------
112 minutes : float | int
113 number of minutesto be checked
115 Returns
116 -------
117 float
118 number of minutes if valid
119 """
120 if not 0 <= minutes < 60.0:
121 msg = (
122 f"minutes should be 0 < > 60, currently {minutes:.0f} "
123 "conversion will account for non-uniform "
124 "time. Be sure to check accuracy."
125 )
126 logger.warning(msg)
128 return minutes
131def _assert_seconds(seconds: float | int) -> float:
132 """
133 Assert seconds are between 0 and 60
135 Parameters
136 ----------
137 seconds : float | int
138 number of seconds to be checked
140 Returns
141 -------
142 float
143 number of seconds if valid
144 """
145 if not 0 <= seconds < 60.0:
146 msg = (
147 f"seconds should be 0 < > 60, currently {seconds:.0f} "
148 "conversion will account for non-uniform "
149 "time. Be sure to check accuracy."
150 )
151 logger.warning(msg)
153 return seconds
156def validate_position(value: str | float, position_type: str) -> float:
157 """
158 Validate position value (latitude or longitude) and convert to float.
160 Parameters
161 ----------
162 value : str | float
163 The position value to validate and convert.
164 position_type : str
165 The type of position ('latitude' or 'longitude').
167 Returns
168 -------
169 float
170 The validated and converted position value.
172 Raises
173 ------
174 ValueError
175 If the value is not a valid latitude or longitude.
176 """
177 if position_type not in ["latitude", "longitude"]:
178 raise ValueError("position_type must be 'latitude' or 'longitude'")
180 if value in [None, "None"]:
181 return 0.0
183 elif isinstance(value, str) and ":" in value:
184 value = convert_position_str2float(value)
185 else:
186 try:
187 value = float(value)
188 except ValueError:
189 raise ValueError("latitude and longitude must be float or str")
191 if not (abs(value) <= 90) and position_type in ["latitude", "lat"]:
192 raise ValueError("latitude must be between -90 and 90 degrees")
193 if not (abs(value) <= 180) and position_type in ["longitude", "lon"]:
194 raise ValueError("longitude must be between -180 and 180 degrees")
195 return value