variables now available as postgis datasource tokens

This commit is contained in:
Dane Springmeyer 2014-05-13 20:47:22 -07:00
parent 2f8451b99a
commit e300a41dca
8 changed files with 103 additions and 22 deletions

View file

@ -267,15 +267,17 @@ class _Path(Path,_injector):
class _Datasource(Datasource,_injector):
def all_features(self,fields=None):
def all_features(self,fields=None,variables={}):
query = Query(self.envelope())
query.set_variables(variables);
attributes = fields or self.fields()
for fld in attributes:
query.add_property_name(fld)
return self.features(query).features
def featureset(self,fields=None):
def featureset(self,fields=None,variables={}):
query = Query(self.envelope())
query.set_variables(variables);
attributes = fields or self.fields()
for fld in attributes:
query.add_property_name(fld)

View file

@ -21,6 +21,7 @@
*****************************************************************************/
#include "boost_std_shared_shim.hpp"
#include "python_to_value.hpp"
// boost
#include <boost/python.hpp>
@ -69,6 +70,15 @@ struct names_to_list
}
};
namespace {
void set_variables(mapnik::query & q, boost::python::dict const& d)
{
mapnik::attributes vars = mapnik::dict2attr(d);
q.set_variables(vars);
}
}
void export_query()
{
using namespace boost::python;
@ -85,5 +95,6 @@ void export_query()
return_value_policy<copy_const_reference>()) )
.add_property("property_names", make_function(&query::property_names,
return_value_policy<copy_const_reference>()) )
.def("add_property_name", &query::add_property_name);
.def("add_property_name", &query::add_property_name)
.def("set_variables",&set_variables);
}

View file

@ -471,6 +471,7 @@ void feature_style_processor<Processor>::prepare_layer(layer_rendering_material
height/qh);
query q(layer_ext,res,scale_denom,extent);
q.set_variables(p.variables());
if (p.attribute_collection_policy() == COLLECT_ALL)
{

View file

@ -25,6 +25,7 @@
//mapnik
#include <mapnik/box2d.hpp>
#include <mapnik/attribute.hpp>
// boost
#include <boost/tuple/tuple.hpp>
@ -49,7 +50,8 @@ public:
scale_denominator_(scale_denominator),
filter_factor_(1.0),
unbuffered_bbox_(unbuffered_bbox),
names_()
names_(),
vars_()
{}
query(box2d<double> const& bbox,
@ -60,7 +62,8 @@ public:
scale_denominator_(scale_denominator),
filter_factor_(1.0),
unbuffered_bbox_(bbox),
names_()
names_(),
vars_()
{}
query(box2d<double> const& bbox)
@ -69,7 +72,8 @@ public:
scale_denominator_(1.0),
filter_factor_(1.0),
unbuffered_bbox_(bbox),
names_()
names_(),
vars_()
{}
query(query const& other)
@ -78,7 +82,8 @@ public:
scale_denominator_(other.scale_denominator_),
filter_factor_(other.filter_factor_),
unbuffered_bbox_(other.unbuffered_bbox_),
names_(other.names_)
names_(other.names_),
vars_(other.vars_)
{}
query& operator=(query const& other)
@ -90,6 +95,7 @@ public:
filter_factor_=other.filter_factor_;
unbuffered_bbox_=other.unbuffered_bbox_;
names_=other.names_;
vars_=other.vars_;
return *this;
}
@ -143,6 +149,16 @@ public:
return names_;
}
void set_variables(attributes const& vars)
{
vars_ = vars;
}
attributes const& variables() const
{
return vars_;
}
private:
box2d<double> bbox_;
resolution_type resolution_;
@ -150,6 +166,7 @@ private:
double filter_factor_;
box2d<double> unbuffered_bbox_;
std::set<std::string> names_;
attributes vars_;
};
}

View file

@ -54,6 +54,7 @@ if env['PLUGIN_LINKING'] == 'shared':
libraries.insert(0,'mapnik')
libraries.append(env['ICU_LIB_NAME'])
libraries.append('boost_system%s' % env['BOOST_APPEND'])
libraries.append('boost_regex%s' % env['BOOST_APPEND'])
TARGET = plugin_env.SharedLibrary('../%s' % PLUGIN_NAME,
SHLIBPREFIX='',

View file

@ -38,6 +38,7 @@
// boost
#include <boost/algorithm/string.hpp>
#include <boost/tokenizer.hpp>
#include <boost/regex.hpp>
// stl
#include <memory>
@ -85,9 +86,11 @@ postgis_datasource::postgis_datasource(parameters const& params)
extent_from_subquery_(*params.get<mapnik::boolean>("extent_from_subquery", false)),
max_async_connections_(*params_.get<mapnik::value_integer>("max_async_connection", 1)),
asynchronous_request_(false),
// TODO - use for known tokens too: "(@\\w+|!\\w+!)"
pattern_(boost::regex("(@\\w+)",boost::regex::normal | boost::regbase::icase)),
// params below are for testing purposes only and may be removed at any time
intersect_min_scale_(*params.get<mapnik::value_integer>("intersect_min_scale", 0)),
intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0))
intersect_min_scale_(*params.get<mapnik::value_integer>("intersect_min_scale", 0)),
intersect_max_scale_(*params.get<mapnik::value_integer>("intersect_max_scale", 0))
{
#ifdef MAPNIK_STATS
mapnik::progress_timer __stats__(std::clog, "postgis_datasource::init");
@ -520,22 +523,34 @@ std::string postgis_datasource::populate_tokens(std::string const& sql) const
if (boost::algorithm::icontains(sql, pixel_width_token_))
{
std::ostringstream ss;
ss << 0;
boost::algorithm::replace_all(populated_sql, pixel_width_token_, ss.str());
boost::algorithm::replace_all(populated_sql, pixel_width_token_, "0");
}
if (boost::algorithm::icontains(sql, pixel_height_token_))
{
std::ostringstream ss;
ss << 0;
boost::algorithm::replace_all(populated_sql, pixel_height_token_, ss.str());
boost::algorithm::replace_all(populated_sql, pixel_height_token_, "0");
}
std::string copy2 = populated_sql;
std::list<std::string> l;
boost::regex_split(std::back_inserter(l), copy2, pattern_);
if (!l.empty())
{
for (auto const & token: l)
{
boost::algorithm::replace_all(populated_sql, token, "null");
}
}
return populated_sql;
}
std::string postgis_datasource::populate_tokens(std::string const& sql, double scale_denom, box2d<double> const& env, double pixel_width, double pixel_height) const
std::string postgis_datasource::populate_tokens(
std::string const& sql,
double scale_denom,
box2d<double> const& env,
double pixel_width,
double pixel_height,
mapnik::attributes const& vars) const
{
std::string populated_sql = sql;
std::string box = sql_bbox(env);
@ -564,7 +579,6 @@ std::string postgis_datasource::populate_tokens(std::string const& sql, double s
if (boost::algorithm::icontains(populated_sql, bbox_token_))
{
boost::algorithm::replace_all(populated_sql, bbox_token_, box);
return populated_sql;
}
else
{
@ -582,9 +596,27 @@ std::string postgis_datasource::populate_tokens(std::string const& sql, double s
{
s << " WHERE \"" << geometryColumn_ << "\" && " << box;
}
return populated_sql + s.str();
populated_sql += s.str();
}
std::string copy2 = populated_sql;
std::list<std::string> l;
boost::regex_split(std::back_inserter(l), copy2, pattern_);
if (!l.empty())
{
for (auto const & token: l)
{
auto itr = vars.find(token.substr(1,std::string::npos));
if (itr != vars.end())
{
boost::algorithm::replace_all(populated_sql, token, itr->second.to_string());
}
else
{
boost::algorithm::replace_all(populated_sql, token, "null");
}
}
}
return populated_sql;
}
@ -780,7 +812,7 @@ featureset_ptr postgis_datasource::features_with_context(query const& q,processo
}
}
std::string table_with_bbox = populate_tokens(table_, scale_denom, box, px_gw, px_gh);
std::string table_with_bbox = populate_tokens(table_, scale_denom, box, px_gw, px_gh, q.variables());
s << " FROM " << table_with_bbox;
@ -863,7 +895,7 @@ featureset_ptr postgis_datasource::features_at_point(coord2d const& pt, double t
}
box2d<double> box(pt.x - tol, pt.y - tol, pt.x + tol, pt.y + tol);
std::string table_with_bbox = populate_tokens(table_, FMAX, box, 0, 0);
std::string table_with_bbox = populate_tokens(table_, FMAX, box, 0, 0, mapnik::attributes());
s << " FROM " << table_with_bbox;

View file

@ -33,9 +33,11 @@
#include <mapnik/feature_layer_desc.hpp>
#include <mapnik/unicode.hpp>
#include <mapnik/value_types.hpp>
#include <mapnik/attribute.hpp>
// boost
#include <boost/optional.hpp>
#include <boost/regex.hpp>
// stl
#include <memory>
@ -77,7 +79,12 @@ public:
private:
std::string sql_bbox(box2d<double> const& env) const;
std::string populate_tokens(std::string const& sql, double scale_denom, box2d<double> const& env, double pixel_width, double pixel_height) const;
std::string populate_tokens(std::string const& sql,
double scale_denom,
box2d<double> const& env,
double pixel_width,
double pixel_height,
mapnik::attributes const& vars) const;
std::string populate_tokens(std::string const& sql) const;
std::shared_ptr<IResultSet> get_resultset(std::shared_ptr<Connection> &conn, std::string const& sql, CnxPool_ptr const& pool, processor_context_ptr ctx= processor_context_ptr()) const;
static const std::string GEOMETRY_COLUMNS;
@ -112,6 +119,7 @@ private:
bool estimate_extent_;
int max_async_connections_;
bool asynchronous_request_;
boost::regex pattern_;
int intersect_min_scale_;
int intersect_max_scale_;
};

View file

@ -1011,6 +1011,15 @@ if 'postgis' in mapnik.DatasourceCache.plugin_names() \
eq_(geoms[0].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
eq_(geoms[1].to_wkt(),'Polygon((0 0,1 1,2 2,0 0))')
def test_variable_in_subquery1():
ds = mapnik.PostGIS(dbname=MAPNIK_TEST_DBNAME,table='''
(select * from test where @zoom = 30 ) as tmp''',
geometry_field='geom',
autodetect_key_field=True)
fs = ds.featureset(variables={'zoom':30})
for id in range(1,5):
eq_(fs.next().id(),id)
atexit.register(postgis_takedown)