#
|
|
#
|
|
#
|
|
|
|
from ..equality import EqualityTupleMixin
|
|
from .base import Record, ValuesMixin, unquote
|
|
from .rr import RrParseError
|
|
|
|
|
|
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
|
|
|
|
@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,
|
|
}
|
|
|
|
@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']
|
|
|
|
if not isinstance(data, (list, tuple)):
|
|
data = (data,)
|
|
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
|
|
|
|
@classmethod
|
|
def process(cls, values):
|
|
return [cls(v) for v in values]
|
|
|
|
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,
|
|
)
|
|
)
|
|
|
|
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,
|
|
)
|
|
|
|
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'"
|
|
)
|
|
|
|
|
|
class LocRecord(ValuesMixin, Record):
|
|
_type = 'LOC'
|
|
_value_type = LocValue
|
|
|
|
|
|
Record.register_type(LocRecord)
|