| # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc. |
| # |
| # Permission to use, copy, modify, and distribute this software and its |
| # documentation for any purpose with or without fee is hereby granted, |
| # provided that the above copyright notice and this permission notice |
| # appear in all copies. |
| # |
| # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES |
| # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR |
| # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
| # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| |
| import cStringIO |
| import struct |
| |
| import dns.exception |
| import dns.rdata |
| |
| _pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L, |
| 100000000L, 1000000000L, 10000000000L) |
| |
| def _exponent_of(what, desc): |
| exp = None |
| for i in xrange(len(_pows)): |
| if what // _pows[i] == 0L: |
| exp = i - 1 |
| break |
| if exp is None or exp < 0: |
| raise dns.exception.SyntaxError("%s value out of bounds" % desc) |
| return exp |
| |
| def _float_to_tuple(what): |
| if what < 0: |
| sign = -1 |
| what *= -1 |
| else: |
| sign = 1 |
| what = long(round(what * 3600000)) |
| degrees = int(what // 3600000) |
| what -= degrees * 3600000 |
| minutes = int(what // 60000) |
| what -= minutes * 60000 |
| seconds = int(what // 1000) |
| what -= int(seconds * 1000) |
| what = int(what) |
| return (degrees * sign, minutes, seconds, what) |
| |
| def _tuple_to_float(what): |
| if what[0] < 0: |
| sign = -1 |
| value = float(what[0]) * -1 |
| else: |
| sign = 1 |
| value = float(what[0]) |
| value += float(what[1]) / 60.0 |
| value += float(what[2]) / 3600.0 |
| value += float(what[3]) / 3600000.0 |
| return sign * value |
| |
| def _encode_size(what, desc): |
| what = long(what); |
| exponent = _exponent_of(what, desc) & 0xF |
| base = what // pow(10, exponent) & 0xF |
| return base * 16 + exponent |
| |
| def _decode_size(what, desc): |
| exponent = what & 0x0F |
| if exponent > 9: |
| raise dns.exception.SyntaxError("bad %s exponent" % desc) |
| base = (what & 0xF0) >> 4 |
| if base > 9: |
| raise dns.exception.SyntaxError("bad %s base" % desc) |
| return long(base) * pow(10, exponent) |
| |
| class LOC(dns.rdata.Rdata): |
| """LOC record |
| |
| @ivar latitude: latitude |
| @type latitude: (int, int, int, int) tuple specifying the degrees, minutes, |
| seconds, and milliseconds of the coordinate. |
| @ivar longitude: longitude |
| @type longitude: (int, int, int, int) tuple specifying the degrees, |
| minutes, seconds, and milliseconds of the coordinate. |
| @ivar altitude: altitude |
| @type altitude: float |
| @ivar size: size of the sphere |
| @type size: float |
| @ivar horizontal_precision: horizontal precision |
| @type horizontal_precision: float |
| @ivar vertical_precision: vertical precision |
| @type vertical_precision: float |
| @see: RFC 1876""" |
| |
| __slots__ = ['latitude', 'longitude', 'altitude', 'size', |
| 'horizontal_precision', 'vertical_precision'] |
| |
| def __init__(self, rdclass, rdtype, latitude, longitude, altitude, |
| size=1.0, hprec=10000.0, vprec=10.0): |
| """Initialize a LOC record instance. |
| |
| The parameters I{latitude} and I{longitude} may be either a 4-tuple |
| of integers specifying (degrees, minutes, seconds, milliseconds), |
| or they may be floating point values specifying the number of |
| degrees. The other parameters are floats.""" |
| |
| super(LOC, self).__init__(rdclass, rdtype) |
| if isinstance(latitude, int) or isinstance(latitude, long): |
| latitude = float(latitude) |
| if isinstance(latitude, float): |
| latitude = _float_to_tuple(latitude) |
| self.latitude = latitude |
| if isinstance(longitude, int) or isinstance(longitude, long): |
| longitude = float(longitude) |
| if isinstance(longitude, float): |
| longitude = _float_to_tuple(longitude) |
| self.longitude = longitude |
| self.altitude = float(altitude) |
| self.size = float(size) |
| self.horizontal_precision = float(hprec) |
| self.vertical_precision = float(vprec) |
| |
| def to_text(self, origin=None, relativize=True, **kw): |
| if self.latitude[0] > 0: |
| lat_hemisphere = 'N' |
| lat_degrees = self.latitude[0] |
| else: |
| lat_hemisphere = 'S' |
| lat_degrees = -1 * self.latitude[0] |
| if self.longitude[0] > 0: |
| long_hemisphere = 'E' |
| long_degrees = self.longitude[0] |
| else: |
| long_hemisphere = 'W' |
| long_degrees = -1 * self.longitude[0] |
| text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % ( |
| lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3], |
| lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2], |
| self.longitude[3], long_hemisphere, self.altitude / 100.0 |
| ) |
| |
| if self.size != 1.0 or self.horizontal_precision != 10000.0 or \ |
| self.vertical_precision != 10.0: |
| text += " %0.2fm %0.2fm %0.2fm" % ( |
| self.size / 100.0, self.horizontal_precision / 100.0, |
| self.vertical_precision / 100.0 |
| ) |
| return text |
| |
| def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True): |
| latitude = [0, 0, 0, 0] |
| longitude = [0, 0, 0, 0] |
| size = 1.0 |
| hprec = 10000.0 |
| vprec = 10.0 |
| |
| latitude[0] = tok.get_int() |
| t = tok.get_string() |
| if t.isdigit(): |
| latitude[1] = int(t) |
| t = tok.get_string() |
| if '.' in t: |
| (seconds, milliseconds) = t.split('.') |
| if not seconds.isdigit(): |
| raise dns.exception.SyntaxError('bad latitude seconds value') |
| latitude[2] = int(seconds) |
| if latitude[2] >= 60: |
| raise dns.exception.SyntaxError('latitude seconds >= 60') |
| l = len(milliseconds) |
| if l == 0 or l > 3 or not milliseconds.isdigit(): |
| raise dns.exception.SyntaxError('bad latitude milliseconds value') |
| if l == 1: |
| m = 100 |
| elif l == 2: |
| m = 10 |
| else: |
| m = 1 |
| latitude[3] = m * int(milliseconds) |
| t = tok.get_string() |
| elif t.isdigit(): |
| latitude[2] = int(t) |
| t = tok.get_string() |
| if t == 'S': |
| latitude[0] *= -1 |
| elif t != 'N': |
| raise dns.exception.SyntaxError('bad latitude hemisphere value') |
| |
| longitude[0] = tok.get_int() |
| t = tok.get_string() |
| if t.isdigit(): |
| longitude[1] = int(t) |
| t = tok.get_string() |
| if '.' in t: |
| (seconds, milliseconds) = t.split('.') |
| if not seconds.isdigit(): |
| raise dns.exception.SyntaxError('bad longitude seconds value') |
| longitude[2] = int(seconds) |
| if longitude[2] >= 60: |
| raise dns.exception.SyntaxError('longitude seconds >= 60') |
| l = len(milliseconds) |
| if l == 0 or l > 3 or not milliseconds.isdigit(): |
| raise dns.exception.SyntaxError('bad longitude milliseconds value') |
| if l == 1: |
| m = 100 |
| elif l == 2: |
| m = 10 |
| else: |
| m = 1 |
| longitude[3] = m * int(milliseconds) |
| t = tok.get_string() |
| elif t.isdigit(): |
| longitude[2] = int(t) |
| t = tok.get_string() |
| if t == 'W': |
| longitude[0] *= -1 |
| elif t != 'E': |
| raise dns.exception.SyntaxError('bad longitude hemisphere value') |
| |
| t = tok.get_string() |
| if t[-1] == 'm': |
| t = t[0 : -1] |
| altitude = float(t) * 100.0 # m -> cm |
| |
| token = tok.get().unescape() |
| if not token.is_eol_or_eof(): |
| value = token.value |
| if value[-1] == 'm': |
| value = value[0 : -1] |
| size = float(value) * 100.0 # m -> cm |
| token = tok.get().unescape() |
| if not token.is_eol_or_eof(): |
| value = token.value |
| if value[-1] == 'm': |
| value = value[0 : -1] |
| hprec = float(value) * 100.0 # m -> cm |
| token = tok.get().unescape() |
| if not token.is_eol_or_eof(): |
| value = token.value |
| if value[-1] == 'm': |
| value = value[0 : -1] |
| vprec = float(value) * 100.0 # m -> cm |
| tok.get_eol() |
| |
| return cls(rdclass, rdtype, latitude, longitude, altitude, |
| size, hprec, vprec) |
| |
| from_text = classmethod(from_text) |
| |
| def to_wire(self, file, compress = None, origin = None): |
| if self.latitude[0] < 0: |
| sign = -1 |
| degrees = long(-1 * self.latitude[0]) |
| else: |
| sign = 1 |
| degrees = long(self.latitude[0]) |
| milliseconds = (degrees * 3600000 + |
| self.latitude[1] * 60000 + |
| self.latitude[2] * 1000 + |
| self.latitude[3]) * sign |
| latitude = 0x80000000L + milliseconds |
| if self.longitude[0] < 0: |
| sign = -1 |
| degrees = long(-1 * self.longitude[0]) |
| else: |
| sign = 1 |
| degrees = long(self.longitude[0]) |
| milliseconds = (degrees * 3600000 + |
| self.longitude[1] * 60000 + |
| self.longitude[2] * 1000 + |
| self.longitude[3]) * sign |
| longitude = 0x80000000L + milliseconds |
| altitude = long(self.altitude) + 10000000L |
| size = _encode_size(self.size, "size") |
| hprec = _encode_size(self.horizontal_precision, "horizontal precision") |
| vprec = _encode_size(self.vertical_precision, "vertical precision") |
| wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude, |
| longitude, altitude) |
| file.write(wire) |
| |
| def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None): |
| (version, size, hprec, vprec, latitude, longitude, altitude) = \ |
| struct.unpack("!BBBBIII", wire[current : current + rdlen]) |
| if latitude > 0x80000000L: |
| latitude = float(latitude - 0x80000000L) / 3600000 |
| else: |
| latitude = -1 * float(0x80000000L - latitude) / 3600000 |
| if latitude < -90.0 or latitude > 90.0: |
| raise dns.exception.FormError("bad latitude") |
| if longitude > 0x80000000L: |
| longitude = float(longitude - 0x80000000L) / 3600000 |
| else: |
| longitude = -1 * float(0x80000000L - longitude) / 3600000 |
| if longitude < -180.0 or longitude > 180.0: |
| raise dns.exception.FormError("bad longitude") |
| altitude = float(altitude) - 10000000.0 |
| size = _decode_size(size, "size") |
| hprec = _decode_size(hprec, "horizontal precision") |
| vprec = _decode_size(vprec, "vertical precision") |
| return cls(rdclass, rdtype, latitude, longitude, altitude, |
| size, hprec, vprec) |
| |
| from_wire = classmethod(from_wire) |
| |
| def _cmp(self, other): |
| f = cStringIO.StringIO() |
| self.to_wire(f) |
| wire1 = f.getvalue() |
| f.seek(0) |
| f.truncate() |
| other.to_wire(f) |
| wire2 = f.getvalue() |
| f.close() |
| |
| return cmp(wire1, wire2) |
| |
| def _get_float_latitude(self): |
| return _tuple_to_float(self.latitude) |
| |
| def _set_float_latitude(self, value): |
| self.latitude = _float_to_tuple(value) |
| |
| float_latitude = property(_get_float_latitude, _set_float_latitude, |
| doc="latitude as a floating point value") |
| |
| def _get_float_longitude(self): |
| return _tuple_to_float(self.longitude) |
| |
| def _set_float_longitude(self, value): |
| self.longitude = _float_to_tuple(value) |
| |
| float_longitude = property(_get_float_longitude, _set_float_longitude, |
| doc="longitude as a floating point value") |