You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

145 lines
5.8 KiB

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