From d5b0432dd73d86d2a204afeff04310527a8a382a Mon Sep 17 00:00:00 2001 From: Hermann Kraus Date: Mon, 12 Jul 2010 15:27:33 +0000 Subject: [PATCH] Many metawriter improvements: - Easier to configure - Correct JSON output - Handling more than one renderer run (writing pre-/postamble is no longer done in constructor/destructor) - Collect all attributes required by metawriters --- include/mapnik/attribute_collector.hpp | 14 +++- include/mapnik/feature_style_processor.hpp | 15 +++- include/mapnik/map.hpp | 22 +++++- include/mapnik/metawriter.hpp | 28 ++++++- include/mapnik/metawriter_json.hpp | 11 ++- include/mapnik/symbolizer.hpp | 11 ++- src/agg/process_point_symbolizer.cpp | 4 +- src/cairo_renderer.cpp | 2 +- src/load_map.cpp | 22 ++---- src/map.cpp | 15 ++++ src/metawriter.cpp | 88 ++++++++++++++++------ src/symbolizer.cpp | 17 +---- 12 files changed, 183 insertions(+), 66 deletions(-) diff --git a/include/mapnik/attribute_collector.hpp b/include/mapnik/attribute_collector.hpp index de31c1fa4..922e4c9ae 100644 --- a/include/mapnik/attribute_collector.hpp +++ b/include/mapnik/attribute_collector.hpp @@ -106,7 +106,7 @@ struct symbolizer_attributes : public boost::static_visitor<> expression_attributes f_attr(names_); boost::apply_visitor(f_attr,*orientation_expr); } - + collect_metawriter(sym); } void operator () (point_symbolizer const& sym) @@ -116,6 +116,8 @@ struct symbolizer_attributes : public boost::static_visitor<> { path_processor_type::collect_attributes(*filename_expr,names_); } + collect_metawriter(sym); + } void operator () (line_pattern_symbolizer const& sym) @@ -125,6 +127,7 @@ struct symbolizer_attributes : public boost::static_visitor<> { path_processor_type::collect_attributes(*filename_expr,names_); } + collect_metawriter(sym); } void operator () (polygon_pattern_symbolizer const& sym) @@ -134,6 +137,7 @@ struct symbolizer_attributes : public boost::static_visitor<> { path_processor_type::collect_attributes(*filename_expr,names_); } + collect_metawriter(sym); } void operator () (shield_symbolizer const& sym) @@ -150,6 +154,7 @@ struct symbolizer_attributes : public boost::static_visitor<> { path_processor_type::collect_attributes(*filename_expr,names_); } + collect_metawriter(sym); } void operator () (glyph_symbolizer const& sym) @@ -188,12 +193,17 @@ struct symbolizer_attributes : public boost::static_visitor<> expression_attributes f_attr(names_); boost::apply_visitor(f_attr,*color_expr); } - + collect_metawriter(sym); } // TODO - support remaining syms private: std::set& names_; + void collect_metawriter(symbolizer_base const& sym) + { + metawriter_properties const& properties = sym.get_metawriter_properties(); + names_.insert(properties.begin(), properties.end()); + } }; diff --git a/include/mapnik/feature_style_processor.hpp b/include/mapnik/feature_style_processor.hpp index 42e0522a4..ac200826c 100644 --- a/include/mapnik/feature_style_processor.hpp +++ b/include/mapnik/feature_style_processor.hpp @@ -84,6 +84,13 @@ public: #endif Processor & p = static_cast(*this); p.start_map_processing(m_); + Map::const_metawriter_iterator metaItr = m_.begin_metawriters(); + Map::const_metawriter_iterator metaItrEnd = m_.end_metawriters(); + + for (;metaItr!=metaItrEnd; ++metaItr) + { + metaItr->second->start(); + } try { @@ -109,7 +116,13 @@ public: { std::clog << "proj_init_error:" << ex.what() << "\n"; } - + + metaItr = m_.begin_metawriters(); + for (;metaItr!=metaItrEnd; ++metaItr) + { + metaItr->second->stop(); + } + p.end_map_processing(m_); } private: diff --git a/include/mapnik/map.hpp b/include/mapnik/map.hpp index 6727c26a7..9a0c2735e 100644 --- a/include/mapnik/map.hpp +++ b/include/mapnik/map.hpp @@ -87,6 +87,7 @@ public: typedef std::map::iterator style_iterator; typedef std::map::const_iterator const_fontset_iterator; typedef std::map::iterator fontset_iterator; + typedef std::map::const_iterator const_metawriter_iterator; /*! \brief Default constructor. * @@ -166,7 +167,7 @@ public: */ boost::optional find_style(std::string const& name) const; - /*! \brief Insert a meta-writer in the map. + /*! \brief Insert a metawriter in the map. * @param name The name of the writer. * @param style A pointer to the writer to insert. * @return true If success. @@ -174,16 +175,31 @@ public: */ bool insert_metawriter(std::string const& name, metawriter_ptr const& writer); - /*! \brief Remove a meta-writer from the map. + /*! \brief Remove a metawriter from the map. * @param name The name of the writer. */ void remove_metawriter(const std::string& name); - /*! \brief Find a meta-writer. + /*! \brief Find a metawriter. * @param name The name of the writer. * @return The writer if found. If not found return 0. */ metawriter_ptr find_metawriter(std::string const& name) const; + + /*! \brief Get all metawriters. + * @return Const reference to metawriters. + */ + std::map const& metawriters() const; + + /*! \brief Get first iterator in metawriters. + * @return Constant metawriter iterator. + */ + const_metawriter_iterator begin_metawriters() const; + + /*! \brief Get last iterator in metawriters. + * @return Constant metawriter iterator. + */ + const_metawriter_iterator end_metawriters() const; /*! \brief Insert a fontset into the map. * @param name The name of the fontset. diff --git a/include/mapnik/metawriter.hpp b/include/mapnik/metawriter.hpp index 92a4400d2..c7f596783 100644 --- a/include/mapnik/metawriter.hpp +++ b/include/mapnik/metawriter.hpp @@ -27,24 +27,48 @@ // Mapnik #include #include -#include // Boost #include #include +#include + +// STL +#include +#include namespace mapnik { +/** All properties to be output by a metawriter. */ +typedef std::set metawriter_properties; + /** Abstract baseclass for all metawriter classes. */ class metawriter { public: + metawriter(metawriter_properties dflt_properties) : dflt_properties_(dflt_properties) {} virtual ~metawriter() {}; - virtual void add_box(box2d box, Feature const &feature, proj_transform const& prj_trans, CoordTransform const &t, expression_ptr expression)=0; + /** Output a rectangular area. + * \param box Area (in pixel coordinates) + * \param feature The feature being processed + * \param prj_trans Projection transformation + * \param t Cooridnate transformation + * \param properties List of properties to output + */ + virtual void add_box(box2d box, Feature const &feature, + proj_transform const& prj_trans, + CoordTransform const &t, + metawriter_properties const& properties = metawriter_properties())=0; + virtual void start() {}; + virtual void stop() {}; + static metawriter_properties parse_properties(boost::optional str); + protected: + metawriter_properties dflt_properties_; }; typedef boost::shared_ptr metawriter_ptr; +typedef std::pair metawriter_with_properties; }; diff --git a/include/mapnik/metawriter_json.hpp b/include/mapnik/metawriter_json.hpp index 8cec634b7..4d29cc552 100644 --- a/include/mapnik/metawriter_json.hpp +++ b/include/mapnik/metawriter_json.hpp @@ -35,12 +35,17 @@ namespace mapnik { class metawriter_json : public metawriter, private boost::noncopyable { public: - metawriter_json(std::string fn, expression_ptr dflt_expr=expression_ptr()); + metawriter_json(metawriter_properties dflt_properties, std::string fn); ~metawriter_json(); - virtual void add_box(box2d box, Feature const &feature, proj_transform const& prj_trans, CoordTransform const &t, expression_ptr expression); + virtual void add_box(box2d box, Feature const &feature, + proj_transform const& prj_trans, + CoordTransform const& t, + metawriter_properties const& properties); + virtual void start(); + virtual void stop(); private: std::fstream f; - expression_ptr dflt_expr_; + std::string fn_; int count; }; diff --git a/include/mapnik/symbolizer.hpp b/include/mapnik/symbolizer.hpp index 728cbe5bd..30690416f 100644 --- a/include/mapnik/symbolizer.hpp +++ b/include/mapnik/symbolizer.hpp @@ -27,7 +27,6 @@ // mapnik #include #include -#include #include // boost @@ -44,7 +43,7 @@ class MAPNIK_DECL symbolizer_base { * * expression can be empty the default expression of * the writer is used in this case. */ - void add_metawriter(std::string name, expression_ptr expression); + void add_metawriter(std::string name, metawriter_properties const& properties); /** Cache metawriter objects to avoid repeated lookups while processing. * * This function has to be called before the symbolizer is used, because @@ -56,9 +55,13 @@ class MAPNIK_DECL symbolizer_base { * * This functions requires that cache_metawriters() was called first. */ - std::pair get_metawriter() const; + metawriter_with_properties get_metawriter() const; + /** Get properties needed for metawriter. + * \note This function is a helperfunction for class attribute_collector. + */ + metawriter_properties const& get_metawriter_properties() const { return properties_;} private: - expression_ptr expression_; + metawriter_properties properties_; std::string writer_name_; metawriter_ptr writer_ptr_; }; diff --git a/src/agg/process_point_symbolizer.cpp b/src/agg/process_point_symbolizer.cpp index 9fa79b68a..ff4ca7fe5 100644 --- a/src/agg/process_point_symbolizer.cpp +++ b/src/agg/process_point_symbolizer.cpp @@ -114,7 +114,7 @@ void agg_renderer::process(point_symbolizer const& sym, { svg_renderer.render(*ras_ptr, sl, ren, tr, renb.clip_box(), sym.get_opacity()); detector_.insert(extent); - std::pair writer = sym.get_metawriter(); + metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) { writer.first->add_box(extent, feature, prj_trans, t_, writer.second); @@ -155,7 +155,7 @@ void agg_renderer::process(point_symbolizer const& sym, { pixmap_.set_rectangle_alpha2(*(*data), px, py, sym.get_opacity()); detector_.insert(label_ext); - std::pair writer = sym.get_metawriter(); + metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) { writer.first->add_box(label_ext, feature, prj_trans, t_, writer.second); diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index f72110d05..7b5e53058 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -777,7 +777,7 @@ void cairo_renderer_base::process(point_symbolizer const& sym, context.add_image(px, py, *(*data), sym.get_opacity()); detector_.insert(label_ext); - std::pair writer = sym.get_metawriter(); + metawriter_with_properties writer = sym.get_metawriter(); if (writer.first) { writer.first->add_box(label_ext, feature, prj_trans, t_, writer.second); diff --git a/src/load_map.cpp b/src/load_map.cpp index d19c3fc56..b0f6cf83e 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -336,10 +336,9 @@ void map_parser::parse_style( Map & map, ptree const & sty ) } catch (const config_error & ex) { if ( ! name.empty() ) { - ex.append_context(string("in style '") + name + "' in map '" + filename_ + "'"); - } else { - ex.append_context(string("in map '") + filename_ + "'"); + ex.append_context(string("in style '") + name + "'"); } + ex.append_context(string("in map '") + filename_ + "'"); throw; } } @@ -354,17 +353,16 @@ void map_parser::parse_metawriter(Map & map, ptree const & pt) string type = get_attr(pt, "type"); if (type == "json") { string file = get_attr(pt, "file"); - writer = metawriter_ptr(new metawriter_json(file)); + writer = metawriter_ptr(new metawriter_json(metawriter_properties(/*TODO*/), file)); } else { throw config_error(string("Unknown type '") + type + "'"); } map.insert_metawriter(name, writer); } catch (const config_error & ex) { if (!name.empty()) { - ex.append_context(string("in meta writer '") + name + "' in map '" + filename_ + "'"); - } else { - ex.append_context(string("in map '") + filename_ + "'"); + ex.append_context(string("in meta writer '") + name + "'"); } + ex.append_context(string("in map '") + filename_ + "'"); throw; } } @@ -403,8 +401,9 @@ void map_parser::parse_fontset( Map & map, ptree const & fset ) fontsets_.insert(pair(name, fontset)); } catch (const config_error & ex) { if ( ! name.empty() ) { - ex.append_context(string("in FontSet '") + name + "' in map '" + filename_ + "')"); + ex.append_context(string("in FontSet '") + name + "'"); } + ex.append_context(string("in map '") + filename_ + "'"); throw; } } @@ -705,12 +704,7 @@ void map_parser::parse_metawriter_in_symbolizer(symbolizer_base &sym, ptree cons optional writer = get_opt_attr(pt, "meta-writer"); if (!writer) return; optional output = get_opt_attr(pt, "meta-output"); - expression_ptr expression; - if (output) { - expression = parse_expression(*output, "utf8"); - } - clog << "adding metawriter " << *writer << "\n"; - sym.add_metawriter(*writer, expression); + sym.add_metawriter(*writer, metawriter::parse_properties(output)); } void map_parser::parse_point_symbolizer( rule_type & rule, ptree const & sym ) diff --git a/src/map.cpp b/src/map.cpp index 71c676418..0d3d31222 100644 --- a/src/map.cpp +++ b/src/map.cpp @@ -169,6 +169,21 @@ metawriter_ptr Map::find_metawriter(std::string const& name) const return metawriter_ptr(); } +std::map const& Map::metawriters() const +{ + return metawriters_; +} + +Map::const_metawriter_iterator Map::begin_metawriters() const +{ + return metawriters_.begin(); +} + +Map::const_metawriter_iterator Map::end_metawriters() const +{ + return metawriters_.end(); +} + bool Map::insert_fontset(std::string const& name, font_set const& fontset) { return fontsets_.insert(make_pair(name, fontset)).second; diff --git a/src/metawriter.cpp b/src/metawriter.cpp index 765ab1184..56151dd07 100644 --- a/src/metawriter.cpp +++ b/src/metawriter.cpp @@ -24,43 +24,66 @@ // Mapnik #include #include -#include + +// Boost +#include +#include // STL #include +#include namespace mapnik { - metawriter_json::metawriter_json(std::string fn, expression_ptr dflt_expr) : dflt_expr_(dflt_expr), count(0) +metawriter_json::metawriter_json(metawriter_properties dflt_properties, std::string fn) + : metawriter(dflt_properties), fn_(fn), count(0) { - f.open(fn.c_str(), std::fstream::out | std::fstream::trunc); - if (f.fail()) perror((std::string("Failed to open file ") + fn).c_str()); - f << "{ \"type\": \"FeatureCollection\", \"features\": [\n"; + } metawriter_json::~metawriter_json() { - if (f.is_open()) { + stop(); +} + +void metawriter_json::start() +{ + if (f.is_open()) + { + std::cerr << "ERROR: GeoJSON metawriter is already active!\n"; + } + f.open(fn_.c_str(), std::fstream::out | std::fstream::trunc); + if (f.fail()) perror((std::string("Failed to open file ") + fn_).c_str()); + f << "{ \"type\": \"FeatureCollection\", \"features\": [\n"; +} + +void metawriter_json::stop() +{ + if (f.is_open()) + { f << " ] }\n"; f.close(); } - std::clog << "Destroying metawriter\n"; } -void metawriter_json::add_box(box2d box, Feature const &feature, proj_transform const& prj_trans, CoordTransform const &t, expression_ptr expression) +void metawriter_json::add_box(box2d box, Feature const &feature, + proj_transform const& prj_trans, CoordTransform const &t, + const metawriter_properties& properties) { - expression_ptr e; - if (expression) { - e = expression; - } else { - e = dflt_expr_; + metawriter_properties props; + if (properties.empty()) + { + props = dflt_properties_; } - if (!e) { - std::clog << "WARNING: No expression available for JSON metawriter.\n"; + else + { + props = properties; + } + if (props.empty()) + { + std::cerr << "WARNING: No expression available for GeoJSON metawriter.\n"; return; } - value_type result = boost::apply_visitor(evaluate(feature),*e); - UnicodeString text = result.to_unicode(); /* Coordinate transform in renderer: input: layer srs @@ -86,15 +109,38 @@ void metawriter_json::add_box(box2d box, Feature const &feature, proj_tr double maxx = box.maxx(); double maxy = box.maxy(); - if (count++) { - f << ",\n"; - } + if (count++) f << ",\n"; f << std::fixed << std::setprecision(8) << "{ \"type\": \"Feature\",\n \"geometry\": { \"type\": \"Polygon\",\n \"coordinates\": [ [ [" << minx << ", " << miny << "], [" << maxx << ", " << miny << "], [" << maxx << ", " << maxy << "], [" << minx << ", " << maxy << "] ] ] }," << - "\n \"properties\": {" << text << "} }"; + "\n \"properties\": {"; + std::map fprops = feature.props(); + int i = 0; + BOOST_FOREACH(std::string p, props) + { + std::map::const_iterator itr = fprops.find(p); + std::string text; + if (itr != fprops.end()) + { + //Property found + text = boost::replace_all_copy(boost::replace_all_copy(itr->second.to_string(), "\\", "\\\\"), "\"", "\\\""); + } + if (i++) f << ","; + f << "\n \"" << p << "\":\"" << text << "\""; + } + + f << "\n} }"; } +metawriter_properties metawriter::parse_properties(boost::optional str) +{ + metawriter_properties properties; + if (str) { + std::string str_ = boost::algorithm::erase_all_copy(*str, " "); + boost::split(properties, str_, boost::is_any_of(","), boost::token_compress_on); + } + return properties; +} }; diff --git a/src/symbolizer.cpp b/src/symbolizer.cpp index 97def9b48..ac1ba6b16 100644 --- a/src/symbolizer.cpp +++ b/src/symbolizer.cpp @@ -27,29 +27,20 @@ namespace mapnik { -void symbolizer_base::add_metawriter(std::string name, expression_ptr expression) +void symbolizer_base::add_metawriter(std::string name, metawriter_properties const& properties) { - expression_ = expression; + properties_ = properties; writer_name_ = name; -#ifdef MAPNIK_DEBUG - std::clog << "adding metawriter" << name << "\n"; -#endif } void symbolizer_base::cache_metawriters(Map const &m) { -#ifdef MAPNIK_DEBUG - std::clog << "Caching metawriters\n"; -#endif writer_ptr_ = m.find_metawriter(writer_name_); -#ifdef MAPNIK_DEBUG - std::clog << writer_ptr_ << "name:" << writer_name_ << "\n"; -#endif } -std::pair symbolizer_base::get_metawriter() const +metawriter_with_properties symbolizer_base::get_metawriter() const { - return std::pair(writer_ptr_, expression_); + return metawriter_with_properties(writer_ptr_, properties_); } symbolizer_with_image::symbolizer_with_image(path_expression_ptr file)