From 196b01c16cd5bb4885887e295b39c1267cbdd6fe Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 1 May 2009 01:21:29 +0000 Subject: [PATCH] + make interface to datasource attributes, featuresets, and features more pythonicby adding feature.envelope() method returning combined extent of geometries, ds.fields() method returning list of names, and featureset.features() which returns list of all features in featurset - now accessible via datasource.all_features()- closes #283, #280, #171 --- bindings/python/mapnik/__init__.py | 31 ++++++++++++++- bindings/python/mapnik_datasource.cpp | 55 ++++++++++++++++++++++++++- bindings/python/mapnik_feature.cpp | 1 + bindings/python/mapnik_featureset.cpp | 29 +++++++------- bindings/python/mapnik_map.cpp | 20 +++++----- include/mapnik/feature.hpp | 21 +++++++++- tests/python_tests/object_test.py | 2 +- 7 files changed, 131 insertions(+), 28 deletions(-) diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index da28f952e..b1701767c 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -90,10 +90,39 @@ class _Projection(Projection,_injector): return forward_(obj,self) def inverse(self,obj): return inverse_(obj,self) - + +def get_types(num): + if num == 1: + return int + elif num == 2: + return float + elif num == 3: + return float + elif num == 4: + return str + elif num == 5: + return Geometry2d + elif num == 6: + return object + class _Datasource(Datasource,_injector): def describe(self): return Describe(self) + def field_types(self): + return map(get_types,self._field_types()) + def all_features(self): + query = Query(self.envelope(),1.0) + for fld in self.fields(): + query.add_property_name(fld) + return self.features(query).features + +class _Feature(Feature,_injector): + @property + def attributes(self): + attr = {} + for prop in self.properties: + attr[prop[0]] = prop[1] + return attr #class _Filter(Filter,_injector): # """Mapnik Filter expression. diff --git a/bindings/python/mapnik_datasource.cpp b/bindings/python/mapnik_datasource.cpp index c2f0da959..45b08cf03 100644 --- a/bindings/python/mapnik_datasource.cpp +++ b/bindings/python/mapnik_datasource.cpp @@ -25,6 +25,8 @@ #include // stl #include +#include + // mapnik #include #include @@ -35,6 +37,8 @@ using mapnik::datasource; using mapnik::point_datasource; +using mapnik::layer_descriptor; +using mapnik::attribute_descriptor; struct ds_pickle_suite : boost::python::pickle_suite { @@ -92,7 +96,52 @@ namespace } return ss.str(); } -} + + std::string encoding(boost::shared_ptr const& ds) + { + layer_descriptor ld = ds->get_descriptor(); + return ld.get_encoding(); + } + + std::string name(boost::shared_ptr const& ds) + { + layer_descriptor ld = ds->get_descriptor(); + return ld.get_name(); + } + + boost::python::list fields(boost::shared_ptr const& ds) + { + boost::python::list flds; + if (ds) + { + layer_descriptor ld = ds->get_descriptor(); + std::vector const& desc_ar = ld.get_descriptors(); + std::vector::const_iterator it = desc_ar.begin(); + std::vector::const_iterator end = desc_ar.end(); + for (; it != end; ++it) + { + flds.append(it->get_name()); + } + } + return flds; + } + boost::python::list field_types(boost::shared_ptr const& ds) + { + boost::python::list fld_types; + if (ds) + { + layer_descriptor ld = ds->get_descriptor(); + std::vector const& desc_ar = ld.get_descriptors(); + std::vector::const_iterator it = desc_ar.begin(); + std::vector::const_iterator end = desc_ar.end(); + for (; it != end; ++it) + { + unsigned type = it->get_type(); + fld_types.append(type); + } + } + return fld_types; + }} void export_datasource() { @@ -104,6 +153,10 @@ void export_datasource() .def("envelope",&datasource::envelope) .def("descriptor",&datasource::get_descriptor) //todo .def("features",&datasource::features) + .def("fields",&fields) + .def("_field_types",&field_types) + .def("encoding",&encoding) //todo expose as property + .def("name",&name) .def("features_at_point",&datasource::features_at_point) .def("params",&datasource::params,return_value_policy(), "The configuration parameters of the data source. " diff --git a/bindings/python/mapnik_feature.cpp b/bindings/python/mapnik_feature.cpp index b2b9e577f..3fc24cfa4 100644 --- a/bindings/python/mapnik_feature.cpp +++ b/bindings/python/mapnik_feature.cpp @@ -221,6 +221,7 @@ void export_feature() // .def("add_geometry", // TODO define more mapnik::Feature methods .def("num_geometries",&Feature::num_geometries) .def("get_geometry", make_function(get_geom1,return_value_policy())) + .def("envelope", &Feature::envelope) ; class_ >("Properties") diff --git a/bindings/python/mapnik_featureset.cpp b/bindings/python/mapnik_featureset.cpp index 979892238..0ded88b3c 100644 --- a/bindings/python/mapnik_featureset.cpp +++ b/bindings/python/mapnik_featureset.cpp @@ -29,18 +29,21 @@ namespace { using namespace boost::python; - inline object pass_through(object const& o) { return o; } - - inline mapnik::feature_ptr next(mapnik::featureset_ptr const& itr) - { - if (!itr) - { - PyErr_SetString(PyExc_StopIteration, "No more features."); - boost::python::throw_error_already_set(); - } - return itr->next(); - } + list features(mapnik::featureset_ptr const& itr) + { + list l; + while (true) + { + mapnik::feature_ptr fp = itr->next(); + if (!fp) + { + break; + } + l.append(fp); + } + return l; + } } void export_featureset() @@ -51,8 +54,6 @@ void export_featureset() class_, boost::noncopyable>("Featureset",no_init) - .def("next",next) - .def("__iter__",pass_through) + .add_property("features",features) ; } - diff --git a/bindings/python/mapnik_map.cpp b/bindings/python/mapnik_map.cpp index 88c06c4fe..204248be2 100644 --- a/bindings/python/mapnik_map.cpp +++ b/bindings/python/mapnik_map.cpp @@ -267,11 +267,11 @@ void export_map() "otherwise will return None.\n" "\n" "Usage:\n" - ">>> feat = m.query_map_point(0,200,200)\n" - ">>> feat\n" - ">>> \n" - ">>> feat.next()\n" - ">>> \n" + ">>> featureset = m.query_map_point(0,200,200)\n" + ">>> featureset\n" + "\n" + ">>> featureset.features\n" + ">>> []\n" ) .def("query_point",&Map::query_point, @@ -282,11 +282,11 @@ void export_map() "otherwise will return None.\n" "\n" "Usage:\n" - ">>> feat = m.query_point(0,-122,48)\n" - ">>> feat\n" - ">>> \n" - ">>> feat.next()\n" - ">>> \n" + ">>> featureset = m.query_point(0,-122,48)\n" + ">>> featureset\n" + "\n" + ">>> featureset.features\n" + ">>> []\n" ) .def("remove_all",&Map::remove_all, diff --git a/include/mapnik/feature.hpp b/include/mapnik/feature.hpp index 518b6f90a..17a4683a0 100644 --- a/include/mapnik/feature.hpp +++ b/include/mapnik/feature.hpp @@ -94,7 +94,26 @@ namespace mapnik { { return geom_cont_[index]; } - + + Envelope envelope() const + { + Envelope result; + for (unsigned i=0;i box = geom.envelope(); + result.init(box.minx(),box.miny(),box.maxx(),box.maxy()); + } + else + { + result.expand_to_include(geom.envelope()); + } + } + return result; + } + const raster_type& get_raster() const { return raster_; diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index 09fbeca87..b445431d3 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -130,7 +130,7 @@ def test_shapefile_init(): # Shapefile properties def test_shapefile_properties(): s = mapnik.Shapefile(file='../../demo/data/boundaries') - f = s.features_at_point(s.envelope().center()).next() + f = s.features_at_point(s.envelope().center()).features[0] eq_(f.properties['CGNS_FID'], u'6f733341ba2011d892e2080020a0f4c9') eq_(f.properties['COUNTRY'], u'CAN')