Merge pull request #916 from kkaefer/grouped-rendering

Grouped rendering
This commit is contained in:
Dane Springmeyer 2011-12-11 22:36:22 -08:00
commit e5ab14a298
10 changed files with 367 additions and 204 deletions

View file

@ -23,8 +23,16 @@
#ifndef MAPNIK_FEATURE_STYLE_PROCESSOR_HPP
#define MAPNIK_FEATURE_STYLE_PROCESSOR_HPP
// mapnik
#include <mapnik/map.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/memory_datasource.hpp>
// stl
#include <set>
#include <string>
#include <vector>
namespace mapnik
{
@ -62,11 +70,23 @@ private:
/*!
* @return render a layer given a projection and scale.
*/
void apply_to_layer(layer const& lay, Processor & p,
void apply_to_layer(layer const& lay,
Processor & p,
projection const& proj0,
double scale_denom,
std::set<std::string>& names);
/*!
* @return renders a featureset with the given styles.
*/
void render_style(layer const& lay,
Processor & p,
feature_type_style* style,
std::string const& style_name,
featureset_ptr features,
proj_transform const& prj_trans,
double scale_denom);
Map const& m_;
double scale_factor_;
};

View file

@ -43,11 +43,19 @@ enum filter_mode_enum {
DEFINE_ENUM( filter_mode_e, filter_mode_enum );
typedef std::vector<rule> rules;
typedef std::vector<rule*> rule_ptrs;
class MAPNIK_DECL feature_type_style
{
private:
rules rules_;
filter_mode_e filter_mode_;
// The rule_ptrs vectors are only valid for the scale_denom_validity_.
double scale_denom_validity_;
rule_ptrs if_rules_;
rule_ptrs else_rules_;
rule_ptrs also_rules_;
public:
feature_type_style();
@ -58,6 +66,9 @@ public:
void add_rule(rule const& rule);
rules const& get_rules() const;
rule_ptrs const& get_if_rules(double scale_denom);
rule_ptrs const& get_else_rules(double scale_denom);
rule_ptrs const& get_also_rules(double scale_denom);
rules &get_rules_nonconst();
@ -66,6 +77,9 @@ public:
filter_mode_e get_filter_mode() const;
~feature_type_style() {}
private:
void update_rule_cache(double scale_denom);
};
}

View file

@ -180,6 +180,16 @@ public:
*/
bool cache_features() const;
/*!
* @param group_by Set the field rendering of this layer is grouped by.
*/
void set_group_by(std::string column);
/*!
* @return The field rendering of this layer is grouped by.
*/
std::string group_by() const;
/*!
* @brief Attach a datasource for this layer.
*
@ -212,6 +222,7 @@ private:
bool queryable_;
bool clear_label_cache_;
bool cache_features_;
std::string group_by_;
std::vector<std::string> styles_;
datasource_ptr ds_;
};

View file

@ -45,6 +45,7 @@ public:
box2d<double> envelope() const;
layer_descriptor get_descriptor() const;
size_t size() const;
void clear();
private:
std::vector<feature_ptr> features_;
mapnik::layer_descriptor desc_;

View file

@ -25,13 +25,10 @@
#include <mapnik/box2d.hpp>
#include <mapnik/datasource.hpp>
#include <mapnik/layer.hpp>
#include <mapnik/map.hpp>
#include <mapnik/attribute_collector.hpp>
#include <mapnik/expression_evaluator.hpp>
#include <mapnik/utils.hpp>
#include <mapnik/projection.hpp>
#include <mapnik/scale_denominator.hpp>
#include <mapnik/memory_datasource.hpp>
#include <mapnik/agg_renderer.hpp>
#include <mapnik/grid/grid_renderer.hpp>
@ -308,65 +305,23 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
}
}
// Don't even try to do more work if there are no active styles.
if (active_styles.size() > 0)
{
// push all property names
BOOST_FOREACH(std::string const& name, names)
{
q.add_property_name(name);
}
memory_datasource cache;
bool cache_features = lay.cache_features() && num_styles>1?true:false;
bool first = true;
#if defined(RENDERING_STATS)
int style_index = 0;
if (!active_styles.size() > 0) {
layer_timer.discard();
}
#endif
// Update filter_factor for all enabled raster layers.
BOOST_FOREACH (feature_type_style * style, active_styles)
{
#if defined(RENDERING_STATS)
std::string s_name = style_names[style_index];
std::ostringstream s1;
s1 << "rendering style #" << style_index+1
<< " for layer: '" << lay.name() << "' and style '" << s_name << "'";
mapnik::progress_timer style_timer(std::clog, s1.str());
if (!num_styles>1)
style_timer.discard();
style_index++;
#endif
std::vector<rule*> if_rules;
std::vector<rule*> else_rules;
std::vector<rule*> also_rules;
std::vector<rule> const& rules=style->get_rules();
#if defined(RENDERING_STATS)
int feature_count = 0;
int feature_processed_count = 0;
#endif
BOOST_FOREACH(rule const& r, rules)
BOOST_FOREACH(rule const& r, style->get_rules())
{
if (r.active(scale_denom))
{
if (r.has_else_filter())
{
else_rules.push_back(const_cast<rule*>(&r));
}
else if (r.has_also_filter())
{
also_rules.push_back(const_cast<rule*>(&r));
}
else
{
if_rules.push_back(const_cast<rule*>(&r));
}
if ( (ds->type() == datasource::Raster) &&
(ds->params().get<double>("filter_factor",0.0) == 0.0) )
if (r.active(scale_denom) &&
ds->type() == datasource::Raster &&
ds->params().get<double>("filter_factor",0.0) == 0.0)
{
rule::symbolizers const& symbols = r.get_symbolizers();
rule::symbolizers::const_iterator symIter = symbols.begin();
@ -382,39 +337,126 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
}
}
// process features
featureset_ptr fs;
if (first)
// Also query the group by attribute
std::string group_by = lay.group_by();
if (group_by != "")
{
if (cache_features)
first = false;
fs = ds->features(q);
q.add_property_name(group_by);
}
bool cache_features = lay.cache_features() && active_styles.size() > 1;
// Render incrementally when the column that we group by
// changes value.
if (group_by != "")
{
featureset_ptr features = ds->features(q);
if (features) {
// Cache all features into the memory_datasource before rendering.
memory_datasource cache;
feature_ptr feature, prev;
while ((feature = features->next()))
{
if (prev && prev->props()[group_by] != feature->props()[group_by])
{
// We're at a value boundary, so render what we have
// up to this point.
int i = 0;
BOOST_FOREACH (feature_type_style * style, active_styles)
{
render_style(lay, p, style, style_names[i++],
cache.features(q), prj_trans, scale_denom);
}
cache.clear();
}
cache.push(feature);
prev = feature;
}
int i = 0;
BOOST_FOREACH (feature_type_style * style, active_styles)
{
render_style(lay, p, style, style_names[i++],
cache.features(q), prj_trans, scale_denom);
}
}
}
else if (cache_features)
{
featureset_ptr features = ds->features(q);
if (features) {
// Cache all features into the memory_datasource before rendering.
memory_datasource cache;
feature_ptr feature;
while ((feature = features->next()))
{
cache.push(feature);
}
int i = 0;
BOOST_FOREACH (feature_type_style * style, active_styles)
{
render_style(lay, p, style, style_names[i++],
cache.features(q), prj_trans, scale_denom);
}
}
}
// We only have a single style and no grouping.
else
{
fs = cache.features(q);
int i = 0;
BOOST_FOREACH (feature_type_style * style, active_styles)
{
featureset_ptr features = ds->features(q);
if (features) {
render_style(lay, p, style, style_names[i++],
features, prj_trans, scale_denom);
}
}
}
}
if (fs)
{
feature_ptr feature;
while ((feature = fs->next()))
{
#if defined(RENDERING_STATS)
layer_timer.stop();
#endif
p.end_layer_processing(lay);
}
template <typename Processor>
void feature_style_processor<Processor>::render_style(
layer const& lay,
Processor & p,
feature_type_style* style,
std::string const& style_name,
featureset_ptr features,
proj_transform const& prj_trans,
double scale_denom)
{
#if defined(RENDERING_STATS)
std::ostringstream s1;
s1 << "rendering style for layer: '" << lay.name()
<< "' and style '" << style_name << "'";
mapnik::progress_timer style_timer(std::clog, s1.str());
int feature_processed_count = 0;
int feature_count = 0;
#endif
feature_ptr feature;
while ((feature = features->next()))
{
#if defined(RENDERING_STATS)
feature_count++;
bool feat_processed = false;
#endif
bool do_else=true;
bool do_also=false;
bool do_else = true;
bool do_also = false;
if (cache_features)
{
cache.push(feature);
}
BOOST_FOREACH(rule * r, if_rules )
BOOST_FOREACH(rule * r, style->get_if_rules(scale_denom) )
{
expression_ptr const& expr=r->get_filter();
value_type result = boost::apply_visitor(evaluate<Feature,value_type>(*feature),*expr);
@ -451,7 +493,7 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
}
if (do_else)
{
BOOST_FOREACH( rule * r, else_rules )
BOOST_FOREACH( rule * r, style->get_else_rules(scale_denom) )
{
#if defined(RENDERING_STATS)
feat_processed = true;
@ -475,7 +517,7 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
}
if (do_also)
{
BOOST_FOREACH( rule * r, also_rules )
BOOST_FOREACH( rule * r, style->get_also_rules(scale_denom) )
{
#if defined(RENDERING_STATS)
feat_processed = true;
@ -519,26 +561,11 @@ void feature_style_processor<Processor>::apply_to_layer(layer const& lay, Proces
s << "" << std::setw(15) << "- no features returned from query for layer '" << lay.name() << "' and style '" << s_name << "'\n";
}
std::clog << s.str();
#endif
}
#if defined(RENDERING_STATS)
else {
style_timer.discard();
layer_timer.discard();
}
#endif
cache_features = false;
}
#if defined(RENDERING_STATS)
layer_timer.stop();
#endif
p.end_layer_processing(lay);
}
#if defined(HAVE_CAIRO)
template class feature_style_processor<cairo_renderer<Cairo::Context> >;
template class feature_style_processor<cairo_renderer<Cairo::Surface> >;

View file

@ -35,10 +35,12 @@ IMPLEMENT_ENUM( filter_mode_e, filter_mode_strings )
feature_type_style::feature_type_style()
: filter_mode_(FILTER_ALL) {}
: filter_mode_(FILTER_ALL),
scale_denom_validity_(-1) {}
feature_type_style::feature_type_style(feature_type_style const& rhs, bool deep_copy)
: filter_mode_(rhs.filter_mode_)
: filter_mode_(rhs.filter_mode_),
scale_denom_validity_(-1)
{
if (!deep_copy) {
rules_ = rhs.rules_;
@ -55,12 +57,14 @@ feature_type_style& feature_type_style::operator=(feature_type_style const& rhs)
{
if (this == &rhs) return *this;
rules_=rhs.rules_;
scale_denom_validity_ = -1;
return *this;
}
void feature_type_style::add_rule(rule const& rule)
{
rules_.push_back(rule);
scale_denom_validity_ = -1;
}
rules const& feature_type_style::get_rules() const
@ -83,4 +87,60 @@ filter_mode_e feature_type_style::get_filter_mode() const
return filter_mode_;
}
void feature_type_style::update_rule_cache(double scale_denom)
{
if_rules_.clear();
else_rules_.clear();
also_rules_.clear();
BOOST_FOREACH(rule const& r, rules_)
{
if (r.active(scale_denom))
{
if (r.has_else_filter())
{
else_rules_.push_back(const_cast<rule*>(&r));
}
else if (r.has_also_filter())
{
also_rules_.push_back(const_cast<rule*>(&r));
}
else
{
if_rules_.push_back(const_cast<rule*>(&r));
}
}
}
scale_denom_validity_ = scale_denom;
}
rule_ptrs const& feature_type_style::get_if_rules(double scale_denom)
{
if (scale_denom_validity_ != scale_denom)
{
update_rule_cache(scale_denom);
}
return if_rules_;
}
rule_ptrs const& feature_type_style::get_else_rules(double scale_denom)
{
if (scale_denom_validity_ != scale_denom)
{
update_rule_cache(scale_denom);
}
return else_rules_;
}
rule_ptrs const& feature_type_style::get_also_rules(double scale_denom)
{
if (scale_denom_validity_ != scale_denom)
{
update_rule_cache(scale_denom);
}
return also_rules_;
}
}

View file

@ -46,6 +46,7 @@ layer::layer(std::string const& name, std::string const& srs)
queryable_(false),
clear_label_cache_(false),
cache_features_(false),
group_by_(""),
ds_() {}
layer::layer(const layer& rhs)
@ -59,6 +60,7 @@ layer::layer(const layer& rhs)
queryable_(rhs.queryable_),
clear_label_cache_(rhs.clear_label_cache_),
cache_features_(rhs.cache_features_),
group_by_(rhs.group_by_),
styles_(rhs.styles_),
ds_(rhs.ds_) {}
@ -86,6 +88,7 @@ void layer::swap(const layer& rhs)
queryable_=rhs.queryable_;
clear_label_cache_ = rhs.clear_label_cache_;
cache_features_ = rhs.cache_features_;
group_by_ = rhs.group_by_;
styles_=rhs.styles_;
ds_=rhs.ds_;
}
@ -228,4 +231,14 @@ bool layer::cache_features() const
return cache_features_;
}
void layer::set_group_by(std::string column)
{
group_by_ = column;
}
std::string layer::group_by() const
{
return group_by_;
}
}

View file

@ -583,7 +583,8 @@ void map_parser::parse_layer( Map & map, ptree const & lay )
<< "maxzoom,"
<< "queryable,"
<< "clear-label-cache,"
<< "cache-features";
<< "cache-features,"
<< "group-by";
ensure_attrs(lay, "Layer", s.str());
try
{
@ -644,6 +645,12 @@ void map_parser::parse_layer( Map & map, ptree const & lay )
lyr.set_cache_features( * cache_features );
}
optional<std::string> group_by =
get_opt_attr<std::string>(lay, "group-by");
if (group_by)
{
lyr.set_group_by( * group_by );
}
ptree::const_iterator itr2 = lay.begin();
ptree::const_iterator end2 = lay.end();

View file

@ -107,6 +107,11 @@ size_t memory_datasource::size() const
return features_.size();
}
void memory_datasource::clear()
{
features_.clear();
}
// point_datasource
void point_datasource::add_point(double x, double y, const char* key, const char* value)

View file

@ -865,6 +865,11 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau
set_attr/*<bool>*/( layer_node, "cache-features", layer.cache_features() );
}
if ( layer.group_by() != "" || explicit_defaults )
{
set_attr( layer_node, "group-by", layer.group_by() );
}
std::vector<std::string> const& style_names = layer.styles();
for (unsigned i = 0; i < style_names.size(); ++i)
{