sqlite: add auto-indexing of sqlite files if use_spatial_index=true and a spatial index is missing (which is the case for ogr2ogr -f SQLite created sqlite files)

This commit is contained in:
Dane Springmeyer 2011-08-15 20:02:57 +00:00
parent 94d3210bcd
commit 40df7f57c3
2 changed files with 71 additions and 19 deletions

View file

@ -96,6 +96,7 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind)
multiple_geometries_ = *params_.get<mapnik::boolean>("multiple_geometries",false); multiple_geometries_ = *params_.get<mapnik::boolean>("multiple_geometries",false);
use_spatial_index_ = *params_.get<mapnik::boolean>("use_spatial_index",true); use_spatial_index_ = *params_.get<mapnik::boolean>("use_spatial_index",true);
has_spatial_index_ = false;
boost::optional<std::string> ext = params_.get<std::string>("extent"); boost::optional<std::string> ext = params_.get<std::string>("extent");
if (ext) extent_initialized_ = extent_.from_string(*ext); if (ext) extent_initialized_ = extent_.from_string(*ext);
@ -335,25 +336,28 @@ void sqlite_datasource::bind() const
if (geometry_field_.empty()) { if (geometry_field_.empty()) {
throw datasource_exception("Sqlite Plugin: cannot detect geometry_field, please supply the name of the geometry_field to use."); throw datasource_exception("Sqlite Plugin: cannot detect geometry_field, please supply the name of the geometry_field to use.");
} }
if (index_table_.size() == 0) {
// Generate implicit index_table name - need to do this after
// we have discovered meta-data or else we don't know the column
// name
index_table_ = "idx_" + mapnik::unquote_sql(geometry_table_) + "_" + geometry_field_;
}
if (use_spatial_index_) if (use_spatial_index_)
{ {
if (index_table_.size() == 0) {
// Generate implicit index_table name - need to do this after
// we have discovered meta-data or else we don't know the column
// name
index_table_ = "idx_" + mapnik::unquote_sql(geometry_table_) + "_" + geometry_field_;
}
std::ostringstream s; std::ostringstream s;
s << "SELECT pkid,xmin,xmax,ymin,ymax FROM " << index_table_; s << "SELECT pkid,xmin,xmax,ymin,ymax FROM " << index_table_;
s << " LIMIT 0"; s << " LIMIT 0";
if (dataset_->execute_with_code(s.str()) != SQLITE_OK) if (dataset_->execute_with_code(s.str()) != SQLITE_OK)
{ {
use_spatial_index_ = false;
std::clog << "Sqlite Plugin: Warning, no suitable spatial index found for " std::clog << "Sqlite Plugin: Warning, no suitable spatial index found for "
<< geometry_table_ << " (checked " << s.str() << "). Rendering will work but will be slow: set 'use_spatial_index' to false to silence this warning." << std::endl; << geometry_table_ << " (checked " << s.str() << "). Rendering will work but will be slow: set 'use_spatial_index' to false to silence this warning." << std::endl;
} }
else
{
has_spatial_index_ = true;
}
} }
if (metadata_ != "" && !extent_initialized_) if (metadata_ != "" && !extent_initialized_)
@ -374,7 +378,7 @@ void sqlite_datasource::bind() const
} }
} }
if (!extent_initialized_ && use_spatial_index_) if (!extent_initialized_ && has_spatial_index_)
{ {
std::ostringstream s; std::ostringstream s;
s << "SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM " s << "SELECT MIN(xmin), MIN(ymin), MAX(xmax), MAX(ymax) FROM "
@ -405,8 +409,7 @@ void sqlite_datasource::bind() const
if (!extent_initialized_) { if (!extent_initialized_) {
std::ostringstream s; std::ostringstream s;
s << "SELECT " s << "SELECT " << geometry_field_ << "," << key_field_
<< geometry_field_
<< " FROM " << table_; << " FROM " << table_;
if (row_limit_ > 0) { if (row_limit_ > 0) {
s << " LIMIT " << row_limit_; s << " LIMIT " << row_limit_;
@ -420,6 +423,22 @@ void sqlite_datasource::bind() const
#endif #endif
boost::shared_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str())); boost::shared_ptr<sqlite_resultset> rs(dataset_->execute_query(s.str()));
// spatial index sql
std::string spatial_index_sql = "create virtual table " + index_table_
+ " using rtree(pkid, xmin, xmax, ymin, ymax)";
std::string spatial_index_insert_sql = "insert into " + index_table_
+ " values (?,?,?,?,?)" ;
sqlite3_stmt* stmt = 0;
if (use_spatial_index_) {
dataset_->execute_with_code(spatial_index_sql);
int rc = sqlite3_prepare_v2 (*(*dataset_), spatial_index_insert_sql.c_str(), -1, &stmt, 0);
if (rc != SQLITE_OK)
{
throw datasource_exception("failed");
}
}
bool first = true; bool first = true;
while (rs->is_valid() && rs->step_next()) while (rs->is_valid() && rs->step_next())
@ -427,20 +446,52 @@ void sqlite_datasource::bind() const
int size; int size;
const char* data = (const char *) rs->column_blob (0, size); const char* data = (const char *) rs->column_blob (0, size);
if (data) { if (data) {
extent_initialized_ = true;
// create a tmp feature to be able to parse geometry // create a tmp feature to be able to parse geometry
// ideally we would not have to do this. // ideally we would not have to do this.
// see: http://trac.mapnik.org/ticket/745 // see: http://trac.mapnik.org/ticket/745
mapnik::feature_ptr feature(mapnik::feature_factory::create(0)); mapnik::feature_ptr feature(mapnik::feature_factory::create(0));
mapnik::geometry_utils::from_wkb(*feature,data,size,multiple_geometries_,format_); mapnik::geometry_utils::from_wkb(*feature,data,size,multiple_geometries_,format_);
if (first) { mapnik::box2d<double> const& bbox = feature->envelope();
first = false; if (bbox.valid()) {
extent_ = feature->envelope(); extent_initialized_ = true;
} else { if (first) {
extent_.expand_to_include(feature->envelope()); first = false;
extent_ = bbox;
} else {
extent_.expand_to_include(bbox);
}
// index creation
if (use_spatial_index_) {
const int type_oid = rs->column_type(1);
if (type_oid != SQLITE_INTEGER)
throw datasource_exception("invalid type for key field");
int pkid = rs->column_integer(1);
if (sqlite3_bind_int(stmt, 1 , pkid ) != SQLITE_OK)
{
throw datasource_exception("invalid value for for key field while generating index");
}
if ((sqlite3_bind_double(stmt, 2 , bbox.minx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 3 , bbox.maxx() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 4 , bbox.miny() ) != SQLITE_OK) ||
(sqlite3_bind_double(stmt, 5 , bbox.maxy() ) != SQLITE_OK)) {
throw datasource_exception("invalid value for for extent while generating index");
}
sqlite3_step(stmt);
sqlite3_reset(stmt);
}
} }
} }
} }
int res = sqlite3_finalize(stmt);
if (res != SQLITE_OK)
{
throw datasource_exception("auto-indexing failed: set use_spatial_index=false to disable auto-indexing and avoid this error");
}
} }
if (!extent_initialized_) { if (!extent_initialized_) {
@ -508,7 +559,7 @@ featureset_ptr sqlite_datasource::features(query const& q) const
std::string query (table_); std::string query (table_);
if (use_spatial_index_) if (has_spatial_index_)
{ {
std::ostringstream spatial_sql; std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16); spatial_sql << std::setprecision(16);
@ -572,7 +623,7 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const
std::string query (table_); std::string query (table_);
if (use_spatial_index_) if (has_spatial_index_)
{ {
std::ostringstream spatial_sql; std::ostringstream spatial_sql;
spatial_sql << std::setprecision(16); spatial_sql << std::setprecision(16);

View file

@ -68,6 +68,7 @@ class sqlite_datasource : public mapnik::datasource
mapnik::wkbFormat format_; mapnik::wkbFormat format_;
bool multiple_geometries_; bool multiple_geometries_;
mutable bool use_spatial_index_; mutable bool use_spatial_index_;
mutable bool has_spatial_index_;
std::vector<std::string> init_statements_; std::vector<std::string> init_statements_;
// Fill init_statements with any statements // Fill init_statements with any statements