1 # -*- coding: iso-8859-1 -*- 2 """ 3 MoinMoin - LocationSupport library (derived from EventAggregatorSupport) 4 5 @copyright: 2011, 2012, 2013 by Paul Boddie <paul@boddie.org.uk> 6 @license: GNU GPL (v2 or later), see COPYING.txt for details. 7 """ 8 9 import operator 10 import re 11 12 location_normalised_regexp = re.compile( 13 ur"(?:\d+\w*\s+)?" # preceding postcode (optional) 14 ur"(?P<location>" # start of group of interest 15 ur"\w[\w\s-]+?" # area or town 16 ur"(?:,(?:\s*[\w-]+)+)?" # country (optional) 17 ur")$", re.UNICODE) 18 19 # Utility functions. 20 21 def sign(x): 22 if x < 0: 23 return -1 24 else: 25 return 1 26 27 # Location-related functions. 28 29 class Reference: 30 31 "A map reference." 32 33 def __init__(self, degrees, minutes=0, seconds=0): 34 self.degrees = degrees 35 self.minutes = minutes 36 self.seconds = seconds 37 38 def __repr__(self): 39 return "Reference(%d, %d, %f)" % (self.degrees, self.minutes, self.seconds) 40 41 def __str__(self): 42 return "%d:%d:%f" % (self.degrees, self.minutes, self.seconds) 43 44 def __add__(self, other): 45 if not isinstance(other, Reference): 46 return NotImplemented 47 else: 48 s = sign(self.degrees) 49 o = sign(other.degrees) 50 carry, seconds = adc(s * self.seconds, o * other.seconds) 51 carry, minutes = adc(s * self.minutes, o * other.minutes + carry) 52 return Reference(self.degrees + other.degrees + carry, minutes, seconds) 53 54 def __sub__(self, other): 55 if not isinstance(other, Reference): 56 return NotImplemented 57 else: 58 return self.__add__(Reference(-other.degrees, other.minutes, other.seconds)) 59 60 def _compare(self, op, other): 61 if not isinstance(other, Reference): 62 return NotImplemented 63 else: 64 return op(self.to_degrees(), other.to_degrees()) 65 66 def __eq__(self, other): 67 return self._compare(operator.eq, other) 68 69 def __ne__(self, other): 70 return self._compare(operator.ne, other) 71 72 def __lt__(self, other): 73 return self._compare(operator.lt, other) 74 75 def __le__(self, other): 76 return self._compare(operator.le, other) 77 78 def __gt__(self, other): 79 return self._compare(operator.gt, other) 80 81 def __ge__(self, other): 82 return self._compare(operator.ge, other) 83 84 def to_degrees(self): 85 return sign(self.degrees) * (abs(self.degrees) + self.minutes / 60.0 + self.seconds / 3600.0) 86 87 def to_pixels(self, scale): 88 return self.to_degrees() * scale 89 90 def adc(x, y): 91 result = x + y 92 return divmod(result, 60) 93 94 def getPositionForReference(latitude, longitude, map_y, map_x, map_x_scale, map_y_scale): 95 return (longitude - map_x).to_pixels(map_x_scale), (latitude - map_y).to_pixels(map_y_scale) 96 97 def getPositionForCentrePoint(position, map_x_scale, map_y_scale): 98 x, y = position 99 return x - map_x_scale / 2.0, y - map_y_scale / 2.0 100 101 def getMapReference(value): 102 103 "Return a map reference by parsing the given 'value'." 104 105 if value.find(":") != -1: 106 return getMapReferenceFromDMS(value) 107 else: 108 return getMapReferenceFromDecimal(value) 109 110 def getMapReferenceFromDMS(value): 111 112 """ 113 Return a map reference by parsing the given 'value' expressed as degrees, 114 minutes, seconds. 115 """ 116 117 values = value.split(":") 118 values = map(int, values[:2]) + map(float, values[2:3]) 119 return Reference(*values) 120 121 def getMapReferenceFromDecimal(value): 122 123 "Return a map reference by parsing the given 'value' in decimal degrees." 124 125 value = float(value) 126 degrees, remainder = divmod(abs(value * 3600), 3600) 127 minutes, seconds = divmod(remainder, 60) 128 return Reference(sign(value) * degrees, minutes, seconds) 129 130 # User interface functions. 131 132 def getNormalisedLocation(location): 133 134 """ 135 Attempt to return a normalised 'location' of the form "<town>, <country>" or 136 "<town>". 137 """ 138 139 match = location_normalised_regexp.search(location) 140 if match: 141 return match.group("location") 142 else: 143 return None 144 145 # vim: tabstop=4 expandtab shiftwidth=4