PyMetOffice  0.4
Pythonic access to UK Met Office DataPoint API
places.py
1 #!/usr/bin/python
2 # Convert various place identifiers to lat, lon pair.
3 #
4 # Experimental code at present.
5 #
6 # The object of the exercise is to convert
7 # a human-readable place name to latitude, longitude
8 # so that we can use the 'nearestlatlon' facility of the DataPoint API.
9 
10 ## _urlopen
11 # This is the function required to open a URL as a file-like object.
12 # We make three attempts to get a url library. If the 3rd one
13 # fails, the exception is \em not caught. I claim this is deliberate!
14 
15 try:
16  import urllib
17  _urlopen = urllib.request.urlopen
18  _quote = urllib.quote
19 except:
20  try:
21  import urllib2
22  _urlopen = urllib2.urlopen
23  _quote = urllib2.quote
24  urllib = urllib2
25  except:
26  import urllib
27  _urlopen = urllib.urlopen
28  _quote = urllib2.quote
29 
30 #import urllib
31 
32 import json
33 import xml
34 import xml.dom.minidom
35 ## This is the URL of the currently used free place locator.
36 # Many thanks MapQuest for providing it.
37 
38 MapQuestKey = 'Fmjtd%7Cluub296r2g%2Crl%3Do5-968shf'
39 MAPQUEST = "http://open.mapquestapi.com/geocoding/v1/address"
40 
41 
42 ## A convenient wrapper round location information
43 # obtained from geocoding sites.
44 class Place(object):
45  ## Constructor:
46  # \param info A dictionary containing
47  # name: original search text, county: UK county,
48  # lat: latitude, lon: longitude.
49  def __init__(self, **info):
50  object.__init__(self)
51  self._info = info
52 
53  ## Enable sorting of lists of places.
54  def __lt__(self, other):
55  return self._info['county'] < other._info['county']
56 
57  ## Printing a representation can be useful for debugging.
58  def __repr__(self):
59  data = []
60  pattern = '%s: %s'
61  for k in self._info.keys():
62  data.append(pattern % (k, self._info[k]))
63  return ', '.join(data)
64 
65  def __str__(self):
66  return self.__repr__()
67 
68  ## A Utility method to return data suitable for requesting forecasts.
69  def getLatLon(self):
70  d = self._info
71  d.pop('name')
72  d.pop('county')
73  return d
74 
75  ## Class to represent a collection of places.
76  # Useful for presenting choices to user after a text search.
77 class PlaceSet(object):
78  ## \param location A full or partial address.
79  # If 'UK' does not appear, it will be added
80  # because the DataPoint API provides UK data only.
81  def __init__(self, location):
82  object.__init__(self)
83  self._places = []
84  loc = location
85  if loc.find('UK') < 0: loc = '%s%s' % (loc, ',UK')
86  data = self._getData(loc)
87  doc = xml.dom.minidom.parseString(data)
88  locations = doc.getElementsByTagName('locations')
89  locs = locations[0].getElementsByTagName('location')
90  for loc in locs:
91  name = loc.getElementsByTagName('adminArea5')[0].firstChild.data
92  county = loc.getElementsByTagName('adminArea4')[0].firstChild.data
93  lat = loc.getElementsByTagName('lat')[0].firstChild.data
94  lon = loc.getElementsByTagName('lng')[0].firstChild.data
95  info = {'name': name, 'county': county, 'lat': lat, 'lon': lon}
96  place = Place(**info)
97  self._places.append(place)
98  self._places.sort()
99 
100  ## This method makes the geocoding request from a
101  # full or partial address.
102  def _getData(self, location):
103  data = None
104  url = '%s?location=%s&outFormat=xml&key=%s'
105  fullurl = url % (MAPQUEST, _quote(location), MapQuestKey)
106  data = _urlopen(fullurl).read()
107  return data
108 
109  def _getPlaces(self):
110  return self._places
111 
112  def __repr__(self):
113  data = [s.__repr__() for s in self._places]
114  return '\n'.join(data)
115 
116  places = property(_getPlaces)
117