/***************************************************************************** * * 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 * *****************************************************************************/ //mapnik #include #include #include #include #include #include #include #include #include #include #include #include #include // boost #include //stl #include #if defined(HAVE_CAIRO) #include #endif #if defined(RENDERING_STATS) #include #include #include #endif namespace mapnik { /** 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, Feature const& f, proj_transform const& prj_trans) : output_(output), f_(f), prj_trans_(prj_trans) {} template void operator () (T const& sym) const { output_.process(sym,f_,prj_trans_); } Processor & output_; Feature const& f_; proj_transform const& prj_trans_; }; 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()); start_metawriters(m_,proj); double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); scale_denom *= scale_factor_; BOOST_FOREACH ( layer const& lyr, m_.layers() ) { if (lyr.isVisible(scale_denom)) { std::set names; apply_to_layer(lyr, p, proj, scale_denom, names); } } stop_metawriters(m_); } catch (proj_init_error& ex) { std::clog << "proj_init_error:" << ex.what() << "\n"; } 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.isVisible(scale_denom)) { apply_to_layer(lyr, p, proj, scale_denom, names); } } catch (proj_init_error& ex) { std::clog << "proj_init_error:" << ex.what() << "\n"; } p.end_map_processing(m_); } template void feature_style_processor::start_metawriters(Map const& m_, projection const& proj) { Map::const_metawriter_iterator metaItr = m_.begin_metawriters(); Map::const_metawriter_iterator metaItrEnd = m_.end_metawriters(); for (;metaItr!=metaItrEnd; ++metaItr) { metaItr->second->set_size(m_.width(), m_.height()); metaItr->second->set_map_srs(proj); metaItr->second->start(m_.metawriter_output_properties); } } template void feature_style_processor::stop_metawriters(Map const& m_) { Map::const_metawriter_iterator metaItr = m_.begin_metawriters(); Map::const_metawriter_iterator metaItrEnd = m_.end_metawriters(); for (;metaItr!=metaItrEnd; ++metaItr) { metaItr->second->stop(); } } 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) { std::clog << "WARNING: No style for layer '" << lay.name() << "'\n"; return; } mapnik::datasource_ptr ds = lay.datasource(); if (!ds) { std::clog << "WARNING: No datasource for layer '" << lay.name() << "'\n"; return; } p.start_layer_processing(lay); #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 map_ext = m_.get_buffered_extent(); // clip buffered extent by maximum extent, if supplied boost::optional > const& maximum_extent = m_.maximum_extent(); if (maximum_extent) { map_ext.clip(*maximum_extent); } box2d layer_ext = lay.envelope(); // first, try intersection of map extent forward projected into layer srs if (prj_trans.forward(map_ext, PROJ_ENVELOPE_POINTS) && map_ext.intersects(layer_ext)) { layer_ext.clip(map_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) && map_ext.intersects(layer_ext)) { layer_ext.clip(map_ext); // forward project layer extent back into native projection if (!prj_trans.forward(layer_ext)) std::clog << "WARNING: layer " << lay.name() << " extent " << layer_ext << " in map projection " << " did not reproject properly back to layer projection\n"; } else { // if no intersection then nothing to do for layer #if defined(RENDERING_STATS) layer_timer.discard(); #endif return; } box2d query_ext = m_.get_current_extent(); prj_trans.forward(query_ext, PROJ_ENVELOPE_POINTS); query::resolution_type res(m_.width()/query_ext.width(), m_.height()/query_ext.height()); query q(layer_ext,res,scale_denom); std::vector active_styles; attribute_collector collector(names); double filt_factor = 1; 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) { std::clog << "WARNING: style '" << style_name << "' required for layer '" << lay.name() << "' does not exist.\n"; 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))); } } // 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 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) { 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)); } if ( (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); } } } // process features featureset_ptr fs; if (first) { if (cache_features) first = false; fs = ds->features(q); } else { fs = cache.features(q); } if (fs) { feature_ptr feature; while ((feature = fs->next())) { #if defined(RENDERING_STATS) feature_count++; bool feat_processed = false; #endif bool do_else=true; bool do_also=false; if (cache_features) { cache.push(feature); } BOOST_FOREACH(rule * r, if_rules ) { 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. #ifdef 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, 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. #ifdef 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. #ifdef 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(); #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 >; template class feature_style_processor >; #endif template class feature_style_processor >; template class feature_style_processor >; }