/***************************************************************************** * * 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 * *****************************************************************************/ #include //mapnik #include #include #include #include #include #include #include #include #include #include #include #include #ifdef MAPNIK_DEBUG //#include #endif // boost #include //stl #include #if defined(HAVE_CAIRO) #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() { #ifdef MAPNIK_DEBUG //mapnik::wall_clock_progress_timer t(std::clog, "map rendering took: "); #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_; #ifdef MAPNIK_DEBUG std::clog << "scale denominator = " << scale_denom << "\n"; #endif 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_); } 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) { #ifdef MAPNIK_DEBUG //wall_clock_progress_timer timer(clog, "end layer rendering: "); #endif std::vector const& style_names = lay.styles(); unsigned int num_styles = style_names.size(); if (!num_styles) 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 (ds) { projection proj1(lay.srs()); proj_transform prj_trans(proj0,proj1); // todo: only display raster if src and dest proj are matched // todo: add raster re-projection as an optional feature if (ds->type() == datasource::Raster && !prj_trans.equal()) { std::clog << "WARNING: Map srs does not match layer srs, skipping raster layer '" << lay.name() << "' as raster re-projection is not currently supported (http://trac.mapnik.org/ticket/663)\n" << "map srs: '" << m_.srs() << "'\nlayer srs: '" << lay.srs() << "' \n"; return; } 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) && 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()) { return; } // next try intersection of layer extent back projected into map srs else if (prj_trans.backward(layer_ext) && 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 return; } query::resolution_type res(m_.width()/m_.get_current_extent().width(), m_.height()/m_.get_current_extent().height()); query q(layer_ext,res,scale_denom); //BBOX query 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; BOOST_FOREACH (feature_type_style * style, active_styles) { std::vector if_rules; std::vector else_rules; std::vector const& rules=style->get_rules(); 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_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())) { bool do_else=true; 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()) { do_else=false; 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 ) { 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); } } } } } } cache_features = false; } } 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 >; }