#
#
#
from ..equality import EqualityTupleMixin
from .base import Record, ValuesMixin, unquote
from .rr import RrParseError
[docs]
class LocValue(EqualityTupleMixin, dict):
# TODO: this does not really match the RFC, but it's stuck using the details
# of how the type was impelemented. Would be nice to rework things to match
# while maintaining backwards compatibility.
# https://www.rfc-editor.org/rfc/rfc1876.html
[docs]
@classmethod
def parse_rdata_text(cls, value):
try:
value = value.replace('m', '')
(
lat_degrees,
lat_minutes,
lat_seconds,
lat_direction,
long_degrees,
long_minutes,
long_seconds,
long_direction,
altitude,
size,
precision_horz,
precision_vert,
) = value.split(' ')
except ValueError:
raise RrParseError()
try:
lat_degrees = int(lat_degrees)
except ValueError:
pass
try:
lat_minutes = int(lat_minutes)
except ValueError:
pass
try:
long_degrees = int(long_degrees)
except ValueError:
pass
try:
long_minutes = int(long_minutes)
except ValueError:
pass
try:
lat_seconds = float(lat_seconds)
except ValueError:
pass
try:
long_seconds = float(long_seconds)
except ValueError:
pass
try:
altitude = float(unquote(altitude))
except ValueError:
pass
try:
size = float(unquote(size))
except ValueError:
pass
try:
precision_horz = float(unquote(precision_horz))
except ValueError:
pass
try:
precision_vert = float(unquote(precision_vert))
except ValueError:
pass
lat_direction = unquote(lat_direction)
long_direction = unquote(long_direction)
return {
'lat_degrees': lat_degrees,
'lat_minutes': lat_minutes,
'lat_seconds': lat_seconds,
'lat_direction': lat_direction,
'long_degrees': long_degrees,
'long_minutes': long_minutes,
'long_seconds': long_seconds,
'long_direction': long_direction,
'altitude': altitude,
'size': size,
'precision_horz': precision_horz,
'precision_vert': precision_vert,
}
[docs]
@classmethod
def validate(cls, data, _type):
int_keys = [
'lat_degrees',
'lat_minutes',
'long_degrees',
'long_minutes',
]
float_keys = [
'lat_seconds',
'long_seconds',
'altitude',
'size',
'precision_horz',
'precision_vert',
]
direction_keys = ['lat_direction', 'long_direction']
reasons = []
for value in data:
for key in int_keys:
try:
int(value[key])
if (
(
key == 'lat_degrees'
and not 0 <= int(value[key]) <= 90
)
or (
key == 'long_degrees'
and not 0 <= int(value[key]) <= 180
)
or (
key in ['lat_minutes', 'long_minutes']
and not 0 <= int(value[key]) <= 59
)
):
reasons.append(
f'invalid value for {key} ' f'"{value[key]}"'
)
except KeyError:
reasons.append(f'missing {key}')
except ValueError:
reasons.append(f'invalid {key} "{value[key]}"')
for key in float_keys:
try:
float(value[key])
if (
(
key in ['lat_seconds', 'long_seconds']
and not 0 <= float(value[key]) <= 59.999
)
or (
key == 'altitude'
and not -100000.00
<= float(value[key])
<= 42849672.95
)
or (
key in ['size', 'precision_horz', 'precision_vert']
and not 0 <= float(value[key]) <= 90000000.00
)
):
reasons.append(
f'invalid value for {key} ' f'"{value[key]}"'
)
except KeyError:
reasons.append(f'missing {key}')
except ValueError:
reasons.append(f'invalid {key} "{value[key]}"')
for key in direction_keys:
try:
str(value[key])
if key == 'lat_direction' and value[key] not in ['N', 'S']:
reasons.append(
f'invalid direction for {key} ' f'"{value[key]}"'
)
if key == 'long_direction' and value[key] not in ['E', 'W']:
reasons.append(
f'invalid direction for {key} ' f'"{value[key]}"'
)
except KeyError:
reasons.append(f'missing {key}')
return reasons
[docs]
@classmethod
def process(cls, values):
return [cls(v) for v in values]
[docs]
def __init__(self, value):
super().__init__(
{
'lat_degrees': int(value['lat_degrees']),
'lat_minutes': int(value['lat_minutes']),
'lat_seconds': float(value['lat_seconds']),
'lat_direction': value['lat_direction'].upper(),
'long_degrees': int(value['long_degrees']),
'long_minutes': int(value['long_minutes']),
'long_seconds': float(value['long_seconds']),
'long_direction': value['long_direction'].upper(),
'altitude': float(value['altitude']),
'size': float(value['size']),
'precision_horz': float(value['precision_horz']),
'precision_vert': float(value['precision_vert']),
}
)
@property
def lat_degrees(self):
return self['lat_degrees']
@lat_degrees.setter
def lat_degrees(self, value):
self['lat_degrees'] = value
@property
def lat_minutes(self):
return self['lat_minutes']
@lat_minutes.setter
def lat_minutes(self, value):
self['lat_minutes'] = value
@property
def lat_seconds(self):
return self['lat_seconds']
@lat_seconds.setter
def lat_seconds(self, value):
self['lat_seconds'] = value
@property
def lat_direction(self):
return self['lat_direction']
@lat_direction.setter
def lat_direction(self, value):
self['lat_direction'] = value
@property
def long_degrees(self):
return self['long_degrees']
@long_degrees.setter
def long_degrees(self, value):
self['long_degrees'] = value
@property
def long_minutes(self):
return self['long_minutes']
@long_minutes.setter
def long_minutes(self, value):
self['long_minutes'] = value
@property
def long_seconds(self):
return self['long_seconds']
@long_seconds.setter
def long_seconds(self, value):
self['long_seconds'] = value
@property
def long_direction(self):
return self['long_direction']
@long_direction.setter
def long_direction(self, value):
self['long_direction'] = value
@property
def altitude(self):
return self['altitude']
@altitude.setter
def altitude(self, value):
self['altitude'] = value
@property
def size(self):
return self['size']
@size.setter
def size(self, value):
self['size'] = value
@property
def precision_horz(self):
return self['precision_horz']
@precision_horz.setter
def precision_horz(self, value):
self['precision_horz'] = value
@property
def precision_vert(self):
return self['precision_vert']
@precision_vert.setter
def precision_vert(self, value):
self['precision_vert'] = value
@property
def data(self):
return self
@property
def rdata_text(self):
return f'{self.lat_degrees} {self.lat_minutes} {self.lat_seconds} {self.lat_direction} {self.long_degrees} {self.long_minutes} {self.long_seconds} {self.long_direction} {self.altitude}m {self.size}m {self.precision_horz}m {self.precision_vert}m'
def __hash__(self):
return hash(
(
self.lat_degrees,
self.lat_minutes,
self.lat_seconds,
self.lat_direction,
self.long_degrees,
self.long_minutes,
self.long_seconds,
self.long_direction,
self.altitude,
self.size,
self.precision_horz,
self.precision_vert,
)
)
[docs]
def _equality_tuple(self):
return (
self.lat_degrees,
self.lat_minutes,
self.lat_seconds,
self.lat_direction,
self.long_degrees,
self.long_minutes,
self.long_seconds,
self.long_direction,
self.altitude,
self.size,
self.precision_horz,
self.precision_vert,
)
[docs]
def __repr__(self):
return (
f"'{self.lat_degrees} {self.lat_minutes} "
f"{self.lat_seconds:.3f} {self.lat_direction} "
f"{self.long_degrees} {self.long_minutes} "
f"{self.long_seconds:.3f} {self.long_direction} "
f"{self.altitude:.2f}m {self.size:.2f}m "
f"{self.precision_horz:.2f}m {self.precision_vert:.2f}m'"
)
[docs]
class LocRecord(ValuesMixin, Record):
_type = 'LOC'
_value_type = LocValue
Record.register_type(LocRecord)