1 # Copyright (C) 2003-2007, 2009-2011 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 # default values are in centimeters
27 _default_hprec = 1000000.0
28 _default_vprec = 1000.0
30 def _exponent_of(what, desc):
34 for i in xrange(len(_pows)):
35 if what // _pows[i] == 0L:
38 if exp is None or exp < 0:
39 raise dns.exception.SyntaxError("%s value out of bounds" % desc)
42 def _float_to_tuple(what):
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)
56 return (degrees * sign, minutes, seconds, what)
58 def _tuple_to_float(what):
61 value = float(what[0]) * -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
70 def _encode_size(what, desc):
72 exponent = _exponent_of(what, desc) & 0xF
73 base = what // pow(10, exponent) & 0xF
74 return base * 16 + exponent
76 def _decode_size(what, desc):
77 exponent = what & 0x0F
79 raise dns.exception.SyntaxError("bad %s exponent" % desc)
80 base = (what & 0xF0) >> 4
82 raise dns.exception.SyntaxError("bad %s base" % desc)
83 return long(base) * pow(10, exponent)
85 class LOC(dns.rdata.Rdata):
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
96 @ivar size: size of the sphere
98 @ivar horizontal_precision: horizontal precision
99 @type horizontal_precision: float
100 @ivar vertical_precision: vertical precision
101 @type vertical_precision: float
104 __slots__ = ['latitude', 'longitude', 'altitude', 'size',
105 'horizontal_precision', 'vertical_precision']
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.
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."""
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)
133 def to_text(self, origin=None, relativize=True, **kw):
134 if self.latitude[0] > 0:
136 lat_degrees = self.latitude[0]
139 lat_degrees = -1 * self.latitude[0]
140 if self.longitude[0] > 0:
141 long_hemisphere = 'E'
142 long_degrees = self.longitude[0]
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
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
162 def from_text(cls, rdclass, rdtype, tok, origin = None, relativize = True):
163 latitude = [0, 0, 0, 0]
164 longitude = [0, 0, 0, 0]
166 hprec = _default_hprec
167 vprec = _default_vprec
169 latitude[0] = tok.get_int()
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')
190 latitude[3] = m * int(milliseconds)
198 raise dns.exception.SyntaxError('bad latitude hemisphere value')
200 longitude[0] = tok.get_int()
203 longitude[1] = int(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')
221 longitude[3] = m * int(milliseconds)
224 longitude[2] = int(t)
229 raise dns.exception.SyntaxError('bad longitude hemisphere value')
234 altitude = float(t) * 100.0 # m -> cm
236 token = tok.get().unescape()
237 if not token.is_eol_or_eof():
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():
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():
252 value = value[0 : -1]
253 vprec = float(value) * 100.0 # m -> cm
256 return cls(rdclass, rdtype, latitude, longitude, altitude,
259 from_text = classmethod(from_text)
261 def to_wire(self, file, compress = None, origin = None):
262 if self.latitude[0] < 0:
264 degrees = long(-1 * self.latitude[0])
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:
275 degrees = long(-1 * self.longitude[0])
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,
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
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
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,
314 from_wire = classmethod(from_wire)
316 def _cmp(self, other):
317 f = cStringIO.StringIO()
326 return cmp(wire1, wire2)
328 def _get_float_latitude(self):
329 return _tuple_to_float(self.latitude)
331 def _set_float_latitude(self, value):
332 self.latitude = _float_to_tuple(value)
334 float_latitude = property(_get_float_latitude, _set_float_latitude,
335 doc="latitude as a floating point value")
337 def _get_float_longitude(self):
338 return _tuple_to_float(self.longitude)
340 def _set_float_longitude(self, value):
341 self.longitude = _float_to_tuple(value)
343 float_longitude = property(_get_float_longitude, _set_float_longitude,
344 doc="longitude as a floating point value")