ensure all plugins report best guess at top level geometry_type using new descriptor attribute

This commit is contained in:
Dane Springmeyer 2012-01-11 20:03:23 -08:00
parent 967652efb6
commit 975afebd87
10 changed files with 301 additions and 16 deletions

View file

@ -13,6 +13,7 @@
#include <mapnik/geometry.hpp> #include <mapnik/geometry.hpp>
#include <mapnik/memory_featureset.hpp> #include <mapnik/memory_featureset.hpp>
#include <mapnik/wkt/wkt_factory.hpp> #include <mapnik/wkt/wkt_factory.hpp>
#include <mapnik/util/geometry_to_type_str.hpp>
#include <mapnik/ptree_helpers.hpp> // mapnik::boolean #include <mapnik/ptree_helpers.hpp> // mapnik::boolean
// stl // 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"; 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<mapnik::geometry_type> 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() std::string csv_datasource::name()

View file

@ -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_) if (! extent_initialized_)
{ {
throw datasource_exception("GEOS Plugin: cannot determine extent for <wkt> geometry"); throw datasource_exception("GEOS Plugin: cannot determine extent for <wkt> geometry");

View file

@ -119,6 +119,8 @@ void kismet_datasource::bind() const
{ {
if (is_bound_) return; if (is_bound_) return;
desc_.set_geometry_type("point");
is_bound_ = true; is_bound_ = true;
} }

View file

@ -62,7 +62,7 @@ private:
unsigned int port_; unsigned int port_;
int type_; int type_;
std::string srs_; std::string srs_;
mapnik::layer_descriptor desc_; mutable mapnik::layer_descriptor desc_;
boost::shared_ptr<boost::thread> kismet_thread; boost::shared_ptr<boost::thread> kismet_thread;
}; };

View file

@ -28,6 +28,7 @@
#include "ogr_datasource.hpp" #include "ogr_datasource.hpp"
#include "ogr_featureset.hpp" #include "ogr_featureset.hpp"
#include "ogr_index_featureset.hpp" #include "ogr_index_featureset.hpp"
#include "ogr_feature_ptr.hpp"
// mapnik // mapnik
#include <mapnik/ptree_helpers.hpp> #include <mapnik/ptree_helpers.hpp>
@ -265,6 +266,70 @@ void ogr_datasource::bind() const
} }
#endif #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 // deal with attributes descriptions
OGRFeatureDefn* def = layer->GetLayerDefn(); OGRFeatureDefn* def = layer->GetLayerDefn();
if (def != 0) if (def != 0)

View file

@ -123,6 +123,9 @@ void osm_datasource::bind() const
extent_ = box2d<double>(b.w, b.s, b.e, b.n); extent_ = box2d<double>(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; is_bound_ = true;
} }

View file

@ -28,6 +28,8 @@
#include <mapnik/global.hpp> #include <mapnik/global.hpp>
#include <mapnik/ptree_helpers.hpp> #include <mapnik/ptree_helpers.hpp>
#include <mapnik/sql_utils.hpp> #include <mapnik/sql_utils.hpp>
#include <mapnik/util/geometry_to_type_str.hpp>
#include <mapnik/wkb.hpp>
// boost // boost
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -106,6 +108,8 @@ void postgis_datasource::bind() const
ConnectionManager *mgr=ConnectionManager::instance(); ConnectionManager *mgr=ConnectionManager::instance();
mgr->registerPool(creator_, *initial_size, *max_size); mgr->registerPool(creator_, *initial_size, *max_size);
std::string g_type;
shared_ptr<Pool<Connection,ConnectionCreator> > pool=mgr->getPool(creator_.id()); shared_ptr<Pool<Connection,ConnectionCreator> > pool=mgr->getPool(creator_.id());
if (pool) if (pool)
{ {
@ -113,8 +117,6 @@ void postgis_datasource::bind() const
if (conn && conn->isOK()) if (conn && conn->isOK())
{ {
is_bound_ = true;
PoolGuard<shared_ptr<Connection>, PoolGuard<shared_ptr<Connection>,
shared_ptr<Pool<Connection,ConnectionCreator> > > guard(conn,pool); shared_ptr<Pool<Connection,ConnectionCreator> > > guard(conn,pool);
@ -145,7 +147,7 @@ void postgis_datasource::bind() const
if (!geometryColumn_.length() > 0 || srid_ == 0) if (!geometryColumn_.length() > 0 || srid_ == 0)
{ {
std::ostringstream s; 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_) <<"'"; s << GEOMETRY_COLUMNS <<" WHERE f_table_name='" << mapnik::sql_utils::unquote_double(geometry_table_) <<"'";
if (schema_.length() > 0) if (schema_.length() > 0)
@ -161,7 +163,7 @@ void postgis_datasource::bind() const
} }
*/ */
shared_ptr<ResultSet> rs=conn->executeQuery(s.str()); shared_ptr<ResultSet> rs = conn->executeQuery(s.str());
if (rs->next()) if (rs->next())
{ {
geometryColumn_ = rs->getValue("f_geometry_column"); 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; 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(); rs->close();
@ -196,7 +217,7 @@ void postgis_datasource::bind() const
} }
*/ */
shared_ptr<ResultSet> rs=conn->executeQuery(s.str()); shared_ptr<ResultSet> rs = conn->executeQuery(s.str());
if (rs->next()) if (rs->next())
{ {
try try
@ -238,7 +259,7 @@ void postgis_datasource::bind() const
*/ */
shared_ptr<ResultSet> rs=conn->executeQuery(s.str()); shared_ptr<ResultSet> rs = conn->executeQuery(s.str());
int count = rs->getNumFields(); int count = rs->getNumFields();
bool found_key_field = false; bool found_key_field = false;
for (int i=0;i<count;++i) for (int i=0;i<count;++i)
@ -326,7 +347,55 @@ void postgis_datasource::bind() const
} }
} }
} }
rs->close(); rs->close();
// 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<ResultSet> 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;
} }
} }
} }

View file

@ -185,6 +185,35 @@ void shape_datasource::init(shape_io& shape) const
int shape_type = shape.shp().read_ndr_integer(); int shape_type = shape.shp().read_ndr_integer();
if (shape_type == shape_io::shape_multipatch) if (shape_type == shape_io::shape_multipatch)
throw datasource_exception("Shape Plugin: shapefile multipatch type is not supported"); 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_); shape.shp().read_envelope(extent_);

View file

@ -28,6 +28,8 @@
// mapnik // mapnik
#include <mapnik/ptree_helpers.hpp> #include <mapnik/ptree_helpers.hpp>
#include <mapnik/sql_utils.hpp> #include <mapnik/sql_utils.hpp>
#include <mapnik/util/geometry_to_type_str.hpp>
#include <mapnik/wkb.hpp>
// boost // boost
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
@ -69,16 +71,12 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind)
/* TODO /* TODO
- throw if no primary key but spatial index is present? - throw if no primary key but spatial index is present?
- remove auto-indexing - remove auto-indexing
- if spatialite - leverage more of the metadata for geometry type detection
*/ */
boost::optional<std::string> file = params_.get<std::string>("file"); boost::optional<std::string> file = params_.get<std::string>("file");
if (! file) throw datasource_exception("Sqlite Plugin: missing <file> parameter"); if (! file) throw datasource_exception("Sqlite Plugin: missing <file> parameter");
if (table_.empty())
{
throw mapnik::datasource_exception("Sqlite Plugin: missing <table> parameter");
}
if (bind) if (bind)
{ {
this->bind(); this->bind();
@ -147,6 +145,44 @@ void sqlite_datasource::bind() const
init_statements_.push_back(*initdb); init_statements_.push_back(*initdb);
} }
// now actually create the connection and start executing setup sql
dataset_ = boost::make_shared<sqlite_connection>(dataset_name_);
boost::optional<unsigned> table_by_index = params_.get<unsigned>("table_by_index");
int passed_parameters = 0;
passed_parameters += params_.get<std::string>("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<std::string> 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 <table> parameter");
}
if (geometry_table_.empty()) if (geometry_table_.empty())
{ {
geometry_table_ = mapnik::sql_utils::table_from_sql(table_); 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<sqlite_connection>(dataset_name_);
// Execute init_statements_ // Execute init_statements_
for (std::vector<std::string>::const_iterator iter = init_statements_.begin(); for (std::vector<std::string>::const_iterator iter = init_statements_.begin();
iter != init_statements_.end(); ++iter) iter != init_statements_.end(); ++iter)
@ -188,7 +221,11 @@ void sqlite_datasource::bind() const
std::ostringstream s; std::ostringstream s;
std::string query = populate_tokens(table_); std::string query = populate_tokens(table_);
s << "SELECT " << fields_ << " FROM (" << query << ") LIMIT 1"; 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 // TODO - consider removing this
@ -328,6 +365,38 @@ void sqlite_datasource::bind() const
throw datasource_exception(s.str()); 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<mapnik::geometry_type> 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<sqlite_resultset> 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; is_bound_ = true;
} }

View file

@ -34,6 +34,10 @@ void hello_datasource::bind() const
// see http://spatialreference.org/ref/epsg/4326/ for more details // see http://spatialreference.org/ref/epsg/4326/ for more details
extent_.init(-180,-90,180,90); 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; is_bound_ = true;
} }