From 975afebd87b1736752dc4951e08f1d00c21ce196 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Wed, 11 Jan 2012 20:03:23 -0800 Subject: [PATCH] ensure all plugins report best guess at top level geometry_type using new descriptor attribute --- plugins/input/csv/csv_datasource.cpp | 19 ++++ plugins/input/geos/geos_datasource.cpp | 25 ++++++ plugins/input/kismet/kismet_datasource.cpp | 2 + plugins/input/kismet/kismet_datasource.hpp | 2 +- plugins/input/ogr/ogr_datasource.cpp | 65 ++++++++++++++ plugins/input/osm/osm_datasource.cpp | 3 + plugins/input/postgis/postgis_datasource.cpp | 81 +++++++++++++++-- plugins/input/shape/shape_datasource.cpp | 29 +++++++ plugins/input/sqlite/sqlite_datasource.cpp | 87 +++++++++++++++++-- .../templates/helloworld/hello_datasource.cpp | 4 + 10 files changed, 301 insertions(+), 16 deletions(-) diff --git a/plugins/input/csv/csv_datasource.cpp b/plugins/input/csv/csv_datasource.cpp index 1d44b90c2..06e0e1071 100644 --- a/plugins/input/csv/csv_datasource.cpp +++ b/plugins/input/csv/csv_datasource.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include // mapnik::boolean // stl @@ -830,6 +831,24 @@ void csv_datasource::parse_csv(T& stream, { if (!quiet_) std::clog << "CSV Plugin: could not parse any lines of data\n"; } + else + { + std::string g_type(""); + std::string prev_type(""); + boost::ptr_vector paths; + unsigned num_features = features_.size(); + for (int i = 0; i < num_features; ++i) + { + mapnik::util::to_type_str(features_[i]->paths(),g_type); + if (!prev_type.empty() && g_type != prev_type) + { + g_type = "collection"; + break; + } + prev_type = g_type; + } + desc_.set_geometry_type(g_type); + } } std::string csv_datasource::name() diff --git a/plugins/input/geos/geos_datasource.cpp b/plugins/input/geos/geos_datasource.cpp index f09670a46..bd423d227 100644 --- a/plugins/input/geos/geos_datasource.cpp +++ b/plugins/input/geos/geos_datasource.cpp @@ -212,6 +212,31 @@ void geos_datasource::bind() const } } + // get geometry type + const int type = GEOSGeomTypeId(*geometry_); + switch (type) + { + case GEOS_POINT: + case GEOS_MULTIPOINT: + desc_.set_geometry_type("point"); + break; + case GEOS_LINESTRING: + case GEOS_LINEARRING: + case GEOS_MULTILINESTRING: + desc_.set_geometry_type("linestring"); + break; + case GEOS_POLYGON: + case GEOS_MULTIPOLYGON: + desc_.set_geometry_type("polygon"); + break; + case GEOS_GEOMETRYCOLLECTION: + desc_.set_geometry_type("collection"); + break; + default: + break; + } + + if (! extent_initialized_) { throw datasource_exception("GEOS Plugin: cannot determine extent for geometry"); diff --git a/plugins/input/kismet/kismet_datasource.cpp b/plugins/input/kismet/kismet_datasource.cpp index ce88f7095..e9fe4d2eb 100644 --- a/plugins/input/kismet/kismet_datasource.cpp +++ b/plugins/input/kismet/kismet_datasource.cpp @@ -119,6 +119,8 @@ void kismet_datasource::bind() const { if (is_bound_) return; + desc_.set_geometry_type("point"); + is_bound_ = true; } diff --git a/plugins/input/kismet/kismet_datasource.hpp b/plugins/input/kismet/kismet_datasource.hpp index 69512e0c9..cfae3b1b5 100644 --- a/plugins/input/kismet/kismet_datasource.hpp +++ b/plugins/input/kismet/kismet_datasource.hpp @@ -62,7 +62,7 @@ private: unsigned int port_; int type_; std::string srs_; - mapnik::layer_descriptor desc_; + mutable mapnik::layer_descriptor desc_; boost::shared_ptr kismet_thread; }; diff --git a/plugins/input/ogr/ogr_datasource.cpp b/plugins/input/ogr/ogr_datasource.cpp index 230244655..9fa1bf3ca 100644 --- a/plugins/input/ogr/ogr_datasource.cpp +++ b/plugins/input/ogr/ogr_datasource.cpp @@ -28,6 +28,7 @@ #include "ogr_datasource.hpp" #include "ogr_featureset.hpp" #include "ogr_index_featureset.hpp" +#include "ogr_feature_ptr.hpp" // mapnik #include @@ -265,6 +266,70 @@ void ogr_datasource::bind() const } #endif + // get geometry type + // NOTE: wkbFlatten macro in ogr flattens 2.5d types into base 2d type + switch (wkbFlatten(layer->GetGeomType())) + { + case wkbPoint: + case wkbMultiPoint: + desc_.set_geometry_type("point"); + break; + case wkbLinearRing: + case wkbLineString: + case wkbMultiLineString: + desc_.set_geometry_type("linestring"); + break; + case wkbPolygon: + case wkbMultiPolygon: + desc_.set_geometry_type("polygon"); + break; + case wkbGeometryCollection: + desc_.set_geometry_type("collection"); + break; + case wkbNone: + case wkbUnknown: + { + // fallback to inspecting first actual geometry + // TODO - csv and shapefile inspect first 4 features + if (dataset_ && layer_.is_valid()) + { + OGRLayer* layer = layer_.layer(); + ogr_feature_ptr feat(layer->GetNextFeature()); + if ((*feat) != NULL) + { + OGRGeometry* geom = (*feat)->GetGeometryRef(); + if (geom && ! geom->IsEmpty()) + { + switch (wkbFlatten(geom->getGeometryType())) + { + case wkbPoint: + case wkbMultiPoint: + desc_.set_geometry_type("point"); + break; + case wkbLinearRing: + case wkbLineString: + case wkbMultiLineString: + desc_.set_geometry_type("linestring"); + break; + case wkbPolygon: + case wkbMultiPolygon: + desc_.set_geometry_type("polygon"); + break; + case wkbGeometryCollection: + desc_.set_geometry_type("collection"); + break; + default: + break; + } + } + } + } + break; + } + default: + break; + } + // deal with attributes descriptions OGRFeatureDefn* def = layer->GetLayerDefn(); if (def != 0) diff --git a/plugins/input/osm/osm_datasource.cpp b/plugins/input/osm/osm_datasource.cpp index 3098c992f..ce208f816 100644 --- a/plugins/input/osm/osm_datasource.cpp +++ b/plugins/input/osm/osm_datasource.cpp @@ -123,6 +123,9 @@ void osm_datasource::bind() const extent_ = box2d(b.w, b.s, b.e, b.n); } + // osm data is most likely a collection of geometry types + desc_.set_geometry_type("collection"); + is_bound_ = true; } diff --git a/plugins/input/postgis/postgis_datasource.cpp b/plugins/input/postgis/postgis_datasource.cpp index 2d0d642f9..ebcca86ae 100644 --- a/plugins/input/postgis/postgis_datasource.cpp +++ b/plugins/input/postgis/postgis_datasource.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include // boost #include @@ -106,6 +108,8 @@ void postgis_datasource::bind() const ConnectionManager *mgr=ConnectionManager::instance(); mgr->registerPool(creator_, *initial_size, *max_size); + std::string g_type; + shared_ptr > pool=mgr->getPool(creator_.id()); if (pool) { @@ -113,8 +117,6 @@ void postgis_datasource::bind() const if (conn && conn->isOK()) { - is_bound_ = true; - PoolGuard, shared_ptr > > guard(conn,pool); @@ -145,7 +147,7 @@ void postgis_datasource::bind() const if (!geometryColumn_.length() > 0 || srid_ == 0) { std::ostringstream s; - s << "SELECT f_geometry_column, srid FROM "; + s << "SELECT f_geometry_column, srid, lower(type) as type FROM "; s << GEOMETRY_COLUMNS <<" WHERE f_table_name='" << mapnik::sql_utils::unquote_double(geometry_table_) <<"'"; if (schema_.length() > 0) @@ -161,7 +163,7 @@ void postgis_datasource::bind() const } */ - shared_ptr rs=conn->executeQuery(s.str()); + shared_ptr rs = conn->executeQuery(s.str()); if (rs->next()) { geometryColumn_ = rs->getValue("f_geometry_column"); @@ -177,6 +179,25 @@ void postgis_datasource::bind() const std::clog << "Postgis Plugin: SRID=" << rs->getValue("srid") << " " << ex.what() << std::endl; } } + + g_type = rs->getValue("type"); + if (boost::algorithm::contains(g_type,"line")) + { + g_type = "linestring"; + } + else if (boost::algorithm::contains(g_type,"point")) + { + g_type = "point"; + } + else if (boost::algorithm::contains(g_type,"polygon")) + { + g_type = "polygon"; + } + else // geometry + { + g_type = "collection"; + } + desc_.set_geometry_type(g_type); } rs->close(); @@ -196,7 +217,7 @@ void postgis_datasource::bind() const } */ - shared_ptr rs=conn->executeQuery(s.str()); + shared_ptr rs = conn->executeQuery(s.str()); if (rs->next()) { try @@ -238,7 +259,7 @@ void postgis_datasource::bind() const */ - shared_ptr rs=conn->executeQuery(s.str()); + shared_ptr rs = conn->executeQuery(s.str()); int count = rs->getNumFields(); bool found_key_field = false; for (int i=0;iclose(); + + // fallback to querying first several features + if (g_type.empty() && !geometryColumn_.empty()) + { + s.str(""); + std::string g_type(""); + std::string prev_type(""); + s << "SELECT ST_GeometryType(\"" << geometryColumn_ << "\") AS geom" + << " FROM " << populate_tokens(table_); + if (row_limit_ > 0 && row_limit_ < 5) { + s << " LIMIT " << row_limit_; + } else { + s << " LIMIT 5"; + } + shared_ptr rs = conn->executeQuery(s.str()); + while (rs->next() && !rs->isNull(0)) + { + const char *data = rs->getValue(0); + if (boost::algorithm::icontains(data,"line")) + { + g_type = "linestring"; + } + else if (boost::algorithm::icontains(data,"point")) + { + g_type = "point"; + } + else if (boost::algorithm::icontains(data,"polygon")) + { + g_type = "polygon"; + } + else // geometry + { + g_type = "collection"; + break; + } + if (!prev_type.empty() && g_type != prev_type) + { + g_type = "collection"; + break; + } + prev_type = g_type; + } + desc_.set_geometry_type(g_type); + } + + is_bound_ = true; + } } } diff --git a/plugins/input/shape/shape_datasource.cpp b/plugins/input/shape/shape_datasource.cpp index 5a24994ce..dc34465f8 100644 --- a/plugins/input/shape/shape_datasource.cpp +++ b/plugins/input/shape/shape_datasource.cpp @@ -185,6 +185,35 @@ void shape_datasource::init(shape_io& shape) const int shape_type = shape.shp().read_ndr_integer(); if (shape_type == shape_io::shape_multipatch) throw datasource_exception("Shape Plugin: shapefile multipatch type is not supported"); + switch (shape_type) + { + case shape_io::shape_point: + case shape_io::shape_pointm: + case shape_io::shape_pointz: + case shape_io::shape_multipoint: + case shape_io::shape_multipointm: + case shape_io::shape_multipointz: + { + desc_.set_geometry_type("point"); + break; + } + case shape_io::shape_polyline: + case shape_io::shape_polylinem: + case shape_io::shape_polylinez: + { + desc_.set_geometry_type("linestring"); + break; + } + case shape_io::shape_polygon: + case shape_io::shape_polygonm: + case shape_io::shape_polygonz: + { + desc_.set_geometry_type("polygon"); + break; + } + default: + break; + } shape.shp().read_envelope(extent_); diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index 14b6a068c..999d51b54 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -28,6 +28,8 @@ // mapnik #include #include +#include +#include // boost #include @@ -69,16 +71,12 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind) /* TODO - throw if no primary key but spatial index is present? - remove auto-indexing + - if spatialite - leverage more of the metadata for geometry type detection */ boost::optional file = params_.get("file"); if (! file) throw datasource_exception("Sqlite Plugin: missing parameter"); - if (table_.empty()) - { - throw mapnik::datasource_exception("Sqlite Plugin: missing parameter"); - } - if (bind) { this->bind(); @@ -147,6 +145,44 @@ void sqlite_datasource::bind() const init_statements_.push_back(*initdb); } + // now actually create the connection and start executing setup sql + dataset_ = boost::make_shared(dataset_name_); + + boost::optional table_by_index = params_.get("table_by_index"); + + int passed_parameters = 0; + passed_parameters += params_.get("table") ? 1 : 0; + passed_parameters += table_by_index ? 1 : 0; + + if (passed_parameters > 1) + { + throw datasource_exception("SQLite Plugin: you can only select an by name " + "('table' parameter), by number ('table_by_index' parameter), " + "do not supply 2 or more of them at the same time" ); + } + + if (table_by_index) + { + std::vector tables; + sqlite_utils::get_tables(dataset_,tables); + if (*table_by_index >= tables.size()) + { + std::ostringstream s; + s << "SQLite Plugin: only " + << tables.size() + << " table(s) exist, cannot find table by index '" << *table_by_index << "'"; + + throw datasource_exception(s.str()); + } + table_ = tables[*table_by_index]; + + } + + if (table_.empty()) + { + throw mapnik::datasource_exception("Sqlite Plugin: missing
parameter"); + } + if (geometry_table_.empty()) { geometry_table_ = mapnik::sql_utils::table_from_sql(table_); @@ -169,9 +205,6 @@ void sqlite_datasource::bind() const } } - // now actually create the connection and start executing setup sql - dataset_ = boost::make_shared(dataset_name_); - // Execute init_statements_ for (std::vector::const_iterator iter = init_statements_.begin(); iter != init_statements_.end(); ++iter) @@ -188,7 +221,11 @@ void sqlite_datasource::bind() const std::ostringstream s; std::string query = populate_tokens(table_); s << "SELECT " << fields_ << " FROM (" << query << ") LIMIT 1"; - found_types_via_subquery = sqlite_utils::detect_types_from_subquery(s.str(),geometry_field_,desc_,dataset_); + found_types_via_subquery = sqlite_utils::detect_types_from_subquery( + s.str(), + geometry_field_, + desc_, + dataset_); } // TODO - consider removing this @@ -328,6 +365,38 @@ void sqlite_datasource::bind() const throw datasource_exception(s.str()); } } + + // finally, get geometry type by querying first feature + std::ostringstream s; + std::string g_type(""); + std::string prev_type(""); + boost::ptr_vector paths; + + s << "SELECT " << geometry_field_ << " FROM " << geometry_table_; + if (row_limit_ > 0 && row_limit_ < 5) { + s << " LIMIT " << row_limit_; + } else { + s << " LIMIT 5"; + } + boost::shared_ptr rs = dataset_->execute_query(s.str()); + while (rs->is_valid() && rs->step_next()) + { + int size; + const char* data = (const char*) rs->column_blob(0, size); + if (data) + { + mapnik::geometry_utils::from_wkb(paths, data, size, mapnik::wkbAuto); + mapnik::util::to_type_str(paths,g_type); + if (!prev_type.empty() && g_type != prev_type) + { + g_type = "collection"; + break; + } + prev_type = g_type; + } + } + desc_.set_geometry_type(g_type); + is_bound_ = true; } diff --git a/plugins/input/templates/helloworld/hello_datasource.cpp b/plugins/input/templates/helloworld/hello_datasource.cpp index de4afcbba..3adc8cad6 100644 --- a/plugins/input/templates/helloworld/hello_datasource.cpp +++ b/plugins/input/templates/helloworld/hello_datasource.cpp @@ -34,6 +34,10 @@ void hello_datasource::bind() const // see http://spatialreference.org/ref/epsg/4326/ for more details extent_.init(-180,-90,180,90); + // declare that this datasource is going to provide points + // options are point,polygon,linestring, and collection + desc_.set_geometry_type("point"); + is_bound_ = true; }