diff --git a/CHANGELOG b/CHANGELOG index ba2380754..49aa4e399 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -14,6 +14,8 @@ For a complete change history, see the SVN log. Mapnik 2.1.0 ------------ +- SQLite - Added support for !intersects! token in sql subselects (#809) allow custom positioning of rtree spatial filter. + - New CSV plugin - reads tabular files - autodetecting geo columns, newlines, and delimiters. Uses in-memory featureset for fast rendering and is not designed for large files (#902) - Fixed bug in shield line placement when dx/dy are used to shift the label relative to the placement point (Matt Amos) (#908) diff --git a/plugins/input/sqlite/sqlite_datasource.cpp b/plugins/input/sqlite/sqlite_datasource.cpp index 256600cc7..14b6a068c 100644 --- a/plugins/input/sqlite/sqlite_datasource.cpp +++ b/plugins/input/sqlite/sqlite_datasource.cpp @@ -62,6 +62,7 @@ sqlite_datasource::sqlite_datasource(parameters const& params, bool bind) key_field_(*params_.get("key_field", "")), row_offset_(*params_.get("row_offset", 0)), row_limit_(*params_.get("row_limit", 0)), + intersects_token_("!intersects!"), desc_(*params_.get("type"), *params_.get("encoding", "utf-8")), format_(mapnik::wkbAuto) { @@ -185,7 +186,8 @@ void sqlite_datasource::bind() const if (using_subquery_) { std::ostringstream s; - s << "SELECT " << fields_ << " FROM (" << table_ << ") LIMIT 1"; + 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_); } @@ -306,6 +308,7 @@ void sqlite_datasource::bind() const { // TODO - clean this up - reducing arguments + std::string query = populate_tokens(table_); if (!sqlite_utils::detect_extent(dataset_, has_spatial_index_, extent_, @@ -314,7 +317,7 @@ void sqlite_datasource::bind() const geometry_field_, geometry_table_, key_field_, - table_)) + query)) { std::ostringstream s; s << "Sqlite Plugin: extent could not be determined for table '" @@ -328,6 +331,17 @@ void sqlite_datasource::bind() const is_bound_ = true; } +std::string sqlite_datasource::populate_tokens(const std::string& sql) const +{ + std::string populated_sql = sql; + if (boost::algorithm::ifind_first(populated_sql, intersects_token_)) + { + // replace with dummy comparison that is true + boost::algorithm::ireplace_first(populated_sql, intersects_token_, "1=1"); + } + return populated_sql; +} + sqlite_datasource::~sqlite_datasource() { } @@ -465,12 +479,21 @@ featureset_ptr sqlite_datasource::features(query const& q) const s << " FROM "; - std::string query (table_); + std::string query(table_); if (! key_field_.empty() && has_spatial_index_) { // TODO - debug warn if fails - sqlite_utils::apply_spatial_filter(query,e,table_,key_field_,index_table_,geometry_table_); + sqlite_utils::apply_spatial_filter(query, + e, + table_, + key_field_, + index_table_, + geometry_table_, + intersects_token_); } + else + { + query = populate_tokens(table_); } s << query ; @@ -534,7 +557,17 @@ featureset_ptr sqlite_datasource::features_at_point(coord2d const& pt) const if (! key_field_.empty() && has_spatial_index_) { // TODO - debug warn if fails - sqlite_utils::apply_spatial_filter(query,e,table_,key_field_,index_table_,geometry_table_); + sqlite_utils::apply_spatial_filter(query, + e, + table_, + key_field_, + index_table_, + geometry_table_, + intersects_token_); + } + else + { + query = populate_tokens(table_); } s << query ; diff --git a/plugins/input/sqlite/sqlite_datasource.hpp b/plugins/input/sqlite/sqlite_datasource.hpp index 85c67fdba..f705f762d 100644 --- a/plugins/input/sqlite/sqlite_datasource.hpp +++ b/plugins/input/sqlite/sqlite_datasource.hpp @@ -70,6 +70,8 @@ private: mutable std::string key_field_; mutable int row_offset_; mutable int row_limit_; + // TODO - also add to postgis.input + const std::string intersects_token_; mutable mapnik::layer_descriptor desc_; mutable mapnik::wkbFormat format_; mutable bool use_spatial_index_; @@ -80,6 +82,7 @@ private: // Fill init_statements with any statements // needed to attach auxillary databases void parse_attachdb(std::string const& attachdb) const; + std::string populate_tokens(const std::string& sql) const; }; #endif // MAPNIK_SQLITE_DATASOURCE_HPP diff --git a/plugins/input/sqlite/sqlite_utils.hpp b/plugins/input/sqlite/sqlite_utils.hpp index 2558c0d27..6021d26ca 100644 --- a/plugins/input/sqlite/sqlite_utils.hpp +++ b/plugins/input/sqlite/sqlite_utils.hpp @@ -113,23 +113,32 @@ public: std::string const& table, std::string const& key_field, std::string const& index_table, - std::string const& geometry_table) + std::string const& geometry_table, + std::string const& intersects_token) { std::ostringstream spatial_sql; spatial_sql << std::setprecision(16); - spatial_sql << " WHERE " << key_field << " IN (SELECT pkid FROM " << index_table; + spatial_sql << key_field << " IN (SELECT pkid FROM " << index_table; spatial_sql << " WHERE xmax>=" << e.minx() << " AND xmin<=" << e.maxx() ; spatial_sql << " AND ymax>=" << e.miny() << " AND ymin<=" << e.maxy() << ")"; - // substitute first WHERE found if not using JOIN (because we can't know the WHERE is on the right table) - if (boost::algorithm::ifind_first(query, "WHERE") && !boost::algorithm::ifind_first(query, "JOIN")) + if (boost::algorithm::ifind_first(query, intersects_token)) { - boost::algorithm::ireplace_first(query, "WHERE", spatial_sql.str() + " AND "); + boost::algorithm::ireplace_all(query, intersects_token, spatial_sql.str()); + return true; + } + // substitute first WHERE found if not using JOIN + // (because we can't know the WHERE is on the right table) + else if (boost::algorithm::ifind_first(query, "WHERE") + && !boost::algorithm::ifind_first(query, "JOIN")) + { + std::string replace(" WHERE " + spatial_sql.str() + " AND "); + boost::algorithm::ireplace_first(query, "WHERE", replace); return true; } // fallback to appending spatial filter at end of query else if (boost::algorithm::ifind_first(query, geometry_table)) { - boost::algorithm::ireplace_first(query, table, table + " " + spatial_sql.str()); + query = table + " WHERE " + spatial_sql.str(); return true; } return false;