|
0
|
1 |
from django.contrib.syndication.feeds import Feed as BaseFeed, FeedDoesNotExist |
|
|
2 |
from django.utils.feedgenerator import Atom1Feed, Rss201rev2Feed |
|
|
3 |
|
|
|
4 |
class GeoFeedMixin(object): |
|
|
5 |
""" |
|
|
6 |
This mixin provides the necessary routines for SyndicationFeed subclasses |
|
|
7 |
to produce simple GeoRSS or W3C Geo elements. |
|
|
8 |
""" |
|
|
9 |
|
|
|
10 |
def georss_coords(self, coords): |
|
|
11 |
""" |
|
|
12 |
In GeoRSS coordinate pairs are ordered by lat/lon and separated by |
|
|
13 |
a single white space. Given a tuple of coordinates, this will return |
|
|
14 |
a unicode GeoRSS representation. |
|
|
15 |
""" |
|
|
16 |
return u' '.join([u'%f %f' % (coord[1], coord[0]) for coord in coords]) |
|
|
17 |
|
|
|
18 |
def add_georss_point(self, handler, coords, w3c_geo=False): |
|
|
19 |
""" |
|
|
20 |
Adds a GeoRSS point with the given coords using the given handler. |
|
|
21 |
Handles the differences between simple GeoRSS and the more pouplar |
|
|
22 |
W3C Geo specification. |
|
|
23 |
""" |
|
|
24 |
if w3c_geo: |
|
|
25 |
lon, lat = coords[:2] |
|
|
26 |
handler.addQuickElement(u'geo:lat', u'%f' % lat) |
|
|
27 |
handler.addQuickElement(u'geo:lon', u'%f' % lon) |
|
|
28 |
else: |
|
|
29 |
handler.addQuickElement(u'georss:point', self.georss_coords((coords,))) |
|
|
30 |
|
|
|
31 |
def add_georss_element(self, handler, item, w3c_geo=False): |
|
|
32 |
""" |
|
|
33 |
This routine adds a GeoRSS XML element using the given item and handler. |
|
|
34 |
""" |
|
|
35 |
# Getting the Geometry object. |
|
|
36 |
geom = item.get('geometry', None) |
|
|
37 |
if not geom is None: |
|
|
38 |
if isinstance(geom, (list, tuple)): |
|
|
39 |
# Special case if a tuple/list was passed in. The tuple may be |
|
|
40 |
# a point or a box |
|
|
41 |
box_coords = None |
|
|
42 |
if isinstance(geom[0], (list, tuple)): |
|
|
43 |
# Box: ( (X0, Y0), (X1, Y1) ) |
|
|
44 |
if len(geom) == 2: |
|
|
45 |
box_coords = geom |
|
|
46 |
else: |
|
|
47 |
raise ValueError('Only should be two sets of coordinates.') |
|
|
48 |
else: |
|
|
49 |
if len(geom) == 2: |
|
|
50 |
# Point: (X, Y) |
|
|
51 |
self.add_georss_point(handler, geom, w3c_geo=w3c_geo) |
|
|
52 |
elif len(geom) == 4: |
|
|
53 |
# Box: (X0, Y0, X1, Y1) |
|
|
54 |
box_coords = (geom[:2], geom[2:]) |
|
|
55 |
else: |
|
|
56 |
raise ValueError('Only should be 2 or 4 numeric elements.') |
|
|
57 |
# If a GeoRSS box was given via tuple. |
|
|
58 |
if not box_coords is None: |
|
|
59 |
if w3c_geo: raise ValueError('Cannot use simple GeoRSS box in W3C Geo feeds.') |
|
|
60 |
handler.addQuickElement(u'georss:box', self.georss_coords(box_coords)) |
|
|
61 |
else: |
|
|
62 |
# Getting the lower-case geometry type. |
|
|
63 |
gtype = str(geom.geom_type).lower() |
|
|
64 |
if gtype == 'point': |
|
|
65 |
self.add_georss_point(handler, geom.coords, w3c_geo=w3c_geo) |
|
|
66 |
else: |
|
|
67 |
if w3c_geo: raise ValueError('W3C Geo only supports Point geometries.') |
|
|
68 |
# For formatting consistent w/the GeoRSS simple standard: |
|
|
69 |
# http://georss.org/1.0#simple |
|
|
70 |
if gtype in ('linestring', 'linearring'): |
|
|
71 |
handler.addQuickElement(u'georss:line', self.georss_coords(geom.coords)) |
|
|
72 |
elif gtype in ('polygon',): |
|
|
73 |
# Only support the exterior ring. |
|
|
74 |
handler.addQuickElement(u'georss:polygon', self.georss_coords(geom[0].coords)) |
|
|
75 |
else: |
|
|
76 |
raise ValueError('Geometry type "%s" not supported.' % geom.geom_type) |
|
|
77 |
|
|
|
78 |
### SyndicationFeed subclasses ### |
|
|
79 |
class GeoRSSFeed(Rss201rev2Feed, GeoFeedMixin): |
|
|
80 |
def rss_attributes(self): |
|
|
81 |
attrs = super(GeoRSSFeed, self).rss_attributes() |
|
|
82 |
attrs[u'xmlns:georss'] = u'http://www.georss.org/georss' |
|
|
83 |
return attrs |
|
|
84 |
|
|
|
85 |
def add_item_elements(self, handler, item): |
|
|
86 |
super(GeoRSSFeed, self).add_item_elements(handler, item) |
|
|
87 |
self.add_georss_element(handler, item) |
|
|
88 |
|
|
|
89 |
def add_root_elements(self, handler): |
|
|
90 |
super(GeoRSSFeed, self).add_root_elements(handler) |
|
|
91 |
self.add_georss_element(handler, self.feed) |
|
|
92 |
|
|
|
93 |
class GeoAtom1Feed(Atom1Feed, GeoFeedMixin): |
|
|
94 |
def root_attributes(self): |
|
|
95 |
attrs = super(GeoAtom1Feed, self).root_attributes() |
|
|
96 |
attrs[u'xmlns:georss'] = u'http://www.georss.org/georss' |
|
|
97 |
return attrs |
|
|
98 |
|
|
|
99 |
def add_item_elements(self, handler, item): |
|
|
100 |
super(GeoAtom1Feed, self).add_item_elements(handler, item) |
|
|
101 |
self.add_georss_element(handler, item) |
|
|
102 |
|
|
|
103 |
def add_root_elements(self, handler): |
|
|
104 |
super(GeoAtom1Feed, self).add_root_elements(handler) |
|
|
105 |
self.add_georss_element(handler, self.feed) |
|
|
106 |
|
|
|
107 |
class W3CGeoFeed(Rss201rev2Feed, GeoFeedMixin): |
|
|
108 |
def rss_attributes(self): |
|
|
109 |
attrs = super(W3CGeoFeed, self).rss_attributes() |
|
|
110 |
attrs[u'xmlns:geo'] = u'http://www.w3.org/2003/01/geo/wgs84_pos#' |
|
|
111 |
return attrs |
|
|
112 |
|
|
|
113 |
def add_item_elements(self, handler, item): |
|
|
114 |
super(W3CGeoFeed, self).add_item_elements(handler, item) |
|
|
115 |
self.add_georss_element(handler, item, w3c_geo=True) |
|
|
116 |
|
|
|
117 |
def add_root_elements(self, handler): |
|
|
118 |
super(W3CGeoFeed, self).add_root_elements(handler) |
|
|
119 |
self.add_georss_element(handler, self.feed, w3c_geo=True) |
|
|
120 |
|
|
|
121 |
### Feed subclass ### |
|
|
122 |
class Feed(BaseFeed): |
|
|
123 |
""" |
|
|
124 |
This is a subclass of the `Feed` from `django.contrib.syndication`. |
|
|
125 |
This allows users to define a `geometry(obj)` and/or `item_geometry(item)` |
|
|
126 |
methods on their own subclasses so that geo-referenced information may |
|
|
127 |
placed in the feed. |
|
|
128 |
""" |
|
|
129 |
feed_type = GeoRSSFeed |
|
|
130 |
|
|
|
131 |
def feed_extra_kwargs(self, obj): |
|
|
132 |
return {'geometry' : self.__get_dynamic_attr('geometry', obj)} |
|
|
133 |
|
|
|
134 |
def item_extra_kwargs(self, item): |
|
|
135 |
return {'geometry' : self.__get_dynamic_attr('item_geometry', item)} |