From 763ece8db6c21b703bfd63b323ef959c37d847d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Thu, 13 Oct 2011 01:30:18 +0200 Subject: [PATCH 1/2] group-by attribute for Layer --- include/mapnik/feature_style_processor.hpp | 22 +- include/mapnik/feature_type_style.hpp | 27 +- include/mapnik/layer.hpp | 11 + include/mapnik/memory_datasource.hpp | 1 + src/feature_style_processor.cpp | 413 +++++++++++---------- src/feature_type_style.cpp | 64 +++- src/layer.cpp | 13 + src/load_map.cpp | 9 +- src/memory_datasource.cpp | 5 + src/save_map.cpp | 5 + 10 files changed, 366 insertions(+), 204 deletions(-) diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index 6c944d979..185b7075f 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -25,8 +25,16 @@ #ifndef FEATURE_STYLE_PROCESSOR_HPP #define FEATURE_STYLE_PROCESSOR_HPP +// mapnik +#include +#include +#include + + +// stl #include #include +#include namespace mapnik { @@ -64,11 +72,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& 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_; }; diff --git a/include/mapnik/feature_type_style.hpp b/include/mapnik/feature_type_style.hpp index d4a428778..9a73346b3 100644 --- a/include/mapnik/feature_type_style.hpp +++ b/include/mapnik/feature_type_style.hpp @@ -1,5 +1,5 @@ /***************************************************************************** - * + * * This file is part of Mapnik (c++ mapping toolkit) * * Copyright (C) 2006 Artem Pavlenko @@ -24,7 +24,7 @@ #ifndef FEATURE_TYPE_STYLE_HPP #define FEATURE_TYPE_STYLE_HPP -// mapnik +// mapnik #include #include #include @@ -43,29 +43,42 @@ enum filter_mode_enum { DEFINE_ENUM( filter_mode_e, filter_mode_enum ); typedef std::vector rules; +typedef std::vector rule_ptrs; class 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(); feature_type_style(feature_type_style const& rhs); - + feature_type_style& operator=(feature_type_style const& rhs); - + 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(); - + void set_filter_mode(filter_mode_e mode); filter_mode_e get_filter_mode() const; - + ~feature_type_style() {} + +private: + void update_rule_cache(double scale_denom); }; } diff --git a/include/mapnik/layer.hpp b/include/mapnik/layer.hpp index d4d4c7ea4..7019f50d6 100644 --- a/include/mapnik/layer.hpp +++ b/include/mapnik/layer.hpp @@ -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 styles_; datasource_ptr ds_; }; diff --git a/include/mapnik/memory_datasource.hpp b/include/mapnik/memory_datasource.hpp index f344ff1d7..c7fb14a84 100644 --- a/include/mapnik/memory_datasource.hpp +++ b/include/mapnik/memory_datasource.hpp @@ -44,6 +44,7 @@ public: box2d envelope() const; layer_descriptor get_descriptor() const; size_t size() const; + void clear(); private: std::vector features_; mapnik::layer_descriptor desc_; diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index 01fba989b..53ce6ce6e 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -25,13 +25,10 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include @@ -308,65 +305,23 @@ void feature_style_processor::apply_to_layer(layer const& lay, Proces } } - // push all property names - BOOST_FOREACH(std::string const& name, names) + // Don't even try to do more work if there are no active styles. + if (active_styles.size() > 0) { - 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 - 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 if_rules; - std::vector else_rules; - std::vector also_rules; - - std::vector 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) + // push all property names + BOOST_FOREACH(std::string const& name, names) { - if (r.active(scale_denom)) - { - if (r.has_else_filter()) - { - else_rules.push_back(const_cast(&r)); - } - else if (r.has_also_filter()) - { - also_rules.push_back(const_cast(&r)); - } - else - { - if_rules.push_back(const_cast(&r)); - } + q.add_property_name(name); + } - if ( (ds->type() == datasource::Raster) && - (ds->params().get("filter_factor",0.0) == 0.0) ) + // Update filter_factor for all enabled raster layers. + BOOST_FOREACH (feature_type_style * style, active_styles) + { + BOOST_FOREACH(rule const& r, style->get_rules()) + { + if (r.active(scale_denom) && + ds->type() == datasource::Raster && + ds->params().get("filter_factor",0.0) == 0.0) { rule::symbolizers const& symbols = r.get_symbolizers(); rule::symbolizers::const_iterator symIter = symbols.begin(); @@ -382,163 +337,235 @@ void feature_style_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); - } - else - { - fs = cache.features(q); + q.add_property_name(group_by); } - if (fs) + bool cache_features = lay.cache_features() && active_styles.size() > 1; + + // Render incrementally when the column that we group by + // changes value. + if (group_by != "") { - feature_ptr feature; - while ((feature = fs->next())) - { + featureset_ptr features = ds->features(q); + if (features) { + // Cache all features into the memory_datasource before rendering. + memory_datasource cache; + feature_ptr feature, prev; - #if defined(RENDERING_STATS) - feature_count++; - bool feat_processed = false; - #endif + 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; + } - bool do_else=true; - bool do_also=false; - - if (cache_features) + 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); } - BOOST_FOREACH(rule * r, if_rules ) + int i = 0; + BOOST_FOREACH (feature_type_style * style, active_styles) { - expression_ptr const& expr=r->get_filter(); - value_type result = boost::apply_visitor(evaluate(*feature),*expr); - if (result.to_bool()) - { - #if defined(RENDERING_STATS) - feat_processed = true; - #endif - - p.painted(true); - - do_else=false; - do_also=true; - rule::symbolizers const& symbols = r->get_symbolizers(); - - // if the underlying renderer is not able to process the complete set of symbolizers, - // process one by one. -#if defined(SVG_RENDERER) - if(!p.process(symbols,*feature,prj_trans)) -#endif - { - - BOOST_FOREACH (symbolizer const& sym, symbols) - { - boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); - } - } - if (style->get_filter_mode() == FILTER_FIRST) - { - // Stop iterating over rules and proceed with next feature. - break; - } - } + render_style(lay, p, style, style_names[i++], + cache.features(q), prj_trans, scale_denom); } - if (do_else) - { - BOOST_FOREACH( rule * r, else_rules ) - { - #if defined(RENDERING_STATS) - feat_processed = true; - #endif - - p.painted(true); - - rule::symbolizers const& symbols = r->get_symbolizers(); - // if the underlying renderer is not able to process the complete set of symbolizers, - // process one by one. -#if defined(SVG_RENDERER) - if(!p.process(symbols,*feature,prj_trans)) -#endif - { - BOOST_FOREACH (symbolizer const& sym, symbols) - { - boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); - } - } - } - } - if (do_also) - { - BOOST_FOREACH( rule * r, also_rules ) - { - #if defined(RENDERING_STATS) - feat_processed = true; - #endif - - p.painted(true); - - rule::symbolizers const& symbols = r->get_symbolizers(); - // if the underlying renderer is not able to process the complete set of symbolizers, - // process one by one. -#if defined(SVG_RENDERER) - if(!p.process(symbols,*feature,prj_trans)) -#endif - { - BOOST_FOREACH (symbolizer const& sym, symbols) - { - boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); - } - } - } - } - #if defined(RENDERING_STATS) - if (feat_processed) - feature_processed_count++; - #endif } - - #if defined(RENDERING_STATS) - style_timer.stop(); - - // done with style - std::ostringstream s; - if (feature_count > 0) { - double perc_processed = ((double)feature_processed_count/(double)feature_count)*100.0; - - s << "percent rendered: " << perc_processed << "% - " << feature_processed_count - << " rendered for " << feature_count << " queried for "; - s << std::setw(15 - (int)s.tellp()) << " layer '" << lay.name() << "' and style '" << s_name << "'\n"; - - } else { - s << "" << std::setw(15) << "- no features returned from query for layer '" << lay.name() << "' and style '" << s_name << "'\n"; + } + // We only have a single style and no grouping. + else + { + 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); + } } - 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); } + +template +void feature_style_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; + + 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),*expr); + if (result.to_bool()) + { + #if defined(RENDERING_STATS) + feat_processed = true; + #endif + + p.painted(true); + + do_else=false; + do_also=true; + rule::symbolizers const& symbols = r->get_symbolizers(); + + // if the underlying renderer is not able to process the complete set of symbolizers, + // process one by one. +#if defined(SVG_RENDERER) + if(!p.process(symbols,*feature,prj_trans)) +#endif + { + + BOOST_FOREACH (symbolizer const& sym, symbols) + { + boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); + } + } + if (style->get_filter_mode() == FILTER_FIRST) + { + // Stop iterating over rules and proceed with next feature. + break; + } + } + } + if (do_else) + { + BOOST_FOREACH( rule * r, style->get_else_rules(scale_denom) ) + { + #if defined(RENDERING_STATS) + feat_processed = true; + #endif + + p.painted(true); + + rule::symbolizers const& symbols = r->get_symbolizers(); + // if the underlying renderer is not able to process the complete set of symbolizers, + // process one by one. +#if defined(SVG_RENDERER) + if(!p.process(symbols,*feature,prj_trans)) +#endif + { + BOOST_FOREACH (symbolizer const& sym, symbols) + { + boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); + } + } + } + } + if (do_also) + { + BOOST_FOREACH( rule * r, style->get_also_rules(scale_denom) ) + { + #if defined(RENDERING_STATS) + feat_processed = true; + #endif + + p.painted(true); + + rule::symbolizers const& symbols = r->get_symbolizers(); + // if the underlying renderer is not able to process the complete set of symbolizers, + // process one by one. +#if defined(SVG_RENDERER) + if(!p.process(symbols,*feature,prj_trans)) +#endif + { + BOOST_FOREACH (symbolizer const& sym, symbols) + { + boost::apply_visitor(symbol_dispatch(p,*feature,prj_trans),sym); + } + } + } + } + #if defined(RENDERING_STATS) + if (feat_processed) + feature_processed_count++; + #endif + } + + #if defined(RENDERING_STATS) + style_timer.stop(); + + // done with style + std::ostringstream s; + if (feature_count > 0) { + double perc_processed = ((double)feature_processed_count/(double)feature_count)*100.0; + + s << "percent rendered: " << perc_processed << "% - " << feature_processed_count + << " rendered for " << feature_count << " queried for "; + s << std::setw(15 - (int)s.tellp()) << " layer '" << lay.name() << "' and style '" << s_name << "'\n"; + + } else { + s << "" << std::setw(15) << "- no features returned from query for layer '" << lay.name() << "' and style '" << s_name << "'\n"; + } + std::clog << s.str(); + style_timer.discard(); + #endif +} + + #if defined(HAVE_CAIRO) template class feature_style_processor >; template class feature_style_processor >; diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index f9047f595..4c37ba12b 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -35,22 +35,26 @@ 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) : rules_(rhs.rules_), - filter_mode_(rhs.filter_mode_) {} + filter_mode_(rhs.filter_mode_), + scale_denom_validity_(-1) {} 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 @@ -73,4 +77,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(&r)); + } + else if (r.has_also_filter()) + { + also_rules_.push_back(const_cast(&r)); + } + else + { + if_rules_.push_back(const_cast(&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_; +} + } diff --git a/src/layer.cpp b/src/layer.cpp index ba40a85f6..f4c2a4d99 100644 --- a/src/layer.cpp +++ b/src/layer.cpp @@ -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_; +} + } diff --git a/src/load_map.cpp b/src/load_map.cpp index d89110563..333516822 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -535,7 +535,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 { @@ -596,6 +597,12 @@ void map_parser::parse_layer( Map & map, ptree const & lay ) lyr.set_cache_features( * cache_features ); } + optional group_by = + get_opt_attr(lay, "group-by"); + if (group_by) + { + lyr.set_group_by( * group_by ); + } ptree::const_iterator itr2 = lay.begin(); ptree::const_iterator end2 = lay.end(); diff --git a/src/memory_datasource.cpp b/src/memory_datasource.cpp index d22bcbd8a..7a89e031f 100644 --- a/src/memory_datasource.cpp +++ b/src/memory_datasource.cpp @@ -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) diff --git a/src/save_map.cpp b/src/save_map.cpp index 0e885c9ce..5e99617d9 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -815,6 +815,11 @@ void serialize_layer( ptree & map_node, const layer & layer, bool explicit_defau set_attr/**/( layer_node, "cache-features", layer.cache_features() ); } + if ( layer.group_by() != "" || explicit_defaults ) + { + set_attr( layer_node, "group-by", layer.group_by() ); + } + std::vector const& style_names = layer.styles(); for (unsigned i = 0; i < style_names.size(); ++i) { From bd564449d77bb908b72409d9680cb12c9b8647f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Wed, 7 Dec 2011 23:03:14 +0100 Subject: [PATCH 2/2] fix typo --- src/feature_type_style.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/feature_type_style.cpp b/src/feature_type_style.cpp index 6e5a04924..6dc0fe86b 100644 --- a/src/feature_type_style.cpp +++ b/src/feature_type_style.cpp @@ -40,7 +40,7 @@ feature_type_style::feature_type_style() feature_type_style::feature_type_style(feature_type_style const& rhs, bool deep_copy) : filter_mode_(rhs.filter_mode_), - scale_denom_valicity_(-1) + scale_denom_validity_(-1) { if (!deep_copy) { rules_ = rhs.rules_;