app/django/contrib/gis/feeds.py
changeset 323 ff1a9aa48cfd
equal deleted inserted replaced
322:6641e941ef1e 323:ff1a9aa48cfd
       
     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)}