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.
|
:license: FreeBSD and LGPL, see license.* for more details.
|
||||||
'''
|
'''
|
||||||
import numbers
|
import numbers
|
||||||
|
import warnings
|
||||||
|
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from feedgen.ext.base import BaseEntryExtension
|
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):
|
class GeoEntryExtension(BaseEntryExtension):
|
||||||
'''FeedEntry extension for Simple GeoRSS.
|
'''FeedEntry extension for Simple GeoRSS.
|
||||||
'''
|
'''
|
||||||
|
@ -224,3 +261,62 @@ class GeoEntryExtension(BaseEntryExtension):
|
||||||
self.__radius = radius
|
self.__radius = radius
|
||||||
|
|
||||||
return self.__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