Merge lp:~max-rabkin/ibid/coords-892484 into lp:ibid

Proposed by Max Rabkin on 2011-11-19
Status: Merged
Approved by: Stefano Rivera on 2011-11-30
Approved revision: 1048
Merged at revision: 1042
Proposed branch: lp:~max-rabkin/ibid/coords-892484
Merge into: lp:ibid
Diff against target: 142 lines (+97/-4)
1 file modified
ibid/plugins/geography.py (+97/-4)
To merge this branch: bzr merge lp:~max-rabkin/ibid/coords-892484
Reviewer Review Type Date Requested Status
Keegan Carruthers-Smith Approve on 2011-11-30
Stefano Rivera 2011-11-19 Approve on 2011-11-19
Review via email: mp+82773@code.launchpad.net

Commit message

Add lookup for coordinates of places

To post a comment you must log in.
lp:~max-rabkin/ibid/coords-892484 updated on 2011-11-19
1045. By Max Rabkin on 2011-11-19

Allow 'coords'

1046. By Max Rabkin on 2011-11-19

More (needless) precision

1047. By Max Rabkin on 2011-11-19

Add names for zones

Stefano Rivera (stefanor) :
review: Approve
lp:~max-rabkin/ibid/coords-892484 updated on 2011-11-20
1048. By Max Rabkin on 2011-11-20

handle non-existent places

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ibid/plugins/geography.py'
2--- ibid/plugins/geography.py 2011-06-26 22:44:41 +0000
3+++ ibid/plugins/geography.py 2011-11-20 15:02:24 +0000
4@@ -1,7 +1,9 @@
5+# -*- coding: utf-8 -*-
6 # Copyright (c) 2009-2010, Jonathan Hitchcock, Michael Gorven, Stefano Rivera
7 # Released under terms of the MIT/X/Expat Licence. See COPYING for details.
8+from __future__ import division
9
10-from math import acos, sin, cos, radians
11+from math import acos, sin, cos, degrees, radians
12 from urllib import quote, urlencode
13 from urlparse import urljoin
14 import re
15@@ -29,15 +31,20 @@
16 'description': u'Returns the distance between two places',
17 'categories': ('lookup', 'calculate',),
18 }
19+features['coordinates'] = {
20+ 'description': u'Gives the coordinates of a place',
21+ 'categories': ('lookup',),
22+}
23 class Distance(Processor):
24 usage = u"""distance [in <unit>] between <source> and <destination>
25- place search for <placename>"""
26+ place search for <place>
27+ coordinates for <place>"""
28
29 # For Mathematics, see:
30 # http://www.mathforum.com/library/drmath/view/51711.html
31 # http://mathworld.wolfram.com/GreatCircle.html
32
33- features = ('distance',)
34+ features = ('distance', 'coordinates')
35
36 default_unit_names = {
37 'km': "kilometres",
38@@ -59,10 +66,19 @@
39 if js['totalResultsCount'] == 0:
40 return None
41 info = js['geonames'][0]
42- return {'name': "%s, %s, %s" % (info['name'], info['adminName1'], info['countryName']),
43+ return {'name': self.format_name(info),
44 'lng': radians(info['lng']),
45 'lat': radians(info['lat'])}
46
47+ def format_name(self, info):
48+ parts = info['name'], info['adminName1'], info['countryName']
49+ parts = filter(None, parts)
50+ uniq_parts = [parts[0]]
51+ for part in parts[1:]:
52+ if part != uniq_parts[-1]:
53+ uniq_parts.append(part)
54+ return ', '.join(uniq_parts)
55+
56 @match(r'^(?:(?:search\s+for\s+place)|(?:place\s+search\s+for)|(?:places\s+for))\s+(\S.+?)\s*$')
57 def placesearch(self, event, place):
58 js = self.get_place_data(place, 10)
59@@ -110,6 +126,83 @@
60 conjunction=u'or'),
61 })
62
63+ def degrees_minutes_seconds(self, degrees, kind):
64+ degs = int(degrees)
65+ minutes = abs(degrees - degs)*60
66+ mins = int(minutes)
67+ secs = int((minutes-mins)*60)
68+
69+ dirn = ''
70+ if kind == 'lat':
71+ if degs > 0:
72+ dirn = ' N'
73+ elif degs < 0:
74+ dirn = ' S'
75+ else:
76+ if degs > 0:
77+ dirn = ' E'
78+ elif degs < 0:
79+ dirn = ' W'
80+ degs = abs(degs)
81+ return u'%i° %iʹ %iʺ%s' % (degs, mins, secs, dirn)
82+
83+ @match(r"coord(?:inate)?s (?:for|of|to) (.*)")
84+ def coordinates(self, event, place):
85+ place_data = self.get_place(place)
86+ if not place_data:
87+ event.addresponse("I've never heard of %s", place)
88+ return
89+
90+ lat_deg = degrees(place_data['lat'])
91+ lng_deg = degrees(place_data['lng'])
92+ place_data.update({
93+ 'lat_deg': lat_deg,
94+ 'lng_deg': lng_deg,
95+ 'lat_dms': self.degrees_minutes_seconds(lat_deg, 'lat'),
96+ 'lng_dms': self.degrees_minutes_seconds(lng_deg, 'lng'),
97+ })
98+
99+ latitudes = [('North Pole', 90, 'back of beyond'),
100+ ('Arctic Circle', 66+33/60+39/3600,
101+ 'Arctic'),
102+ ('Tropic of Cancer', 23+26/30+21/3600,
103+ 'north temperate zone'),
104+ ('Equator', 0,
105+ 'northern tropics'),
106+ ('Tropic of Capricorn', -(23+26/30+21/3600),
107+ 'southern tropics'),
108+ ('Antarctic Circle', -(66+33/60+39/3600),
109+ 'south temperate zone'),
110+ ('South Pole', -90,
111+ 'Antarctic'),
112+ ]
113+ for name, lat, zone in latitudes:
114+ if abs(lat-lat_deg) <= 1/60:
115+ if name.endswith('Pole'):
116+ place_data['lat_desc'] = 'at the ' + name
117+ else:
118+ place_data['lat_desc'] = 'on the ' + name
119+ break
120+ elif abs(lat-lat_deg) <= 2:
121+ place_data['lat_desc'] = 'near the ' + name
122+ break
123+ else:
124+ for (name1, lat1, _), (name2, lat2, zone) in zip(latitudes, latitudes[1:]):
125+ if lat1 > lat_deg > lat2:
126+ place_data['lat_desc'] = 'in the ' + zone
127+ break
128+ else:
129+ place_data['lat_desc'] = 'beyond the fields we know'
130+
131+ place_data['tz'] = round(lng_deg/15)
132+
133+ event.addresponse("%(name)s is at %(lat_dms)s, %(lng_dms)s "
134+ u"(%(lat_deg)0.4f°, %(lng_deg)0.4f°). "
135+ "That's in nautical time zone GMT%(tz)+i, "
136+ "%(lat_desc)s.",
137+ place_data)
138+
139+
140 features['weather'] = {
141 'description': u'Retrieves current weather and forecasts for cities.',
142 'categories': ('lookup', 'web',),

Subscribers

People subscribed via source and target branches

to all changes: