Add a geom_from_geo_interface method for GeoRSS Entry
A standard way for different geometry libraries in Python to be interoperable is a `__geo_interface__` for the geometry (see the specification: https://gist.github.com/sgillies/2217756). This includes the shapely library, geometries from QGIS, and geometries in Esri's arcpy libraries for ArcGIS desktop and ArcGIS pro. To make it easier to generate a georss entry a simple method which does the conversion (of the supported geometries only) and sets the appropriate geometry type. This includes a custom error for the geometry being incompatible and a custom warning for a polygon with interior holes. This is done to store the geometries on the exception / warning if required for debugging.
This commit is contained in:
parent
642862bb2b
commit
8d413f576f
1 changed files with 96 additions and 0 deletions
|
@ -10,11 +10,48 @@
|
|||
:license: FreeBSD and LGPL, see license.* for more details.
|
||||
'''
|
||||
import numbers
|
||||
import warnings
|
||||
|
||||
from lxml import etree
|
||||
from feedgen.ext.base import BaseEntryExtension
|
||||
|
||||
|
||||
class GeoRSSPolygonInteriorWarning(Warning):
|
||||
"""
|
||||
Simple placeholder for warning about ignored polygon interiors.
|
||||
|
||||
Stores the original geom on a ``geom`` attribute (if required warnings are
|
||||
raised as errors).
|
||||
"""
|
||||
|
||||
def __init__(self, geom, *args, **kwargs):
|
||||
self.geom = geom
|
||||
super(GeoRSSPolygonInteriorWarning, self).__init__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return '{:d} interiors of polygon ignored'.format(
|
||||
len(self.geom.__geo_interface__['coordinates']) - 1 # ignore exterior in count
|
||||
)
|
||||
|
||||
class GeoRSSGeometryError(ValueError):
|
||||
"""
|
||||
Subclass of ValueError for a GeoRSS geometry error
|
||||
|
||||
Only some geometries are supported in Simple GeoRSS, so if not raise an
|
||||
error. Offending geometry is stored on the ``geom`` attribute.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, geom, *args, **kwargs):
|
||||
self.geom = geom
|
||||
super(GeoRSSGeometryError, self).__init__(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return "Geometry of type '{}' not in Point, Linestring or Polygon".format(
|
||||
self.geom.__geo_interface__['type']
|
||||
)
|
||||
|
||||
|
||||
class GeoEntryExtension(BaseEntryExtension):
|
||||
'''FeedEntry extension for Simple GeoRSS.
|
||||
'''
|
||||
|
@ -224,3 +261,62 @@ class GeoEntryExtension(BaseEntryExtension):
|
|||
self.__radius = radius
|
||||
|
||||
return self.__radius
|
||||
|
||||
def geom_from_geo_interface(self, geom):
|
||||
'''
|
||||
Generate a georss geometry from some Python object with a
|
||||
``__geo_interface__`` property (see the `geo_interface specification by
|
||||
Sean Gillies`_geointerface )
|
||||
|
||||
Note only a subset of GeoJSON (see `geojson.org`_geojson ) can be easily
|
||||
converted to GeoRSS:
|
||||
|
||||
- Point
|
||||
- LineString
|
||||
- Polygon (if there are holes / donuts in the polygons a warning will be
|
||||
generaated
|
||||
|
||||
Other GeoJson types will raise a ``ValueError``.
|
||||
|
||||
.. note:: The geometry is assumed to be x, y as longitude, latitude in
|
||||
the WGS84 projection.
|
||||
|
||||
.. _geointerface: https://gist.github.com/sgillies/2217756
|
||||
.. _geojson: https://geojson.org/
|
||||
|
||||
:param geom: Geometry object with a __geo_interface__ property
|
||||
:return: the formatted GeoRSS geometry
|
||||
'''
|
||||
geojson = geom.__geo_interface__
|
||||
|
||||
if geojson['type'] not in ('Point', 'LineString', 'Polygon'):
|
||||
raise GeoRSSGeometryError(geom)
|
||||
|
||||
if geojson['type'] == 'Point':
|
||||
|
||||
coords = '%f %f'.format(
|
||||
geojson['coordinates'][1], # latitude is y
|
||||
geojson['coordinates'][0]
|
||||
)
|
||||
return self.point(coords)
|
||||
|
||||
elif geojson['type'] == 'LineString':
|
||||
|
||||
coords = ' '.join(
|
||||
'%f %f'.format(vertex[1], vertex[0])
|
||||
for vertex in
|
||||
geojson['coordinates']
|
||||
)
|
||||
return self.line(coords)
|
||||
|
||||
elif geojson['type'] == 'Polygon':
|
||||
|
||||
if len(geojson['coordinates']) > 1:
|
||||
warnings.warn(GeoRSSPolygonInteriorWarning(geom))
|
||||
|
||||
coords = ' '.join(
|
||||
'%f %f'.format(vertex[1], vertex[0])
|
||||
for vertex in
|
||||
geojson['coordinates'][0]
|
||||
)
|
||||
return self.polygon(coords)
|
||||
|
|
Loading…
Reference in a new issue