1 # Copyright (C) 2003-2007, 2009, 2010 Nominum, Inc.
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.
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.
22 _pows = (1L, 10L, 100L, 1000L, 10000L, 100000L, 1000000L, 10000000L,
23 100000000L, 1000000000L, 10000000000L)
25 def _exponent_of(what, desc):
27 for i in xrange(len(_pows)):
28 if what // _pows[i] == 0L:
31 if exp is None or exp < 0:
32 raise dns.exception.SyntaxError("%s value out of bounds" % desc)
35 def _float_to_tuple(what):
41 what = long(round(what * 3600000))
42 degrees = int(what // 3600000)
43 what -= degrees * 3600000
44 minutes = int(what // 60000)
45 what -= minutes * 60000
46 seconds = int(what // 1000)
47 what -= int(seconds * 1000)
49 return (degrees * sign, minutes, seconds, what)
51 def _tuple_to_float(what):
54 value = float(what[0]) * -1
57 value = float(what[0])
58 value += float(what[1]) / 60.0
59 value += float(what[2]) / 3600.0
60 value += float(what[3]) / 3600000.0
63 def _encode_size(what, desc):
65 exponent = _exponent_of(what, desc) & 0xF
66 base = what // pow(10, exponent) & 0xF
67 return base * 16 + exponent
69 def _decode_size(what, desc):
70 exponent = what & 0x0F
72 raise dns.exception.SyntaxError("bad %s exponent" % desc)
73 base = (what & 0xF0) >> 4
75 raise dns.exception.SyntaxError("bad %s base" % desc)
76 return long(base) * pow(10, exponent)
78 class LOC(dns.rdata.Rdata):
81 @ivar latitude: latitude
82 @type latitude: (int, int, int, int) tuple specifying the degrees, minutes,
83 seconds, and milliseconds of the coordinate.
84 @ivar longitude: longitude
85 @type longitude: (int, int, int, int) tuple specifying the degrees,
86 minutes, seconds, and milliseconds of the coordinate.
87 @ivar altitude: altitude
89 @ivar size: size of the sphere
91 @ivar horizontal_precision: horizontal precision
92 @type horizontal_precision: float
93 @ivar vertical_precision: vertical precision
94 @type vertical_precision: float
97 __slots__ = ['latitude', 'longitude', 'altitude', 'size',
98 'horizontal_precision', 'vertical_precision']
100 def __init__(self, rdclass, rdtype, latitude, longitude, altitude,
101 size=1.0, hprec=10000.0, vprec=10.0):
102 """Initialize a LOC record instance.
104 The parameters I{latitude} and I{longitude} may be either a 4-tuple
105 of integers specifying (degrees, minutes, seconds, milliseconds),
106 or they may be floating point values specifying the number of
107 degrees. The other parameters are floats."""
109 super(LOC, self).__init__(rdclass, rdtype)
110 if isinstance(latitude, int) or isinstance(latitude, long):
111 latitude = float(latitude)
112 if isinstance(latitude, float):
113 latitude = _float_to_tuple(latitude)
114 self.latitude = latitude
115 if isinstance(longitude, int) or isinstance(longitude, long):
116 longitude = float(longitude)
117 if isinstance(longitude, float):
118 longitude = _float_to_tuple(longitude)
119 self.longitude = longitude
120 self.altitude = float(altitude)
121 self.size = float(size)
122 self.horizontal_precision = float(hprec)
123 self.vertical_precision = float(vprec)
125 def to_text(self, origin=None, relativize=True, **kw):
126 if self.latitude[0] > 0:
128 lat_degrees = self.latitude[0]
131 lat_degrees = -1 * self.latitude[0]
132 if self.longitude[0] > 0:
133 long_hemisphere = 'E'
134 long_degrees = self.longitude[0]
136 long_hemisphere = 'W'
137 long_degrees = -1 * self.longitude[0]
138 text = "%d %d %d.%03d %s %d %d %d.%03d %s %0.2fm" % (
139 lat_degrees, self.latitude[1], self.latitude[2], self.latitude[3],
140 lat_hemisphere, long_degrees, self.longitude[1], self.longitude[2],
141 self.longitude[3], long_hemisphere, self.altitude / 100.0
144 if self.size != 1.0 or self.horizontal_precision != 10000.0 or \
145 self.vertical_precision != 10.0:
146 text += " %0.2fm %0.2fm %0.2fm" % (
147 self.size / 100.0, self.horizontal_precision / 100.0,
148 self.vertical_precision / 100.0
152 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
153 latitude = [0, 0, 0, 0]
154 longitude = [0, 0, 0, 0]
159 latitude[0] = tok.get_int()
165 (seconds, milliseconds) = t.split('.')
166 if not seconds.isdigit():
167 raise dns.exception.SyntaxError('bad latitude seconds value')
168 latitude[2] = int(seconds)
169 if latitude[2] >= 60:
170 raise dns.exception.SyntaxError('latitude seconds >= 60')
171 l = len(milliseconds)
172 if l == 0 or l > 3 or not milliseconds.isdigit():
173 raise dns.exception.SyntaxError('bad latitude milliseconds value')
180 latitude[3] = m * int(milliseconds)
188 raise dns.exception.SyntaxError('bad latitude hemisphere value')
190 longitude[0] = tok.get_int()
193 longitude[1] = int(t)
196 (seconds, milliseconds) = t.split('.')
197 if not seconds.isdigit():
198 raise dns.exception.SyntaxError('bad longitude seconds value')
199 longitude[2] = int(seconds)
200 if longitude[2] >= 60:
201 raise dns.exception.SyntaxError('longitude seconds >= 60')
202 l = len(milliseconds)
203 if l == 0 or l > 3 or not milliseconds.isdigit():
204 raise dns.exception.SyntaxError('bad longitude milliseconds value')
211 longitude[3] = m * int(milliseconds)
214 longitude[2] = int(t)
219 raise dns.exception.SyntaxError('bad longitude hemisphere value')
224 altitude = float(t) * 100.0 # m -> cm
226 token = tok.get().unescape()
227 if not token.is_eol_or_eof():
230 value = value[0 : -1]
231 size = float(value) * 100.0 # m -> cm
232 token = tok.get().unescape()
233 if not token.is_eol_or_eof():
236 value = value[0 : -1]
237 hprec = float(value) * 100.0 # m -> cm
238 token = tok.get().unescape()
239 if not token.is_eol_or_eof():
242 value = value[0 : -1]
243 vprec = float(value) * 100.0 # m -> cm
246 return cls(rdclass, rdtype, latitude, longitude, altitude,
249 from_text = classmethod(from_text)
251 def to_wire(self, file, compress = None, origin = None):
252 if self.latitude[0] < 0:
254 degrees = long(-1 * self.latitude[0])
257 degrees = long(self.latitude[0])
258 milliseconds = (degrees * 3600000 +
259 self.latitude[1] * 60000 +
260 self.latitude[2] * 1000 +
261 self.latitude[3]) * sign
262 latitude = 0x80000000L + milliseconds
263 if self.longitude[0] < 0:
265 degrees = long(-1 * self.longitude[0])
268 degrees = long(self.longitude[0])
269 milliseconds = (degrees * 3600000 +
270 self.longitude[1] * 60000 +
271 self.longitude[2] * 1000 +
272 self.longitude[3]) * sign
273 longitude = 0x80000000L + milliseconds
274 altitude = long(self.altitude) + 10000000L
275 size = _encode_size(self.size, "size")
276 hprec = _encode_size(self.horizontal_precision, "horizontal precision")
277 vprec = _encode_size(self.vertical_precision, "vertical precision")
278 wire = struct.pack("!BBBBIII", 0, size, hprec, vprec, latitude,
282 def from_wire(cls, rdclass, rdtype, wire, current, rdlen, origin = None):
283 (version, size, hprec, vprec, latitude, longitude, altitude) = \
284 struct.unpack("!BBBBIII", wire[current : current + rdlen])
285 if latitude > 0x80000000L:
286 latitude = float(latitude - 0x80000000L) / 3600000
288 latitude = -1 * float(0x80000000L - latitude) / 3600000
289 if latitude < -90.0 or latitude > 90.0:
290 raise dns.exception.FormError("bad latitude")
291 if longitude > 0x80000000L:
292 longitude = float(longitude - 0x80000000L) / 3600000
294 longitude = -1 * float(0x80000000L - longitude) / 3600000
295 if longitude < -180.0 or longitude > 180.0:
296 raise dns.exception.FormError("bad longitude")
297 altitude = float(altitude) - 10000000.0
298 size = _decode_size(size, "size")
299 hprec = _decode_size(hprec, "horizontal precision")
300 vprec = _decode_size(vprec, "vertical precision")
301 return cls(rdclass, rdtype, latitude, longitude, altitude,
304 from_wire = classmethod(from_wire)
306 def _cmp(self, other):
307 f = cStringIO.StringIO()
316 return cmp(wire1, wire2)
318 def _get_float_latitude(self):
319 return _tuple_to_float(self.latitude)
321 def _set_float_latitude(self, value):
322 self.latitude = _float_to_tuple(value)
324 float_latitude = property(_get_float_latitude, _set_float_latitude,
325 doc="latitude as a floating point value")
327 def _get_float_longitude(self):
328 return _tuple_to_float(self.longitude)
330 def _set_float_longitude(self, value):
331 self.longitude = _float_to_tuple(value)
333 float_longitude = property(_get_float_longitude, _set_float_longitude,
334 doc="longitude as a floating point value")