From 8b1ddd8766c22bbd263bcdb94ab1717bfd3b8476 Mon Sep 17 00:00:00 2001 From: Dane Springmeyer Date: Fri, 2 Mar 2012 09:58:53 -0800 Subject: [PATCH] stats_renderer first pass --- bindings/python/mapnik/__init__.py | 2 + bindings/python/mapnik_parameters.cpp | 14 ++ bindings/python/mapnik_python.cpp | 12 ++ include/mapnik/stats_processor.hpp | 90 +++++++++++ src/build.py | 1 + src/stats_processor.cpp | 225 ++++++++++++++++++++++++++ 6 files changed, 344 insertions(+) create mode 100644 include/mapnik/stats_processor.hpp create mode 100644 src/stats_processor.cpp diff --git a/bindings/python/mapnik/__init__.py b/bindings/python/mapnik/__init__.py index 9fa035211..423d40199 100644 --- a/bindings/python/mapnik/__init__.py +++ b/bindings/python/mapnik/__init__.py @@ -588,6 +588,7 @@ def register_plugins(path=inputpluginspath): """Register plugins located by specified path""" DatasourceCache.instance().register_datasources(path) +# TODO - recurse def register_fonts(path=fontscollectionpath,valid_extensions=['.ttf','.otf','.ttc','.pfa','.pfb','.ttc','.dfont']): """Recursively register fonts using path argument as base directory""" for dirpath, _, filenames in os.walk(path): @@ -692,6 +693,7 @@ __all__ = [ 'PathExpression', # load/save/render 'load_map', + 'render_stats', 'load_map_from_string', 'save_map', 'save_map_to_string', diff --git a/bindings/python/mapnik_parameters.cpp b/bindings/python/mapnik_parameters.cpp index 5a015c0ae..35a06d312 100644 --- a/bindings/python/mapnik_parameters.cpp +++ b/bindings/python/mapnik_parameters.cpp @@ -195,6 +195,19 @@ boost::shared_ptr create_parameter_from_float(std::string con } +boost::python::dict to_json(const parameters& p) +{ + using namespace boost::python; + dict d; + parameters::const_iterator pos=p.begin(); + while(pos!=p.end()) + { + d[pos->first] = pos->second; + ++pos; + } + return d; +} + void export_parameters() { using namespace boost::python; @@ -220,5 +233,6 @@ void export_parameters() .def("__len__",¶meters::size) .def("append",add_parameter) .def("iteritems",iterator()) + .def("to_json",&to_json) ; } diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index e0591977a..8d401b6f5 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -88,6 +88,8 @@ void export_label_collision_detector(); #include "mapnik_threads.hpp" #include "python_optional.hpp" +#include + #if defined(HAVE_CAIRO) && defined(HAVE_PYCAIRO) #include static Pycairo_CAPI_t *Pycairo_CAPI; @@ -100,6 +102,14 @@ bool python_thread::thread_support = true; #endif boost::thread_specific_ptr python_thread::state; +std::string render_stats(const mapnik::Map& map) +{ + python_unblock_auto_block b; + mapnik::stats_processor ren(map); + ren.apply(); + return ren.to_json(); +} + void render(const mapnik::Map& map, mapnik::image_32& image, double scale_factor = 1.0, @@ -577,6 +587,8 @@ BOOST_PYTHON_MODULE(_mapnik) def("has_cairo", &has_cairo, "Get cairo library status"); def("has_pycairo", &has_pycairo, "Get pycairo module status"); + def("render_stats", &render_stats); + python_optional(); python_optional >(); python_optional(); diff --git a/include/mapnik/stats_processor.hpp b/include/mapnik/stats_processor.hpp new file mode 100644 index 000000000..821fd0c3f --- /dev/null +++ b/include/mapnik/stats_processor.hpp @@ -0,0 +1,90 @@ +/***************************************************************************** + * + * 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 + * + *****************************************************************************/ + +#ifndef MAPNIK_STATS_PROCESSOR_HPP +#define MAPNIK_STATS_PROCESSOR_HPP + +// mapnik +#include +#include +#include + +#include +#include +#include + + +// stl +#include +#include +#include + +namespace mapnik +{ + +class Map; +class layer; +class projection; + +struct stats +{ + stats() + : elapsed(0), + count(0), + reprojected(false) + {} + unsigned int elapsed; + unsigned int count; + bool reprojected; +}; + +class stats_processor +{ +public: + explicit stats_processor(Map const& m); + void apply(); + std::string to_json() + { + std::ostringstream ss; + write_json(ss,tree); + return ss.str(); + } + +private: + void apply_to_layer(layer const& lay, + projection const& proj0, + double scale_denom, + std::set& names); + void render_style(layer const& lay, + feature_type_style* style, + std::string const& style_name, + featureset_ptr features, + proj_transform const& prj_trans, + double scale_denom); + + Map const& m_; + boost::property_tree::ptree tree; + boost::scoped_ptr s; +}; +} + +#endif // MAPNIK_STATS_PROCESSOR_HPP diff --git a/src/build.py b/src/build.py index cde069d0f..7130203e4 100644 --- a/src/build.py +++ b/src/build.py @@ -135,6 +135,7 @@ source = Split( polygon_symbolizer.cpp save_map.cpp shield_symbolizer.cpp + stats_processor.cpp text_symbolizer.cpp tiff_reader.cpp wkb.cpp diff --git a/src/stats_processor.cpp b/src/stats_processor.cpp new file mode 100644 index 000000000..0da1fd327 --- /dev/null +++ b/src/stats_processor.cpp @@ -0,0 +1,225 @@ +/***************************************************************************** + * + * 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 + +// boost +#include + +//stl +#include + +#include +#include +#include + +namespace mapnik +{ + + +stats_processor::stats_processor(Map const& m) + : m_(m), + tree(), + s(new stats()) + {} + +void stats_processor::apply() +{ + + mapnik::timer t; + projection proj(m_.srs()); + + double scale_denom = mapnik::scale_denominator(m_,proj.is_geographic()); + + BOOST_FOREACH ( layer const& lyr, m_.layers() ) + { + if (lyr.isVisible(scale_denom)) + { + std::set names; + apply_to_layer(lyr, proj, scale_denom, names); + } + } + //tree.put("wall_clock_elapsed", t.wall_clock_elapsed()); + //tree.put("cpu_elapsed", t.cpu_elapsed()); +} + +void stats_processor::apply_to_layer(layer const& lay, projection const& proj0, + double scale_denom, + std::set& names) +{ + boost::property_tree::ptree ltree; + mapnik::timer t; + + 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; + } + + projection proj1(lay.srs()); + proj_transform prj_trans(proj0,proj1); + + ltree.put("reprojected",!prj_trans.equal()); + + 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()) + { + 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, PROJ_ENVELOPE_POINTS)) + std::clog << "WARNING: layer " << lay.name() + << " extent " << layer_ext << " in map projection " + << " did not reproject properly back to layer projection\n"; + } + else + { + return; + } + + ltree.put("clipped_extent",layer_ext); + + box2d query_ext = m_.get_current_extent(); + box2d unbuffered_extent = 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,unbuffered_extent); + + 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))); + } + } + + // 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); + } + + int i = 0; + BOOST_FOREACH (feature_type_style * style, active_styles) + { + boost::property_tree::ptree stree; + stree.put("name",style_names[i++]); + + featureset_ptr features = ds->features(q); + if (features) { + feature_ptr feature; + while ((feature = features->next())) + { + 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()) + { + + + } + } + } + } + // http://stackoverflow.com/questions/2114466/creating-json-arrays-in-boost-using-property-trees + ltree.push_back( std::make_pair("", stree ) ); + } + } + + ltree.put("wall_clock_elapsed", t.wall_clock_elapsed()); + ltree.put("cpu_elapsed", t.cpu_elapsed()); + + tree.put_child(lay.name(),ltree); +} + +}