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

Proposed by Max Rabkin
Status: Merged
Approved by: Stefano Rivera
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
Stefano Rivera Approve
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
1045. By Max Rabkin

Allow 'coords'

1046. By Max Rabkin

More (needless) precision

1047. By Max Rabkin

Add names for zones

Revision history for this message
Stefano Rivera (stefanor) :
review: Approve
lp:~max-rabkin/ibid/coords-892484 updated
1048. By Max Rabkin

handle non-existent places

Revision history for this message
Keegan Carruthers-Smith (keegan-csmith) :
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: