1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/LocationSupport.py Sun Jan 22 00:04:16 2012 +0100
1.3 @@ -0,0 +1,165 @@
1.4 +# -*- coding: iso-8859-1 -*-
1.5 +"""
1.6 + MoinMoin - LocationSupport library (derived from EventAggregatorSupport)
1.7 +
1.8 + @copyright: 2011, 2012 by Paul Boddie <paul@boddie.org.uk>
1.9 + @license: GNU GPL (v2 or later), see COPYING.txt for details.
1.10 +"""
1.11 +
1.12 +import operator
1.13 +import re
1.14 +
1.15 +__version__ = "0.1"
1.16 +
1.17 +location_normalised_regexp = re.compile(
1.18 + ur"(?:\d+\w*\s+)?" # preceding postcode (optional)
1.19 + ur"(?P<location>" # start of group of interest
1.20 + ur"\w[\w\s-]+?" # area or town
1.21 + ur"(?:,(?:\s*[\w-]+)+)?" # country (optional)
1.22 + ur")$", re.UNICODE)
1.23 +
1.24 +# Utility functions.
1.25 +
1.26 +def sign(x):
1.27 + if x < 0:
1.28 + return -1
1.29 + else:
1.30 + return 1
1.31 +
1.32 +# Location-related functions.
1.33 +
1.34 +class Reference:
1.35 +
1.36 + "A map reference."
1.37 +
1.38 + def __init__(self, degrees, minutes=0, seconds=0):
1.39 + self.degrees = degrees
1.40 + self.minutes = minutes
1.41 + self.seconds = seconds
1.42 +
1.43 + def __repr__(self):
1.44 + return "Reference(%d, %d, %f)" % (self.degrees, self.minutes, self.seconds)
1.45 +
1.46 + def __str__(self):
1.47 + return "%d:%d:%f" % (self.degrees, self.minutes, self.seconds)
1.48 +
1.49 + def __add__(self, other):
1.50 + if not isinstance(other, Reference):
1.51 + return NotImplemented
1.52 + else:
1.53 + s = sign(self.degrees)
1.54 + o = sign(other.degrees)
1.55 + carry, seconds = adc(s * self.seconds, o * other.seconds)
1.56 + carry, minutes = adc(s * self.minutes, o * other.minutes + carry)
1.57 + return Reference(self.degrees + other.degrees + carry, minutes, seconds)
1.58 +
1.59 + def __sub__(self, other):
1.60 + if not isinstance(other, Reference):
1.61 + return NotImplemented
1.62 + else:
1.63 + return self.__add__(Reference(-other.degrees, other.minutes, other.seconds))
1.64 +
1.65 + def _compare(self, op, other):
1.66 + if not isinstance(other, Reference):
1.67 + return NotImplemented
1.68 + else:
1.69 + return op(self.to_degrees(), other.to_degrees())
1.70 +
1.71 + def __eq__(self, other):
1.72 + return self._compare(operator.eq, other)
1.73 +
1.74 + def __ne__(self, other):
1.75 + return self._compare(operator.ne, other)
1.76 +
1.77 + def __lt__(self, other):
1.78 + return self._compare(operator.lt, other)
1.79 +
1.80 + def __le__(self, other):
1.81 + return self._compare(operator.le, other)
1.82 +
1.83 + def __gt__(self, other):
1.84 + return self._compare(operator.gt, other)
1.85 +
1.86 + def __ge__(self, other):
1.87 + return self._compare(operator.ge, other)
1.88 +
1.89 + def to_degrees(self):
1.90 + return sign(self.degrees) * (abs(self.degrees) + self.minutes / 60.0 + self.seconds / 3600.0)
1.91 +
1.92 + def to_pixels(self, scale):
1.93 + return self.to_degrees() * scale
1.94 +
1.95 +def adc(x, y):
1.96 + result = x + y
1.97 + return divmod(result, 60)
1.98 +
1.99 +def getPositionForReference(latitude, longitude, map_y, map_x, map_x_scale, map_y_scale):
1.100 + return (longitude - map_x).to_pixels(map_x_scale), (latitude - map_y).to_pixels(map_y_scale)
1.101 +
1.102 +def getPositionForCentrePoint(position, map_x_scale, map_y_scale):
1.103 + x, y = position
1.104 + return x - map_x_scale / 2.0, y - map_y_scale / 2.0
1.105 +
1.106 +def getMapReference(value):
1.107 +
1.108 + "Return a map reference by parsing the given 'value'."
1.109 +
1.110 + if value.find(":") != -1:
1.111 + return getMapReferenceFromDMS(value)
1.112 + else:
1.113 + return getMapReferenceFromDecimal(value)
1.114 +
1.115 +def getMapReferenceFromDMS(value):
1.116 +
1.117 + """
1.118 + Return a map reference by parsing the given 'value' expressed as degrees,
1.119 + minutes, seconds.
1.120 + """
1.121 +
1.122 + values = value.split(":")
1.123 + values = map(int, values[:2]) + map(float, values[2:3])
1.124 + return Reference(*values)
1.125 +
1.126 +def getMapReferenceFromDecimal(value):
1.127 +
1.128 + "Return a map reference by parsing the given 'value' in decimal degrees."
1.129 +
1.130 + value = float(value)
1.131 + degrees, remainder = divmod(abs(value * 3600), 3600)
1.132 + minutes, seconds = divmod(remainder, 60)
1.133 + return Reference(sign(value) * degrees, minutes, seconds)
1.134 +
1.135 +# User interface functions.
1.136 +
1.137 +def getNormalisedLocation(location):
1.138 +
1.139 + """
1.140 + Attempt to return a normalised 'location' of the form "<town>, <country>" or
1.141 + "<town>".
1.142 + """
1.143 +
1.144 + match = location_normalised_regexp.search(location)
1.145 + if match:
1.146 + return match.group("location")
1.147 + else:
1.148 + return None
1.149 +
1.150 +def getLocationPosition(location, locations):
1.151 +
1.152 + """
1.153 + Attempt to return the position of the given 'location' using the 'locations'
1.154 + dictionary provided. If no position can be found, return a latitude of None
1.155 + and a longitude of None.
1.156 + """
1.157 +
1.158 + latitude, longitude = None, None
1.159 +
1.160 + if location is not None:
1.161 + try:
1.162 + latitude, longitude = map(getMapReference, locations[location].split())
1.163 + except (KeyError, ValueError):
1.164 + pass
1.165 +
1.166 + return latitude, longitude
1.167 +
1.168 +# vim: tabstop=4 expandtab shiftwidth=4