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  import cStringIO
     17  import struct
     18
     19  import dns.exception
     20  import dns.rdata
     21
     22  _pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L,
     23           100000000L, 1000000000L, 10000000000L)
     24
     25  # default values are in centimeters
     26  _default_size = 100.0
     27  _default_hprec = 1000000.0
     28  _default_vprec = 1000.0
     29
     30 -def _exponent_of(what, desc):
     31      if what == 0:
     32          return 0
     33      exp = None
     34      for i in xrange(len(_pows)):
     35          if what // _pows[i] == 0L:
     36              exp = i - 1
     37              break
     38      if exp is None or exp < 0:
     39          raise dns.exception.SyntaxError("%s value out of bounds" % desc)
     40      return exp
     41
     42 -def _float_to_tuple(what):
     43      if what < 0:
     44          sign = -1
     45          what *= -1
     46      else:
     47          sign = 1
     48      what = long(round(what * 3600000))
     49      degrees = int(what // 3600000)
     50      what -= degrees * 3600000
     51      minutes = int(what // 60000)
     52      what -= minutes * 60000
     53      seconds = int(what // 1000)
     54      what -= int(seconds * 1000)
     55      what = int(what)
     56      return (degrees * sign, minutes, seconds, what)
     57
     58 -def _tuple_to_float(what):
     59      if what[0] < 0:
     60          sign = -1
     61          value = float(what[0]) * -1
     62      else:
     63          sign = 1
     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 sign * value
     69
     70 -def _encode_size(what, desc):
     71      what = long(what);
     72      exponent = _exponent_of(what, desc) & 0xF
     73      base = what // pow(10, exponent) & 0xF
     74      return base * 16 + exponent
     75
     76 -def _decode_size(what, desc):
     77      exponent = what & 0x0F
     78      if exponent > 9:
     79          raise dns.exception.SyntaxError("bad %s exponent" % desc)
     80      base = (what & 0xF0) >> 4
     81      if base > 9:
     82          raise dns.exception.SyntaxError("bad %s base" % desc)
     83      return long(base) * pow(10, exponent)
     84
     85 -class LOC(dns.rdata.Rdata):
     86      """LOC record
     87
     88      @ivar latitude: latitude
     89      @type latitude: (int, int, int, int) tuple specifying the degrees, minutes,
     90      seconds, and milliseconds of the coordinate.
     91      @ivar longitude: longitude
     92      @type longitude: (int, int, int, int) tuple specifying the degrees,
     93      minutes, seconds, and milliseconds of the coordinate.
     94      @ivar altitude: altitude
     95      @type altitude: float
     96      @ivar size: size of the sphere
     97      @type size: float
     98      @ivar horizontal_precision: horizontal precision
     99      @type horizontal_precision: float
    100      @ivar vertical_precision: vertical precision
    101      @type vertical_precision: float
    102      @see: RFC 1876"""
    103
    104      __slots__ = ['latitude', 'longitude', 'altitude', 'size',
    105                   'horizontal_precision', 'vertical_precision']
    106
    107 -    def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
    108                   size=_default_size, hprec=_default_hprec, vprec=_default_vprec):
    109          """Initialize a LOC record instance.
    110
    111          The parameters I{latitude} and I{longitude} may be either a 4-tuple
    112          of integers specifying (degrees, minutes, seconds, milliseconds),
    113          or they may be floating point values specifying the number of
    114          degrees. The other parameters are floats. Size, horizontal precision,
    115          and vertical precision are specified in centimeters."""
    116
    117          super(LOC, self).__init__(rdclass, rdtype)
    118          if isinstance(latitude, int) or isinstance(latitude, long):
    119              latitude = float(latitude)
    120          if isinstance(latitude, float):
    121              latitude = _float_to_tuple(latitude)
    122          self.latitude = latitude
    123          if isinstance(longitude, int) or isinstance(longitude, long):
    124              longitude = float(longitude)
    125          if isinstance(longitude, float):
    126              longitude = _float_to_tuple(longitude)
    127          self.longitude = longitude
    128          self.altitude = float(altitude)
    129          self.size = float(size)
    130          self.horizontal_precision = float(hprec)
    131          self.vertical_precision = float(vprec)
    132
    133 -    def to_text(self, origin=None, relativize=True, **kw):
    134          if self.latitude[0] > 0:
    135              lat_hemisphere = 'N'
    136              lat_degrees = self.latitude[0]
    137          else:
    138              lat_hemisphere = 'S'
    139              lat_degrees = -1 * self.latitude[0]
    140          if self.longitude[0] > 0:
    141              long_hemisphere = 'E'
    142              long_degrees = self.longitude[0]
    143          else:
    144              long_hemisphere = 'W'
    145              long_degrees = -1 * self.longitude[0]
    146          text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
    147              lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3],
    148              lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2],
    149              self.longitude[3], long_hemisphere, self.altitude / 100.0
    150              )
    151
    152          # do not print default values
    153          if self.size != _default_size or \
    154              self.horizontal_precision != _default_hprec or \
    155              self.vertical_precision != _default_vprec:
    156              text += " %0.2fm %0.2fm %0.2fm" % (
    157                  self.size / 100.0, self.horizontal_precision / 100.0,
    158                  self.vertical_precision / 100.0
    159              )
    160          return text
    161
    162 -    def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
    163          latitude = [0, 0, 0, 0]
    164          longitude = [0, 0, 0, 0]
    165          size = _default_size
    166          hprec = _default_hprec
    167          vprec = _default_vprec
    168
    169          latitude[0] = tok.get_int()
    170          t = tok.get_string()
    171          if t.isdigit():
    172              latitude[1] = int(t)
    173              t = tok.get_string()
    174              if '.' in t:
    175                  (seconds, milliseconds) = t.split('.')
    176                  if not seconds.isdigit():
    177                      raise dns.exception.SyntaxError('bad latitude seconds value')
    178                  latitude[2] = int(seconds)
    179                  if latitude[2] >= 60:
    180                      raise dns.exception.SyntaxError('latitude seconds >= 60')
    181                  l = len(milliseconds)
    182                  if l == 0 or l > 3 or not milliseconds.isdigit():
    183                      raise dns.exception.SyntaxError('bad latitude milliseconds value')
    184                  if l == 1:
    185                      m = 100
    186                  elif l == 2:
    187                      m = 10
    188                  else:
    189                      m = 1
    190                  latitude[3] = m * int(milliseconds)
    191                  t = tok.get_string()
    192              elif t.isdigit():
    193                  latitude[2] = int(t)
    194                  t = tok.get_string()
    195          if t == 'S':
    196              latitude[0] *= -1
    197          elif t != 'N':
    198              raise dns.exception.SyntaxError('bad latitude hemisphere value')
    199
    200          longitude[0] = tok.get_int()
    201          t = tok.get_string()
    202          if t.isdigit():
    203              longitude[1] = int(t)
    204              t = tok.get_string()
    205              if '.' in t:
    206                  (seconds, milliseconds) = t.split('.')
    207                  if not seconds.isdigit():
    208                      raise dns.exception.SyntaxError('bad longitude seconds value')
    209                  longitude[2] = int(seconds)
    210                  if longitude[2] >= 60:
    211                      raise dns.exception.SyntaxError('longitude seconds >= 60')
    212                  l = len(milliseconds)
    213                  if l == 0 or l > 3 or not milliseconds.isdigit():
    214                      raise dns.exception.SyntaxError('bad longitude milliseconds value')
    215                  if l == 1:
    216                      m = 100
    217                  elif l == 2:
    218                      m = 10
    219                  else:
    220                      m = 1
    221                  longitude[3] = m * int(milliseconds)
    222                  t = tok.get_string()
    223              elif t.isdigit():
    224                  longitude[2] = int(t)
    225                  t = tok.get_string()
    226          if t == 'W':
    227              longitude[0] *= -1
    228          elif t != 'E':
    229              raise dns.exception.SyntaxError('bad longitude hemisphere value')
    230
    231          t = tok.get_string()
    232          if t[-1] == 'm':
    233              t = t[0 : -1]
    234          altitude = float(t) * 100.0     # m -> cm
    235
    236          token = tok.get().unescape()
    237          if not token.is_eol_or_eof():
    238              value = token.value
    239              if value[-1] == 'm':
    240                  value = value[0 : -1]
    241              size = float(value) * 100.0 # m -> cm
    242              token = tok.get().unescape()
    243              if not token.is_eol_or_eof():
    244                  value = token.value
    245                  if value[-1] == 'm':
    246                      value = value[0 : -1]
    247                  hprec = float(value) * 100.0    # m -> cm
    248                  token = tok.get().unescape()
    249                  if not token.is_eol_or_eof():
    250                      value = token.value
    251                      if value[-1] == 'm':
    252                          value = value[0 : -1]
    253                      vprec = float(value) * 100.0        # m -> cm
    254                      tok.get_eol()
    255
    256          return cls(rdclass, rdtype, latitude, longitude, altitude,
    257                     size, hprec, vprec)
    258
    259      from_text = classmethod(from_text)
    260
    261 -    def to_wire(self, file, compress = None, origin = None):
    262          if self.latitude[0] < 0:
    263              sign = -1
    264              degrees = long(-1 * self.latitude[0])
    265          else:
    266              sign = 1
    267              degrees = long(self.latitude[0])
    268          milliseconds = (degrees * 3600000 +
    269                          self.latitude[1] * 60000 +
    270                          self.latitude[2] * 1000 +
    271                          self.latitude[3]) * sign
    272          latitude = 0x80000000L + milliseconds
    273          if self.longitude[0] < 0:
    274              sign = -1
    275              degrees = long(-1 * self.longitude[0])
    276          else:
    277              sign = 1
    278              degrees = long(self.longitude[0])
    279          milliseconds = (degrees * 3600000 +
    280                          self.longitude[1] * 60000 +
    281                          self.longitude[2] * 1000 +
    282                          self.longitude[3]) * sign
    283          longitude = 0x80000000L + milliseconds
    284          altitude = long(self.altitude) + 10000000L
    285          size = _encode_size(self.size, "size")
    286          hprec = _encode_size(self.horizontal_precision, "horizontal precision")
    287          vprec = _encode_size(self.vertical_precision, "vertical precision")
    288          wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
    289                             longitude, altitude)
    290          file.write(wire)
    291
    292 -    def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
    293          (version, size, hprec, vprec, latitude, longitude, altitude) = \
    294                    struct.unpack("!BBBBIII", wire[current : current + rdlen])
    295          if latitude > 0x80000000L:
    296              latitude = float(latitude - 0x80000000L) / 3600000
    297          else:
    298              latitude = -1 * float(0x80000000L - latitude) / 3600000
    299          if latitude < -90.0 or latitude > 90.0:
    300              raise dns.exception.FormError("bad latitude")
    301          if longitude > 0x80000000L:
    302              longitude = float(longitude - 0x80000000L) / 3600000
    303          else:
    304              longitude = -1 * float(0x80000000L - longitude) / 3600000
    305          if longitude < -180.0 or longitude > 180.0:
    306              raise dns.exception.FormError("bad longitude")
    307          altitude = float(altitude) - 10000000.0
    308          size = _decode_size(size, "size")
    309          hprec = _decode_size(hprec, "horizontal precision")
    310          vprec = _decode_size(vprec, "vertical precision")
    311          return cls(rdclass, rdtype, latitude, longitude, altitude,
    312                     size, hprec, vprec)
    313
    314      from_wire = classmethod(from_wire)
    315
    316 -    def _cmp(self, other):
    317          f = cStringIO.StringIO()
    318          self.to_wire(f)
    319          wire1 = f.getvalue()
    320          f.seek(0)
    321          f.truncate()
    322          other.to_wire(f)
    323          wire2 = f.getvalue()
    324          f.close()
    325
    326          return cmp(wire1, wire2)
    327
    328 -    def _get_float_latitude(self):
    329          return _tuple_to_float(self.latitude)
    330
    331 -    def _set_float_latitude(self, value):
    332          self.latitude = _float_to_tuple(value)
    333
    334      float_latitude = property(_get_float_latitude, _set_float_latitude,
    335                                doc="latitude as a floating point value")
    336
    337 -    def _get_float_longitude(self):
    338          return _tuple_to_float(self.longitude)
    339
    340 -    def _set_float_longitude(self, value):
    341          self.longitude = _float_to_tuple(value)
    342
    343      float_longitude = property(_get_float_longitude, _set_float_longitude,
    344                                 doc="longitude as a floating point value")
    345