Package dns :: Package rdtypes :: Package ANY :: Module LOC |
[hide private]
[frames] | no frames]

# Source Code for Module dns.rdtypes.ANY.LOC

      1  # Copyright (C) 2003-2007, 2009-2011 Nominum, Inc.
      2  #
      3  # Permission to use, copy, modify, and distribute this software and its
      4  # documentation for any purpose with or without fee is hereby granted,
      5  # provided that the above copyright notice and this permission notice
      6  # appear in all copies.
      7  #
      8  # THE SOFTWARE IS PROVIDED "AS IS" AND NOMINUM DISCLAIMS ALL WARRANTIES
      9  # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     10  # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL NOMINUM BE LIABLE FOR
     11  # ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     12  # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     13  # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     14  # OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15
     16  from __future__ import division
     17
     18  import struct
     19
     20  import dns.exception
     21  import dns.rdata
     22  from dns._compat import long, xrange, round_py2_compat
     23
     24
     25  _pows = tuple(long(10**i) for i in range(0, 11))
     26
     27  # default values are in centimeters
     28  _default_size = 100.0
     29  _default_hprec = 1000000.0
     30  _default_vprec = 1000.0
     31
     32
     33 -def _exponent_of(what, desc):
     34      if what == 0:
     35          return 0
     36      exp = None
     37      for i in xrange(len(_pows)):
     38          if what // _pows[i] == long(0):
     39              exp = i - 1
     40              break
     41      if exp is None or exp < 0:
     42          raise dns.exception.SyntaxError("%s value out of bounds" % desc)
     43      return exp
     44
     45
     46 -def _float_to_tuple(what):
     47      if what < 0:
     48          sign = -1
     49          what *= -1
     50      else:
     51          sign = 1
     52      what = round_py2_compat(what * 3600000)
     53      degrees = int(what // 3600000)
     54      what -= degrees * 3600000
     55      minutes = int(what // 60000)
     56      what -= minutes * 60000
     57      seconds = int(what // 1000)
     58      what -= int(seconds * 1000)
     59      what = int(what)
     60      return (degrees, minutes, seconds, what, sign)
     61
     62
     63 -def _tuple_to_float(what):
     64      value = float(what[0])
     65      value += float(what[1]) / 60.0
     66      value += float(what[2]) / 3600.0
     67      value += float(what[3]) / 3600000.0
     68      return float(what[4]) * value
     69
     70
     71 -def _encode_size(what, desc):
     72      what = long(what)
     73      exponent = _exponent_of(what, desc) & 0xF
     74      base = what // pow(10, exponent) & 0xF
     75      return base * 16 + exponent
     76
     77
     78 -def _decode_size(what, desc):
     79      exponent = what & 0x0F
     80      if exponent > 9:
     81          raise dns.exception.SyntaxError("bad %s exponent" % desc)
     82      base = (what & 0xF0) >> 4
     83      if base > 9:
     84          raise dns.exception.SyntaxError("bad %s base" % desc)
     85      return long(base) * pow(10, exponent)
     86
     87
     88 -class LOC(dns.rdata.Rdata):
     89
     90      """LOC record
     91
     92      @ivar latitude: latitude
     93      @type latitude: (int, int, int, int, sign) tuple specifying the degrees, minutes,
     94      seconds, milliseconds, and sign of the coordinate.
     95      @ivar longitude: longitude
     96      @type longitude: (int, int, int, int, sign) tuple specifying the degrees,
     97      minutes, seconds, milliseconds, and sign of the coordinate.
     98      @ivar altitude: altitude
     99      @type altitude: float
    100      @ivar size: size of the sphere
    101      @type size: float
    102      @ivar horizontal_precision: horizontal precision
    103      @type horizontal_precision: float
    104      @ivar vertical_precision: vertical precision
    105      @type vertical_precision: float
    106      @see: RFC 1876"""
    107
    108      __slots__ = ['latitude', 'longitude', 'altitude', 'size',
    109                   'horizontal_precision', 'vertical_precision']
    110
    111 -    def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
    112                   size=_default_size, hprec=_default_hprec,
    113                   vprec=_default_vprec):
    114          """Initialize a LOC record instance.
    115
    116          The parameters I{latitude} and I{longitude} may be either a 4-tuple
    117          of integers specifying (degrees, minutes, seconds, milliseconds),
    118          or they may be floating point values specifying the number of
    119          degrees. The other parameters are floats. Size, horizontal precision,
    120          and vertical precision are specified in centimeters."""
    121
    122          super(LOC, self).__init__(rdclass, rdtype)
    123          if isinstance(latitude, int) or isinstance(latitude, long):
    124              latitude = float(latitude)
    125          if isinstance(latitude, float):
    126              latitude = _float_to_tuple(latitude)
    127          self.latitude = latitude
    128          if isinstance(longitude, int) or isinstance(longitude, long):
    129              longitude = float(longitude)
    130          if isinstance(longitude, float):
    131              longitude = _float_to_tuple(longitude)
    132          self.longitude = longitude
    133          self.altitude = float(altitude)
    134          self.size = float(size)
    135          self.horizontal_precision = float(hprec)
    136          self.vertical_precision = float(vprec)
    137
    138 -    def to_text(self, origin=None, relativize=True, **kw):
    139          if self.latitude[4] > 0:
    140              lat_hemisphere = 'N'
    141          else:
    142              lat_hemisphere = 'S'
    143          if self.longitude[4] > 0:
    144              long_hemisphere = 'E'
    145          else:
    146              long_hemisphere = 'W'
    147          text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
    148              self.latitude[0], self.latitude[1],
    149              self.latitude[2], self.latitude[3], lat_hemisphere,
    150              self.longitude[0], self.longitude[1], self.longitude[2],
    151              self.longitude[3], long_hemisphere,
    152              self.altitude / 100.0
    153          )
    154
    155          # do not print default values
    156          if self.size != _default_size or \
    157              self.horizontal_precision != _default_hprec or \
    158                  self.vertical_precision != _default_vprec:
    159              text += " %0.2fm %0.2fm %0.2fm" % (
    160                  self.size / 100.0, self.horizontal_precision / 100.0,
    161                  self.vertical_precision / 100.0
    162              )
    163          return text
    164
    165      @classmethod
    166 -    def from_text(cls, rdclass, rdtype, tok, origin=None, relativize=True):
    167          latitude = [0, 0, 0, 0, 1]
    168          longitude = [0, 0, 0, 0, 1]
    169          size = _default_size
    170          hprec = _default_hprec
    171          vprec = _default_vprec
    172
    173          latitude[0] = tok.get_int()
    174          t = tok.get_string()
    175          if t.isdigit():
    176              latitude[1] = int(t)
    177              t = tok.get_string()
    178              if '.' in t:
    179                  (seconds, milliseconds) = t.split('.')
    180                  if not seconds.isdigit():
    181                      raise dns.exception.SyntaxError(
    182                          'bad latitude seconds value')
    183                  latitude[2] = int(seconds)
    184                  if latitude[2] >= 60:
    185                      raise dns.exception.SyntaxError('latitude seconds >= 60')
    186                  l = len(milliseconds)
    187                  if l == 0 or l > 3 or not milliseconds.isdigit():
    188                      raise dns.exception.SyntaxError(
    189                          'bad latitude milliseconds value')
    190                  if l == 1:
    191                      m = 100
    192                  elif l == 2:
    193                      m = 10
    194                  else:
    195                      m = 1
    196                  latitude[3] = m * int(milliseconds)
    197                  t = tok.get_string()
    198              elif t.isdigit():
    199                  latitude[2] = int(t)
    200                  t = tok.get_string()
    201          if t == 'S':
    202              latitude[4] = -1
    203          elif t != 'N':
    204              raise dns.exception.SyntaxError('bad latitude hemisphere value')
    205
    206          longitude[0] = tok.get_int()
    207          t = tok.get_string()
    208          if t.isdigit():
    209              longitude[1] = int(t)
    210              t = tok.get_string()
    211              if '.' in t:
    212                  (seconds, milliseconds) = t.split('.')
    213                  if not seconds.isdigit():
    214                      raise dns.exception.SyntaxError(
    215                          'bad longitude seconds value')
    216                  longitude[2] = int(seconds)
    217                  if longitude[2] >= 60:
    218                      raise dns.exception.SyntaxError('longitude seconds >= 60')
    219                  l = len(milliseconds)
    220                  if l == 0 or l > 3 or not milliseconds.isdigit():
    221                      raise dns.exception.SyntaxError(
    222                          'bad longitude milliseconds value')
    223                  if l == 1:
    224                      m = 100
    225                  elif l == 2:
    226                      m = 10
    227                  else:
    228                      m = 1
    229                  longitude[3] = m * int(milliseconds)
    230                  t = tok.get_string()
    231              elif t.isdigit():
    232                  longitude[2] = int(t)
    233                  t = tok.get_string()
    234          if t == 'W':
    235              longitude[4] = -1
    236          elif t != 'E':
    237              raise dns.exception.SyntaxError('bad longitude hemisphere value')
    238
    239          t = tok.get_string()
    240          if t[-1] == 'm':
    241              t = t[0: -1]
    242          altitude = float(t) * 100.0        # m -> cm
    243
    244          token = tok.get().unescape()
    245          if not token.is_eol_or_eof():
    246              value = token.value
    247              if value[-1] == 'm':
    248                  value = value[0: -1]
    249              size = float(value) * 100.0        # m -> cm
    250              token = tok.get().unescape()
    251              if not token.is_eol_or_eof():
    252                  value = token.value
    253                  if value[-1] == 'm':
    254                      value = value[0: -1]
    255                  hprec = float(value) * 100.0        # m -> cm
    256                  token = tok.get().unescape()
    257                  if not token.is_eol_or_eof():
    258                      value = token.value
    259                      if value[-1] == 'm':
    260                          value = value[0: -1]
    261                      vprec = float(value) * 100.0        # m -> cm
    262                      tok.get_eol()
    263
    264          return cls(rdclass, rdtype, latitude, longitude, altitude,
    265                     size, hprec, vprec)
    266
    267 -    def to_wire(self, file, compress=None, origin=None):
    268          milliseconds = (self.latitude[0] * 3600000 +
    269                          self.latitude[1] * 60000 +
    270                          self.latitude[2] * 1000 +
    271                          self.latitude[3]) * self.latitude[4]
    272          latitude = long(0x80000000) + milliseconds
    273          milliseconds = (self.longitude[0] * 3600000 +
    274                          self.longitude[1] * 60000 +
    275                          self.longitude[2] * 1000 +
    276                          self.longitude[3]) * self.longitude[4]
    277          longitude = long(0x80000000) + milliseconds
    278          altitude = long(self.altitude) + long(10000000)
    279          size = _encode_size(self.size, "size")
    280          hprec = _encode_size(self.horizontal_precision, "horizontal precision")
    281          vprec = _encode_size(self.vertical_precision, "vertical precision")
    282          wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
    283                             longitude, altitude)
    284          file.write(wire)
    285
    286      @classmethod
    287 -    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin=None):
    288          (version, size, hprec, vprec, latitude, longitude, altitude) = \
    289              struct.unpack("!BBBBIII", wire[current: current + rdlen])
    290          if latitude > long(0x80000000):
    291              latitude = float(latitude - long(0x80000000)) / 3600000
    292          else:
    293              latitude = -1 * float(long(0x80000000) - latitude) / 3600000
    294          if latitude < -90.0 or latitude > 90.0:
    295              raise dns.exception.FormError("bad latitude")
    296          if longitude > long(0x80000000):
    297              longitude = float(longitude - long(0x80000000)) / 3600000
    298          else:
    299              longitude = -1 * float(long(0x80000000) - longitude) / 3600000
    300          if longitude < -180.0 or longitude > 180.0:
    301              raise dns.exception.FormError("bad longitude")
    302          altitude = float(altitude) - 10000000.0
    303          size = _decode_size(size, "size")
    304          hprec = _decode_size(hprec, "horizontal precision")
    305          vprec = _decode_size(vprec, "vertical precision")
    306          return cls(rdclass, rdtype, latitude, longitude, altitude,
    307                     size, hprec, vprec)
    308
    309 -    def _get_float_latitude(self):
    310          return _tuple_to_float(self.latitude)
    311
    312 -    def _set_float_latitude(self, value):
    313          self.latitude = _float_to_tuple(value)
    314
    315      float_latitude = property(_get_float_latitude, _set_float_latitude,
    316                                doc="latitude as a floating point value")
    317
    318 -    def _get_float_longitude(self):
    319          return _tuple_to_float(self.longitude)
    320
    321 -    def _set_float_longitude(self, value):
    322          self.longitude = _float_to_tuple(value)
    323
    324      float_longitude = property(_get_float_longitude, _set_float_longitude,
    325                                 doc="longitude as a floating point value")
    326