/django/contrib/gis/feeds.py
Python | 135 lines | 83 code | 16 blank | 36 comment | 17 complexity | 0d979417c8ea1269bfee7d3a1d1ff5fd MD5 | raw file
1from django.contrib.syndication.feeds import Feed as BaseFeed, FeedDoesNotExist 2from django.utils.feedgenerator import Atom1Feed, Rss201rev2Feed 3 4class 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 ### 79class 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 93class 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 107class 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 ### 122class 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)}