From 7c9700237f02d0561fea4e0834c5b155a1e9f094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Sat, 25 Aug 2012 13:35:41 +0200 Subject: [PATCH] move feature_style_processor to an implementation header file This allows other applications to create custom instances of the feature_style_processor with their own template arguments without forcing freuquent recompiles in mapnik itself --- .../mapnik/feature_style_processor_impl.hpp | 628 ++++++++++++++++++ src/feature_style_processor.cpp | 597 +---------------- 2 files changed, 629 insertions(+), 596 deletions(-) create mode 100644 include/mapnik/feature_style_processor_impl.hpp diff --git a/include/mapnik/feature_style_processor_impl.hpp b/include/mapnik/feature_style_processor_impl.hpp new file mode 100644 index 000000000..cab5179fd --- /dev/null +++ b/include/mapnik/feature_style_processor_impl.hpp @@ -0,0 +1,628 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2011 Artem Pavlenko + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + *****************************************************************************/ + +// NOTE: This is an implementation header file and is only meant to be included +// from implementation files. It therefore doesn't have an include guard. To +// create a custom feature_style_processor, include this file and instantiate +// the template with the desired template arguments. + +// mapnik +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// boost +#include +#include + +// stl +#include + +#if defined(HAVE_CAIRO) +#include +#endif + +#if defined(SVG_RENDERER) +#include +#endif + +#if defined(RENDERING_STATS) +#include +#include +#include +#endif + +namespace mapnik +{ + +template struct has_process; + +template +struct process_impl +{ + template + static void process(T0 & ren, T1 const& sym, T2 & f, T3 const& tr) + { + ren.process(sym,f,tr); + } +}; + +template <> // No-op specialization +struct process_impl +{ + template + static void process(T0 & ren, T1 const& sym, T2 & f, T3 const& tr) + { + boost::ignore_unused_variable_warning(ren); + boost::ignore_unused_variable_warning(f); + boost::ignore_unused_variable_warning(tr); +#ifdef MAPNIK_DEBUG + std::clog << "NO-OP ...\n"; +#endif + } +}; + +/** Calls the renderer's process function, + * \param output Renderer + * \param f Feature to process + * \param prj_trans Projection + * \param sym Symbolizer object + */ +template +struct feature_style_processor::symbol_dispatch : public boost::static_visitor<> +{ + symbol_dispatch (Processor & output, + mapnik::feature_impl & f, + proj_transform const& prj_trans) + : output_(output), + f_(f), + prj_trans_(prj_trans) {} + + template + void operator () (T const& sym) const + { + process_impl::value>::process(output_,sym,f_,prj_trans_); + } + + Processor & output_; + mapnik::feature_impl & f_; + proj_transform const& prj_trans_; +}; + +typedef char (&no_tag)[1]; +typedef char (&yes_tag)[2]; + +template +struct process_memfun_helper {}; + +template no_tag has_process_helper(...); +template yes_tag has_process_helper(process_memfun_helper* p); + +template +struct has_process +{ + typedef typename T0::processor_impl_type processor_impl_type; + BOOST_STATIC_CONSTANT(bool + , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) + ); +}; + + +template +feature_style_processor::feature_style_processor(Map const& m, double scale_factor) + : m_(m), scale_factor_(scale_factor) +{ +} + +template +void feature_style_processor::apply() +{ +#if defined(RENDERING_STATS) + std::clog << "\n//-- starting rendering timer...\n"; + mapnik::progress_timer t(std::clog, "total map rendering"); +#endif + + Processor & p = static_cast(*this); + p.start_map_processing(m_); + + try + { + projection proj(m_.srs()); + double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); + scale_denom *= scale_factor_; + + BOOST_FOREACH ( layer const& lyr, m_.layers() ) + { + if (lyr.visible(scale_denom)) + { + std::set names; + apply_to_layer(lyr, p, proj, scale_denom, names); + } + } + } + catch (proj_init_error& ex) + { + MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: proj_init_error=" << ex.what(); + } + + p.end_map_processing(m_); + +#if defined(RENDERING_STATS) + t.stop(); + std::clog << "//-- rendering timer stopped...\n\n"; +#endif + +} + +template +void feature_style_processor::apply(mapnik::layer const& lyr, std::set& names) +{ + Processor & p = static_cast(*this); + p.start_map_processing(m_); + try + { + projection proj(m_.srs()); + double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); + scale_denom *= scale_factor_; + + if (lyr.visible(scale_denom)) + { + apply_to_layer(lyr, p, proj, scale_denom, names); + } + } + catch (proj_init_error& ex) + { + MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: proj_init_error=" << ex.what(); + } + p.end_map_processing(m_); +} + +template +void feature_style_processor::apply_to_layer(layer const& lay, Processor & p, + projection const& proj0, + double scale_denom, + std::set& names) +{ + std::vector const& style_names = lay.styles(); + + unsigned int num_styles = style_names.size(); + if (! num_styles) + { + MAPNIK_LOG_DEBUG(feature_style_processor) << "feature_style_processor: No style for layer=" << lay.name(); + + return; + } + + mapnik::datasource_ptr ds = lay.datasource(); + if (! ds) + { + MAPNIK_LOG_DEBUG(feature_style_processor) << "feature_style_processor: No datasource for layer=" << lay.name(); + + return; + } + +#if defined(RENDERING_STATS) + progress_timer layer_timer(std::clog, "rendering total for layer: '" + lay.name() + "'"); +#endif + + projection proj1(lay.srs()); + proj_transform prj_trans(proj0,proj1); + +#if defined(RENDERING_STATS) + if (! prj_trans.equal()) + { + std::clog << "notice: reprojecting layer: '" << lay.name() << "' from/to:\n\t'" + << lay.srs() << "'\n\t'" + << m_.srs() << "'\n"; + } +#endif + + box2d buffered_query_ext = m_.get_buffered_extent(); // buffered + + // clip buffered extent by maximum extent, if supplied + boost::optional > const& maximum_extent = m_.maximum_extent(); + if (maximum_extent) { + buffered_query_ext.clip(*maximum_extent); + } + + box2d layer_ext = lay.envelope(); + bool fw_success = false; + + // first, try intersection of map extent forward projected into layer srs + if (prj_trans.forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) + { + fw_success = true; + layer_ext.clip(buffered_query_ext); + } + // if no intersection and projections are also equal, early return + else if (prj_trans.equal()) + { +#if defined(RENDERING_STATS) + layer_timer.discard(); +#endif + return; + } + // next try intersection of layer extent back projected into map srs + else if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) + { + layer_ext.clip(buffered_query_ext); + // forward project layer extent back into native projection + if (! prj_trans.forward(layer_ext, PROJ_ENVELOPE_POINTS)) + { + MAPNIK_LOG_DEBUG(feature_style_processor) + << "feature_style_processor: Layer=" << lay.name() + << " extent=" << layer_ext << " in map projection " + << " did not reproject properly back to layer projection"; + } + } + else + { + // if no intersection then nothing to do for layer +#if defined(RENDERING_STATS) + layer_timer.discard(); +#endif + return; + } + + // if we've got this far, now prepare the unbuffered extent + // which is used as a bbox for clipping geometries + box2d query_ext = m_.get_current_extent(); // unbuffered + if (maximum_extent) + { + query_ext.clip(*maximum_extent); + } + + box2d layer_ext2 = lay.envelope(); + if (fw_success) + { + if (prj_trans.forward(query_ext, PROJ_ENVELOPE_POINTS)) + { + layer_ext2.clip(query_ext); + } + } + else + { + if (prj_trans.backward(layer_ext2, PROJ_ENVELOPE_POINTS)) + { + layer_ext2.clip(query_ext); + prj_trans.forward(layer_ext2, PROJ_ENVELOPE_POINTS); + } + } + + p.start_layer_processing(lay, layer_ext2); + + double qw = query_ext.width()>0 ? query_ext.width() : 1; + double qh = query_ext.height()>0 ? query_ext.height() : 1; + query::resolution_type res(m_.width()/qw, + m_.height()/qh); + + query q(layer_ext,res,scale_denom,m_.get_current_extent()); + std::vector active_styles; + attribute_collector collector(names); + double filt_factor = 1.0; + directive_collector d_collector(filt_factor); + + // iterate through all named styles collecting active styles and attribute names + BOOST_FOREACH(std::string const& style_name, style_names) + { + boost::optional style=m_.find_style(style_name); + if (!style) + { + MAPNIK_LOG_DEBUG(feature_style_processor) + << "feature_style_processor: Style=" << style_name + << " required for layer=" << lay.name() << " does not exist."; + + continue; + } + + const std::vector& rules=(*style).get_rules(); + bool active_rules=false; + + BOOST_FOREACH(rule const& r, rules) + { + if (r.active(scale_denom)) + { + active_rules = true; + if (ds->type() == datasource::Vector) + { + collector(r); + } + // TODO - in the future rasters should be able to be filtered. + } + } + if (active_rules) + { + active_styles.push_back(const_cast(&(*style))); + } + } + + // 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); + } + + // 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(); + rule::symbolizers::const_iterator symEnd = symbols.end(); + while (symIter != symEnd) + { + // if multiple raster symbolizers, last will be respected + // should we warn or throw? + boost::apply_visitor(d_collector,*symIter++); + } + q.set_filter_factor(filt_factor); + } + } + } + + // Also query the group by attribute + std::string group_by = lay.group_by(); + if (group_by != "") + { + 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->get(group_by) != feature->get(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 + { + 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 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) +{ + + p.start_style_processing(*style); + +#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(!p.process(symbols,*feature,prj_trans)) + { + + 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(!p.process(symbols,*feature,prj_trans)) + { + 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(!p.process(symbols,*feature,prj_trans)) + { + 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 '" << style_name << "'\n"; + } + else + { + s << "" << std::setw(15) << "- no features returned from query for layer '" << lay.name() << "' and style '" << style_name << "'\n"; + } + std::clog << s.str(); + style_timer.discard(); +#endif + p.end_style_processing(*style); +} + +} diff --git a/src/feature_style_processor.cpp b/src/feature_style_processor.cpp index a7d788ba3..642b81213 100644 --- a/src/feature_style_processor.cpp +++ b/src/feature_style_processor.cpp @@ -21,606 +21,11 @@ *****************************************************************************/ // mapnik -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// boost -#include -#include - -// stl -#include - -#if defined(HAVE_CAIRO) -#include -#endif - -#if defined(SVG_RENDERER) -#include -#endif - -#if defined(RENDERING_STATS) -#include -#include -#include -#endif +#include namespace mapnik { -template struct has_process; - -template -struct process_impl -{ - template - static void process(T0 & ren, T1 const& sym, T2 & f, T3 const& tr) - { - ren.process(sym,f,tr); - } -}; - -template <> // No-op specialization -struct process_impl -{ - template - static void process(T0 & ren, T1 const& sym, T2 & f, T3 const& tr) - { - boost::ignore_unused_variable_warning(ren); - boost::ignore_unused_variable_warning(f); - boost::ignore_unused_variable_warning(tr); -#ifdef MAPNIK_DEBUG - std::clog << "NO-OP ...\n"; -#endif - } -}; - -/** Calls the renderer's process function, - * \param output Renderer - * \param f Feature to process - * \param prj_trans Projection - * \param sym Symbolizer object - */ -template -struct feature_style_processor::symbol_dispatch : public boost::static_visitor<> -{ - symbol_dispatch (Processor & output, - mapnik::feature_impl & f, - proj_transform const& prj_trans) - : output_(output), - f_(f), - prj_trans_(prj_trans) {} - - template - void operator () (T const& sym) const - { - process_impl::value>::process(output_,sym,f_,prj_trans_); - } - - Processor & output_; - mapnik::feature_impl & f_; - proj_transform const& prj_trans_; -}; - -typedef char (&no_tag)[1]; -typedef char (&yes_tag)[2]; - -template -struct process_memfun_helper {}; - -template no_tag has_process_helper(...); -template yes_tag has_process_helper(process_memfun_helper* p); - -template -struct has_process -{ - typedef typename T0::processor_impl_type processor_impl_type; - BOOST_STATIC_CONSTANT(bool - , value = sizeof(has_process_helper(0)) == sizeof(yes_tag) - ); -}; - - -template -feature_style_processor::feature_style_processor(Map const& m, double scale_factor) - : m_(m), scale_factor_(scale_factor) -{ -} - -template -void feature_style_processor::apply() -{ -#if defined(RENDERING_STATS) - std::clog << "\n//-- starting rendering timer...\n"; - mapnik::progress_timer t(std::clog, "total map rendering"); -#endif - - Processor & p = static_cast(*this); - p.start_map_processing(m_); - - try - { - projection proj(m_.srs()); - double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); - scale_denom *= scale_factor_; - - BOOST_FOREACH ( layer const& lyr, m_.layers() ) - { - if (lyr.visible(scale_denom)) - { - std::set names; - apply_to_layer(lyr, p, proj, scale_denom, names); - } - } - } - catch (proj_init_error& ex) - { - MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: proj_init_error=" << ex.what(); - } - - p.end_map_processing(m_); - -#if defined(RENDERING_STATS) - t.stop(); - std::clog << "//-- rendering timer stopped...\n\n"; -#endif - -} - -template -void feature_style_processor::apply(mapnik::layer const& lyr, std::set& names) -{ - Processor & p = static_cast(*this); - p.start_map_processing(m_); - try - { - projection proj(m_.srs()); - double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); - scale_denom *= scale_factor_; - - if (lyr.visible(scale_denom)) - { - apply_to_layer(lyr, p, proj, scale_denom, names); - } - } - catch (proj_init_error& ex) - { - MAPNIK_LOG_ERROR(feature_style_processor) << "feature_style_processor: proj_init_error=" << ex.what(); - } - p.end_map_processing(m_); -} - -template -void feature_style_processor::apply_to_layer(layer const& lay, Processor & p, - projection const& proj0, - double scale_denom, - std::set& names) -{ - std::vector const& style_names = lay.styles(); - - unsigned int num_styles = style_names.size(); - if (! num_styles) - { - MAPNIK_LOG_DEBUG(feature_style_processor) << "feature_style_processor: No style for layer=" << lay.name(); - - return; - } - - mapnik::datasource_ptr ds = lay.datasource(); - if (! ds) - { - MAPNIK_LOG_DEBUG(feature_style_processor) << "feature_style_processor: No datasource for layer=" << lay.name(); - - return; - } - -#if defined(RENDERING_STATS) - progress_timer layer_timer(std::clog, "rendering total for layer: '" + lay.name() + "'"); -#endif - - projection proj1(lay.srs()); - proj_transform prj_trans(proj0,proj1); - -#if defined(RENDERING_STATS) - if (! prj_trans.equal()) - { - std::clog << "notice: reprojecting layer: '" << lay.name() << "' from/to:\n\t'" - << lay.srs() << "'\n\t'" - << m_.srs() << "'\n"; - } -#endif - - box2d buffered_query_ext = m_.get_buffered_extent(); // buffered - - // clip buffered extent by maximum extent, if supplied - boost::optional > const& maximum_extent = m_.maximum_extent(); - if (maximum_extent) { - buffered_query_ext.clip(*maximum_extent); - } - - box2d layer_ext = lay.envelope(); - bool fw_success = false; - - // first, try intersection of map extent forward projected into layer srs - if (prj_trans.forward(buffered_query_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) - { - fw_success = true; - layer_ext.clip(buffered_query_ext); - } - // if no intersection and projections are also equal, early return - else if (prj_trans.equal()) - { -#if defined(RENDERING_STATS) - layer_timer.discard(); -#endif - return; - } - // next try intersection of layer extent back projected into map srs - else if (prj_trans.backward(layer_ext, PROJ_ENVELOPE_POINTS) && buffered_query_ext.intersects(layer_ext)) - { - layer_ext.clip(buffered_query_ext); - // forward project layer extent back into native projection - if (! prj_trans.forward(layer_ext, PROJ_ENVELOPE_POINTS)) - { - MAPNIK_LOG_DEBUG(feature_style_processor) - << "feature_style_processor: Layer=" << lay.name() - << " extent=" << layer_ext << " in map projection " - << " did not reproject properly back to layer projection"; - } - } - else - { - // if no intersection then nothing to do for layer -#if defined(RENDERING_STATS) - layer_timer.discard(); -#endif - return; - } - - // if we've got this far, now prepare the unbuffered extent - // which is used as a bbox for clipping geometries - box2d query_ext = m_.get_current_extent(); // unbuffered - if (maximum_extent) - { - query_ext.clip(*maximum_extent); - } - - box2d layer_ext2 = lay.envelope(); - if (fw_success) - { - if (prj_trans.forward(query_ext, PROJ_ENVELOPE_POINTS)) - { - layer_ext2.clip(query_ext); - } - } - else - { - if (prj_trans.backward(layer_ext2, PROJ_ENVELOPE_POINTS)) - { - layer_ext2.clip(query_ext); - prj_trans.forward(layer_ext2, PROJ_ENVELOPE_POINTS); - } - } - - p.start_layer_processing(lay, layer_ext2); - - double qw = query_ext.width()>0 ? query_ext.width() : 1; - double qh = query_ext.height()>0 ? query_ext.height() : 1; - query::resolution_type res(m_.width()/qw, - m_.height()/qh); - - query q(layer_ext,res,scale_denom,m_.get_current_extent()); - std::vector active_styles; - attribute_collector collector(names); - double filt_factor = 1.0; - directive_collector d_collector(filt_factor); - - // iterate through all named styles collecting active styles and attribute names - BOOST_FOREACH(std::string const& style_name, style_names) - { - boost::optional style=m_.find_style(style_name); - if (!style) - { - MAPNIK_LOG_DEBUG(feature_style_processor) - << "feature_style_processor: Style=" << style_name - << " required for layer=" << lay.name() << " does not exist."; - - continue; - } - - const std::vector& rules=(*style).get_rules(); - bool active_rules=false; - - BOOST_FOREACH(rule const& r, rules) - { - if (r.active(scale_denom)) - { - active_rules = true; - if (ds->type() == datasource::Vector) - { - collector(r); - } - // TODO - in the future rasters should be able to be filtered. - } - } - if (active_rules) - { - active_styles.push_back(const_cast(&(*style))); - } - } - - // 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); - } - - // 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(); - rule::symbolizers::const_iterator symEnd = symbols.end(); - while (symIter != symEnd) - { - // if multiple raster symbolizers, last will be respected - // should we warn or throw? - boost::apply_visitor(d_collector,*symIter++); - } - q.set_filter_factor(filt_factor); - } - } - } - - // Also query the group by attribute - std::string group_by = lay.group_by(); - if (group_by != "") - { - 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->get(group_by) != feature->get(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 - { - 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 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) -{ - - p.start_style_processing(*style); - -#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(!p.process(symbols,*feature,prj_trans)) - { - - 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(!p.process(symbols,*feature,prj_trans)) - { - 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(!p.process(symbols,*feature,prj_trans)) - { - 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 '" << style_name << "'\n"; - } - else - { - s << "" << std::setw(15) << "- no features returned from query for layer '" << lay.name() << "' and style '" << style_name << "'\n"; - } - std::clog << s.str(); - style_timer.discard(); -#endif - p.end_style_processing(*style); -} - - #if defined(HAVE_CAIRO) template class feature_style_processor >; template class feature_style_processor >;