From 18b811d19db0ea88afcdbf9dffdcfbbc12cd83d4 Mon Sep 17 00:00:00 2001 From: Alberto Valverde Date: Fri, 19 Mar 2010 18:14:46 +0000 Subject: [PATCH] implemented a mechanism in mapnik2._injector to be able to override c++ methods (at the python layer only). Used it to implement a friendlier constructor for Feature and a add_geometry() method that accepts shapely.geometry.Geometrys, and wkb/wkt strings --- bindings/python/mapnik/__init__.py | 45 +++++++++++++++++++++++++++++- tests/python_tests/feature_test.py | 39 ++++++++++++++++++-------- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index 35944ed18..0ac30760b 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -63,6 +63,8 @@ class _injector(object): for b in bases: if type(b) not in (self, type): for k,v in dict.items(): + if hasattr(b, k): + setattr(b, '_c_'+k, getattr(b, k)) setattr(b,k,v) return type.__init__(self, name, bases, dict) @@ -250,15 +252,56 @@ class _DeprecatedFeatureProperties(object): "feature object itself for the same effect", DeprecationWarning, 2) return iter(self._feature) -class _Feature(Feature,_injector): +class _Feature(Feature, _injector): + """ + A Feature. + + TODO: docs + """ @property def properties(self): return _DeprecatedFeatureProperties(self) @property def attributes(self): + #XXX Returns a copy! changes to it won't affect feat.'s attrs. + # maybe deprecate? return dict(self) + @property + def geometry(self): + if self.num_geometries() > 0: + return self.get_geometry(0) + + @property + def geometries(self): + return [self.get_geometry(i) for i in xrange(self.num_geometries())] + + def __init__(self, id, geometry=None, **properties): + Feature._c___init__(self, id) + if geometry is not None: + self.add_geometry(geometry) + for k, v in properties.iteritems(): + self[k] = v + + def add_geometry(self, geometry): + geometry = self._as_wkb(geometry) + Feature._c_add_geometry(self, geometry) + + def _as_wkb(self, geometry): + if hasattr(geometry, 'wkb'): + # a shapely.geometry.Geometry + geometry = geometry.wkb + if isinstance(geometry, str): + # ignoring unicode un purpose + for type_ in ('POINT', 'POLYGON', 'LINE'): + if type_ in geometry: + # A WKT encoded string + from shapely import wkt + geometry = wkt.loads(geometry).wkb + return geometry + raise TypeError("%r (%s) not supported" % (geometry, type(geometry))) + class _Symbolizer(Symbolizer,_injector): def symbol(self): return getattr(self,self.type())() diff --git a/tests/python_tests/feature_test.py b/tests/python_tests/feature_test.py index c760b38da..e34b1051f 100644 --- a/tests/python_tests/feature_test.py +++ b/tests/python_tests/feature_test.py @@ -13,6 +13,18 @@ class FeatureTest(unittest.TestCase): f = self.makeOne(1) self.failUnless(f is not None) + def test_python_extended_constructor(self): + try: + from shapely.geometry import Point + except ImportError: + raise Todo("Make this test not dependant on shapely") + + f = self.makeOne(1, Point(3,6), foo="bar") + self.failUnlessEqual(f['foo'], 'bar') + env = f.geometry.envelope() + self.failUnlessEqual(env.minx, 3) + self.failUnlessEqual(env.miny, 6) + def test_set_get_properties(self): f = self.makeOne(1) counter = itertools.count(0) @@ -26,19 +38,24 @@ class FeatureTest(unittest.TestCase): for v in (1, True, 1.4, "foo", u"avión"): test_val(v) - def test_add_wkb_geometry_(self): + + def test_add_wkb_geometry(self): try: from shapely.geometry import Point except ImportError: raise Todo("Make this test not dependant on shapely") - f = self.makeOne(1) - self.failUnlessEqual(f.num_geometries(), 0) - f.add_geometry(Point(3,6).wkb) - self.failUnlessEqual(f.num_geometries(), 1) - geom = f.get_geometry(0) - env = geom.envelope() - self.failUnlessEqual(env.minx, 3) - self.failUnlessEqual(env.minx, env.maxx) - self.failUnlessEqual(env.miny, 6) - self.failUnlessEqual(env.miny, env.maxy) + def add_it(geometry): + f = self.makeOne(1) + self.failUnlessEqual(len(f.geometries), 0) + f.add_geometry(geometry) + self.failUnlessEqual(len(f.geometries), 1) + env = f.geometry.envelope() + self.failUnlessEqual(env.minx, 3) + self.failUnlessEqual(env.minx, env.maxx) + self.failUnlessEqual(env.miny, 6) + self.failUnlessEqual(env.miny, env.maxy) + + geometries = (Point(3,6), 'POINT(3 6)', Point(3,6).wkb) + for geom in geometries: + add_it(geom)