diff --git a/bindings/python/mapnik_python.cpp b/bindings/python/mapnik_python.cpp index bb9198c2e..1f26e9a0b 100644 --- a/bindings/python/mapnik_python.cpp +++ b/bindings/python/mapnik_python.cpp @@ -60,6 +60,7 @@ void export_polygon_symbolizer(); void export_polygon_pattern_symbolizer(); void export_raster_symbolizer(); void export_text_symbolizer(); +void export_text_placement(); void export_shield_symbolizer(); void export_font_engine(); void export_projection(); @@ -425,6 +426,7 @@ BOOST_PYTHON_MODULE(_mapnik) export_polygon_symbolizer(); export_polygon_pattern_symbolizer(); export_raster_symbolizer(); + export_text_placement(); export_text_symbolizer(); export_shield_symbolizer(); export_font_engine(); diff --git a/bindings/python/mapnik_shield_symbolizer.cpp b/bindings/python/mapnik_shield_symbolizer.cpp index e3180845e..5da6bad6a 100644 --- a/bindings/python/mapnik_shield_symbolizer.cpp +++ b/bindings/python/mapnik_shield_symbolizer.cpp @@ -37,6 +37,7 @@ using mapnik::path_expression_ptr; using mapnik::guess_type; using mapnik::expression_ptr; using mapnik::parse_path; +using mapnik::position; namespace { @@ -44,8 +45,8 @@ using namespace boost::python; tuple get_shield_displacement(const shield_symbolizer& s) { - boost::tuple pos = s.get_shield_displacement(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); + position const& pos = s.get_shield_displacement(); + return boost::python::make_tuple(pos.first, pos.second); } void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg) @@ -55,8 +56,8 @@ void set_shield_displacement(shield_symbolizer & s, boost::python::tuple arg) tuple get_text_displacement(const shield_symbolizer& t) { - boost::tuple pos = t.get_displacement(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); + position const& pos = t.get_displacement(); + return boost::python::make_tuple(pos.first, pos.second); } void set_text_displacement(shield_symbolizer & t, boost::python::tuple arg) @@ -223,9 +224,6 @@ void export_shield_symbolizer() .add_property("wrap_before", &shield_symbolizer::get_wrap_before, &shield_symbolizer::set_wrap_before) - .add_property("no_text", - &shield_symbolizer::get_no_text, - &shield_symbolizer::set_no_text) .add_property("unlock_image", &shield_symbolizer::get_unlock_image, &shield_symbolizer::set_unlock_image) diff --git a/bindings/python/mapnik_text_placement.cpp b/bindings/python/mapnik_text_placement.cpp new file mode 100644 index 000000000..74f7d8d73 --- /dev/null +++ b/bindings/python/mapnik_text_placement.cpp @@ -0,0 +1,115 @@ +/***************************************************************************** + * + * This file is part of Mapnik (c++ mapping toolkit) + * + * Copyright (C) 2006 Artem Pavlenko, Jean-Francois Doyon + * + * 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 + * + *****************************************************************************/ +//$Id$ + +#include + +#include +#include "mapnik_enumeration.hpp" +#include + +using namespace mapnik; +//using mapnik::color; +//using mapnik::text_symbolizer; +//using mapnik::expr_node; +//using mapnik::expression_ptr; +//using mapnik::to_expression_string; + +namespace { +using namespace boost::python; + +tuple get_displacement(text_symbolizer_properties const& t) +{ + return boost::python::make_tuple(t.displacement.first, t.displacement.second); +} + +void set_displacement(text_symbolizer_properties &t, boost::python::tuple arg) +{ + double x = extract(arg[0]); + double y = extract(arg[0]); + t.displacement = std::make_pair(x, y); +} + +} + +void export_text_placement() +{ + using namespace boost::python; + + enumeration_("label_placement") + .value("LINE_PLACEMENT",LINE_PLACEMENT) + .value("POINT_PLACEMENT",POINT_PLACEMENT) + .value("VERTEX_PLACEMENT",VERTEX_PLACEMENT) + .value("INTERIOR_PLACEMENT",INTERIOR_PLACEMENT) + ; + enumeration_("vertical_alignment") + .value("TOP",V_TOP) + .value("MIDDLE",V_MIDDLE) + .value("BOTTOM",V_BOTTOM) + .value("AUTO",V_AUTO) + ; + + enumeration_("horizontal_alignment") + .value("LEFT",H_LEFT) + .value("MIDDLE",H_MIDDLE) + .value("RIGHT",H_RIGHT) + .value("AUTO",H_AUTO) + ; + + enumeration_("justify_alignment") + .value("LEFT",J_LEFT) + .value("MIDDLE",J_MIDDLE) + .value("RIGHT",J_RIGHT) + ; + + enumeration_("text_transform") + .value("NONE",NONE) + .value("UPPERCASE",UPPERCASE) + .value("LOWERCASE",LOWERCASE) + .value("CAPITALIZE",CAPITALIZE) + ; + + + class_("TextSymbolizerProperties") + .def_readwrite("orientation", &text_symbolizer_properties::orientation) + .add_property("displacement", + &get_displacement, + &set_displacement) + .def_readwrite("label_placement", &text_symbolizer_properties::label_placement) + .def_readwrite("horizontal_alignment", &text_symbolizer_properties::halign) + .def_readwrite("justify_alignment", &text_symbolizer_properties::jalign) + .def_readwrite("vertical_alignment", &text_symbolizer_properties::valign) + .def_readwrite("label_spacing", &text_symbolizer_properties::label_spacing) + .def_readwrite("label_position_tolerance", &text_symbolizer_properties::label_position_tolerance) + .def_readwrite("avoid_edges", &text_symbolizer_properties::avoid_edges) + .def_readwrite("minimum_distance", &text_symbolizer_properties::minimum_distance) + .def_readwrite("minimum_padding", &text_symbolizer_properties::minimum_padding) + .def_readwrite("minimum_path_length", &text_symbolizer_properties::minimum_path_length) + .def_readwrite("maximum_angle_char_delta", &text_symbolizer_properties::max_char_angle_delta) + .def_readwrite("force_odd_labels", &text_symbolizer_properties::force_odd_labels) + .def_readwrite("allow_overlap", &text_symbolizer_properties::allow_overlap) + .def_readwrite("text_ratio", &text_symbolizer_properties::text_ratio) + .def_readwrite("wrap_width", &text_symbolizer_properties::wrap_width) + /* TODO: text_processor */ + /* from_xml, to_xml operate on mapnik's internal XML tree and don't make sense in python.*/ + ; +} diff --git a/bindings/python/mapnik_text_symbolizer.cpp b/bindings/python/mapnik_text_symbolizer.cpp index 31515f11a..ccb5b99e0 100644 --- a/bindings/python/mapnik_text_symbolizer.cpp +++ b/bindings/python/mapnik_text_symbolizer.cpp @@ -39,8 +39,8 @@ using namespace boost::python; tuple get_text_displacement(const text_symbolizer& t) { - position pos = t.get_displacement(); - return boost::python::make_tuple(boost::get<0>(pos),boost::get<1>(pos)); + mapnik::position const& pos = t.get_displacement(); + return boost::python::make_tuple(pos.first, pos.second); } void set_text_displacement(text_symbolizer & t, boost::python::tuple arg) diff --git a/docs/textrendering.gv b/docs/textrendering.gv index 90a897a6a..26bf9fb1f 100644 --- a/docs/textrendering.gv +++ b/docs/textrendering.gv @@ -1,13 +1,14 @@ -#process with: dot textrendering.gv -Tpng > textrendering.png +/* process with: dot textrendering.gv -Tsvg > textrendering.svg */ digraph textrendering { -# Classes without important virtual members: Round -# Classes with important virtual members: Rect -# Pointers [style=dashed] -# Red: function is called + /* Classes without important virtual members: Round + Classes with important virtual members: Rect + Pointers [style=dashed] */ + + Renderer [color=red] + rankdir="TD"; text_placements[shape=box] text_placement_info[shape=box] - Renderer - + node_ -> text_processor [label="tree_", style=dashed] TextSymbolizer -> text_placements [label="placement_options_", style=dashed] text_placements -> text_symbolizer_properties [label="properties"] text_placements -> text_placement_info [label="get_placement_info()", style=dashed] @@ -16,9 +17,24 @@ digraph textrendering { text_placement_info -> text_placement_info [label="next()"] text_symbolizer_properties -> text_processor [label="processor"] text_processor -> processed_text [label="process()", style=dashed] - processed_text -> string_info [label="get_string_info()"] + processed_text -> string_info [label="get_string_info()", style=dashed] text_path -> Renderer [color=red, label="used by"] - Renderer -> text_placement_info [color=red, label="init()"] - Renderer -> processed_text [color=red, label="initializes"] + processed_text -> Renderer [color=red, label="owned by"] + Renderer -> text_symbolizer_helper [color=red, label="creates"] + text_symbolizer_helper -> placement_finder [color=red, label="creates"] + placement_finder -> text_path [color=red, label="creates"] + string_info -> placement_finder [color=red, label="used by"] + text_processor -> Renderer [color=red, label="called by"] + text_placement_info -> Renderer [color=red, label="used by"] + + node_[label="node"] + node_ -> text_node [style=dashed] + node_ -> list_node [style=dashed] + node_ -> format_node [style=dashed] + list_node -> text_node [style=dashed] + list_node -> format_node [style=dashed] + format_node -> text_node [style=dashed] + { rank=same; text_path text_symbolizer_helper } + { rank=same; node_ TextSymbolizer} } diff --git a/docs/textrendering.png b/docs/textrendering.png new file mode 100644 index 000000000..ee7b33c4b Binary files /dev/null and b/docs/textrendering.png differ diff --git a/docs/textrendering.svg b/docs/textrendering.svg new file mode 100644 index 000000000..9a3e2f834 --- /dev/null +++ b/docs/textrendering.svg @@ -0,0 +1,226 @@ + + + + + + +textrendering + + +Renderer + +Renderer + + +text_symbolizer_helper + +text_symbolizer_helper + + +Renderer->text_symbolizer_helper + + +creates + + +text_placements + +text_placements + + +text_placement_info + +text_placement_info + + +text_placements->text_placement_info + + +get_placement_info() + + +text_symbolizer_properties + +text_symbolizer_properties + + +text_placements->text_symbolizer_properties + + +properties + + +text_placement_info->Renderer + + +used by + + +text_placement_info->text_placement_info + + +next() + + +text_placement_info->text_symbolizer_properties + + +properties + + +text_path + +text_path + + +text_placement_info->text_path + + +placements + + +node_ + +node + + +text_processor + +text_processor + + +node_->text_processor + + +tree_ + + +text_node + +text_node + + +node_->text_node + + + + +list_node + +list_node + + +node_->list_node + + + + +format_node + +format_node + + +node_->format_node + + + + +text_processor->Renderer + + +called by + + +processed_text + +processed_text + + +text_processor->processed_text + + +process() + + +TextSymbolizer + +TextSymbolizer + + +TextSymbolizer->text_placements + + +placement_options_ + + +text_symbolizer_properties->text_processor + + +processor + + +text_path->Renderer + + +used by + + +processed_text->Renderer + + +owned by + + +string_info + +string_info + + +processed_text->string_info + + +get_string_info() + + +placement_finder + +placement_finder + + +string_info->placement_finder + + +used by + + +text_symbolizer_helper->placement_finder + + +creates + + +placement_finder->text_path + + +creates + + +list_node->text_node + + + + +list_node->format_node + + + + +format_node->text_node + + + + + diff --git a/include/mapnik/placement_finder.hpp b/include/mapnik/placement_finder.hpp index 37a0219f6..f565a90f6 100644 --- a/include/mapnik/placement_finder.hpp +++ b/include/mapnik/placement_finder.hpp @@ -36,20 +36,17 @@ public: placement_finder(text_placement_info &p, string_info &info, DetectorT & detector); placement_finder(text_placement_info &p, string_info &info, DetectorT & detector, box2d const& extent); - //Try place a single label at the given point + /** Try place a single label at the given point. */ void find_point_placement(double pos_x, double pos_y, double angle=0.0); - //Iterate over the given path, placing point labels with respect to label_spacing + /** Iterate over the given path, placing point labels with respect to label_spacing. */ template void find_point_placements(T & path); - //Iterate over the given path, placing line-following labels with respect to label_spacing + /** Iterate over the given path, placing line-following labels with respect to label_spacing. */ template void find_line_placements(T & path); - //Find placement, automatically select point or line placement - void find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans); - void update_detector(); void clear(); @@ -65,14 +62,14 @@ private: // otherwise it will autodetect the orientation. // If >= 50% of the characters end up upside down, it will be retried the other way. // RETURN: 1/-1 depending which way up the string ends up being. - std::auto_ptr get_placement_offset(const std::vector & path_positions, + std::auto_ptr get_placement_offset(const std::vector & path_positions, const std::vector & path_distances, int & orientation, unsigned index, double distance); - ///Tests wether the given placement_element be placed without a collision + ///Tests wether the given text_path be placed without a collision // Returns true if it can // NOTE: This edits p.envelopes so it can be used afterwards (you must clear it otherwise) - bool test_placement(const std::auto_ptr & current_placement, const int & orientation); + bool test_placement(const std::auto_ptr & current_placement, const int & orientation); ///Does a line-circle intersect calculation // NOTE: Follow the strict pre conditions @@ -87,7 +84,7 @@ private: void find_line_breaks(); void init_string_size(); void init_alignment(); - void adjust_position(placement_element *current_placement, double label_x, double label_y); + void adjust_position(text_path *current_placement, double label_x, double label_y); ///General Internals DetectorT & detector_; diff --git a/include/mapnik/shield_symbolizer.hpp b/include/mapnik/shield_symbolizer.hpp index b5aaf94e8..06b12fb2e 100644 --- a/include/mapnik/shield_symbolizer.hpp +++ b/include/mapnik/shield_symbolizer.hpp @@ -50,15 +50,12 @@ struct MAPNIK_DECL shield_symbolizer : public text_symbolizer, bool get_unlock_image() const; // image is not locked to the text placement void set_unlock_image(bool unlock_image); - bool get_no_text() const; // do no render text - void set_no_text(bool unlock_image); void set_shield_displacement(double shield_dx,double shield_dy); - boost::tuple const& get_shield_displacement() const; + position const& get_shield_displacement() const; private: bool unlock_image_; - bool no_text_; - boost::tuple shield_displacement_; + position shield_displacement_; }; } diff --git a/include/mapnik/symbolizer_helpers.hpp b/include/mapnik/symbolizer_helpers.hpp index bc7eb8467..6c9dcdcb5 100644 --- a/include/mapnik/symbolizer_helpers.hpp +++ b/include/mapnik/symbolizer_helpers.hpp @@ -23,163 +23,147 @@ #define SYMBOLIZER_HELPERS_HPP #include +#include #include #include #include #include +#include +#include #include -#include + namespace mapnik { - -struct greater_bbox_comp -{ - bool operator() (geometry_type const* g0, geometry_type const* g1) const - { - box2d b0 = g0->envelope(); - box2d b1 = g1->envelope(); - return b0.width()*b0.height() > b1.width()*b1.height(); - } - -}; - +/** Helper object that does all the TextSymbolizer placment finding + * work except actually rendering the object. */ template class text_symbolizer_helper { public: - text_symbolizer_helper(unsigned width, + text_symbolizer_helper(text_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans, + unsigned width, unsigned height, double scale_factor, CoordTransform const &t, FaceManagerT &font_manager, - DetectorT &detector) : - width_(width), - height_(height), - scale_factor_(scale_factor), - t_(t), - font_manager_(font_manager), - detector_(detector), - text_() - { + DetectorT &detector) + : sym_(sym), + feature_(feature), + prj_trans_(prj_trans), + t_(t), + font_manager_(font_manager), + detector_(detector), + writer_(sym.get_metawriter()), + dims_(0, 0, width, height), + text_(font_manager, scale_factor), + angle_(0.0), + placement_valid_(true) + { + initialize_geometries(); + if (!geometries_to_process_.size()) return; //TODO: Test this + placement_ = sym_.get_placement_options()->get_placement_info( + scale_factor, std::make_pair(width, height), false); + //TODO: has_dimensions? Why? When? + if (writer_.first) placement_->collect_extents = true; + next_placement(); + initialize_points(); + } - } + /** Return next placement. + * If no more placements are found returns null pointer. + */ + text_placement_info_ptr get_placement(); + text_placement_info_ptr get_point_placement(); + text_placement_info_ptr get_line_placement(); +protected: + bool next_placement(); + void initialize_geometries(); + void initialize_points(); - text_placement_info_ptr get_placement(text_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); -private: - bool initialize_geometries(std::vector & geometries_to_process, - text_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans); - - unsigned width_; - unsigned height_; - double scale_factor_; + //Input + text_symbolizer const& sym_; + Feature const& feature_; + proj_transform const& prj_trans_; CoordTransform const &t_; FaceManagerT &font_manager_; DetectorT &detector_; - boost::shared_ptr text_; /*TODO: Use shared pointers for text placement so we don't need to keep a reference here! */ + metawriter_with_properties writer_; + box2d dims_; + + //Processing + processed_text text_; + /* Using list instead of vector, because we delete random elements and need iterators to stay valid. */ + std::list geometries_to_process_; + std::list::iterator geo_itr_; + std::list points_; + std::list::iterator point_itr_; + double angle_; + string_info *info_; + bool placement_valid_; + bool point_placement_; + + //Output + text_placement_info_ptr placement_; }; - template -text_placement_info_ptr text_symbolizer_helper::get_placement( - text_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) +class shield_symbolizer_helper: public text_symbolizer_helper { - - unsigned num_geom = feature.num_geometries(); - if (!num_geom) return text_placement_info_ptr(); //Nothing to do - - std::vector geometries_to_process; - - if (!initialize_geometries(geometries_to_process,sym, feature, prj_trans)) - return text_placement_info_ptr(); - - text_ = boost::shared_ptr(new processed_text(font_manager_, scale_factor_)); - metawriter_with_properties writer = sym.get_metawriter(); - - box2d dims(0, 0, width_, height_); - - text_placement_info_ptr placement = sym.get_placement_options()->get_placement_info(); - placement->init(scale_factor_, width_, height_); - if (writer.first) - placement->collect_extents = true; - - - - while (placement->next()) +public: + shield_symbolizer_helper(shield_symbolizer const& sym, + Feature const& feature, + proj_transform const& prj_trans, + unsigned width, + unsigned height, + double scale_factor, + CoordTransform const &t, + FaceManagerT &font_manager, + DetectorT &detector) : + text_symbolizer_helper(sym, feature, prj_trans, width, height, scale_factor, t, font_manager, detector), + sym_(sym) { - text_processor &processor = placement->properties.processor; - text_symbolizer_properties const& p = placement->properties; - /* TODO: Simplify this. */ - text_->clear(); - processor.process(*text_, feature); - string_info &info = text_->get_string_info(); - /* END TODO */ - // text rotation - double angle = 0.0; - if (p.orientation) - { - angle = boost::apply_visitor(evaluate(feature),*(p.orientation)).to_double(); - } - placement_finder finder(*placement, info, detector_, dims); - - BOOST_FOREACH( geometry_type * geom, geometries_to_process ) - { - finder.find_placement(angle, *geom, t_, prj_trans); - //if (!placement->placements.size()) - // continue; - //if (writer.first) writer.first->add_text(*placement, font_manager_, feature, t_, writer.second); - //return placement; - } - return placement; - } - return text_placement_info_ptr(); -} - -template -bool text_symbolizer_helper::initialize_geometries( - std::vector & geometries_to_process, - text_symbolizer const& sym, - Feature const& feature, - proj_transform const& prj_trans) -{ - unsigned num_geom = feature.num_geometries(); - - for (unsigned i=0; i 0) - { - // TODO - find less costly method than fetching full envelope - box2d gbox = t_.forward(geom.envelope(),prj_trans); - if (gbox.width() < sym.get_minimum_path_length()) - { - continue; - } - } - // TODO - calculate length here as well - geometries_to_process.push_back(const_cast(&geom)); + init_marker(); } - std::sort(geometries_to_process.begin(), geometries_to_process.end(), greater_bbox_comp()); - - if (!geometries_to_process.size() > 0) - { - // early return to avoid significant overhead of rendering setup - return false; - } - return true; -} - -} + text_placement_info_ptr get_placement(); + std::pair get_marker_position(text_path &p); + marker &get_marker() const; + agg::trans_affine const& get_transform() const; +protected: + text_placement_info_ptr get_point_placement(); + text_placement_info_ptr get_line_placement(); + void init_marker(); + shield_symbolizer const& sym_; + box2d marker_ext_; + boost::optional marker_; + agg::trans_affine transform_; + int marker_w_; + int marker_h_; + int marker_x_; + int marker_y_; + // F***ing templates... + // http://womble.decadent.org.uk/c++/template-faq.html#base-lookup + using text_symbolizer_helper::geometries_to_process_; + using text_symbolizer_helper::placement_; + using text_symbolizer_helper::next_placement; + using text_symbolizer_helper::info_; + using text_symbolizer_helper::geo_itr_; + using text_symbolizer_helper::point_itr_; + using text_symbolizer_helper::points_; + using text_symbolizer_helper::writer_; + using text_symbolizer_helper::font_manager_; + using text_symbolizer_helper::feature_; + using text_symbolizer_helper::t_; + using text_symbolizer_helper::detector_; + using text_symbolizer_helper::dims_; + using text_symbolizer_helper::prj_trans_; + using text_symbolizer_helper::placement_valid_; + using text_symbolizer_helper::point_placement_; + using text_symbolizer_helper::angle_; +}; +} //namespace #endif // SYMBOLIZER_HELPERS_HPP diff --git a/include/mapnik/text_placements.hpp b/include/mapnik/text_placements.hpp index fdb6530dc..0a5d903f8 100644 --- a/include/mapnik/text_placements.hpp +++ b/include/mapnik/text_placements.hpp @@ -46,9 +46,8 @@ namespace mapnik { class text_placements; -typedef text_path placement_element; - -typedef boost::tuple position; +typedef std::pair position; +typedef std::pair dimension_type; enum label_placement_enum { POINT_PLACEMENT, @@ -97,7 +96,7 @@ struct text_symbolizer_properties { text_symbolizer_properties(); /** Load all values and also the ```processor``` object from XML ptree. */ - void set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); + void from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); /** Save all values to XML ptree (but does not create a new parent node!). */ void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_symbolizer_properties const &dfl=text_symbolizer_properties()) const; @@ -136,7 +135,8 @@ class text_placement_info : boost::noncopyable public: /** Constructor. Takes the parent text_placements object as a parameter * to read defaults from it. */ - text_placement_info(text_placements const* parent); + text_placement_info(text_placements const* parent, + double scale_factor_, dimension_type dim, bool has_dimensions_); /** Get next placement. * This function is also called before the first placement is tried. * Each class has to return at least one position! @@ -160,7 +160,7 @@ public: /* TODO: Don't know what this is used for. */ bool has_dimensions; /* TODO: Don't know what this is used for. */ - std::pair dimensions; + dimension_type dimensions; /** Set scale factor. */ void set_scale_factor(double factor) { scale_factor = factor; } /** Get scale factor. */ @@ -177,10 +177,16 @@ public: //Output by placement finder /** Bounding box of all texts placed. */ box2d extents; + /** Additional boxes to take into account when finding placement. + * Used for finding line placements where multiple placements are returned. + * Boxes are relative to starting point of current placement. + */ + std::vector > additional_boxes; + /* TODO */ std::queue< box2d > envelopes; - /* TODO */ - boost::ptr_vector placements; + /** Used to return all placements found. */ + boost::ptr_vector placements; }; typedef boost::shared_ptr text_placement_info_ptr; @@ -206,7 +212,9 @@ public: * return text_placement_info_ptr(new text_placement_info_XXX(this)); * } */ - virtual text_placement_info_ptr get_placement_info() const =0; + virtual text_placement_info_ptr get_placement_info( + double scale_factor_, dimension_type dim, + bool has_dimensions_) const =0; /** Get a list of all expressions used in any placement. * This function is used to collect attributes. */ @@ -229,7 +237,8 @@ class text_placements_info_dummy; class MAPNIK_DECL text_placements_dummy: public text_placements { public: - text_placement_info_ptr get_placement_info() const; + text_placement_info_ptr get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const; friend class text_placement_info_dummy; }; @@ -237,8 +246,10 @@ public: class MAPNIK_DECL text_placement_info_dummy : public text_placement_info { public: - text_placement_info_dummy(text_placements_dummy const* parent) : text_placement_info(parent), - state(0), parent_(parent) {} + text_placement_info_dummy(text_placements_dummy const* parent, + double scale_factor, dimension_type dim, bool has_dimensions) + : text_placement_info(parent, scale_factor, dim, has_dimensions), + state(0), parent_(parent) {} bool next(); private: unsigned state; diff --git a/include/mapnik/text_placements_list.hpp b/include/mapnik/text_placements_list.hpp index e43fe724a..3453c3010 100644 --- a/include/mapnik/text_placements_list.hpp +++ b/include/mapnik/text_placements_list.hpp @@ -33,7 +33,8 @@ class text_placements_list: public text_placements { public: text_placements_list(); - text_placement_info_ptr get_placement_info() const; + text_placement_info_ptr get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const; virtual std::set get_all_expressions(); text_symbolizer_properties & add(); text_symbolizer_properties & get(unsigned i); @@ -48,8 +49,10 @@ private: class text_placement_info_list : public text_placement_info { public: - text_placement_info_list(text_placements_list const* parent) : - text_placement_info(parent), state(0), parent_(parent) {} + text_placement_info_list(text_placements_list const* parent, + double scale_factor, dimension_type dim, bool has_dimensions) : + text_placement_info(parent, scale_factor, dim, has_dimensions), + state(0), parent_(parent) {} bool next(); private: unsigned state; diff --git a/include/mapnik/text_placements_simple.hpp b/include/mapnik/text_placements_simple.hpp index 5ad35f52e..575dee4cd 100644 --- a/include/mapnik/text_placements_simple.hpp +++ b/include/mapnik/text_placements_simple.hpp @@ -49,9 +49,10 @@ class text_placements_simple: public text_placements public: text_placements_simple(); text_placements_simple(std::string positions); - text_placement_info_ptr get_placement_info() const; + text_placement_info_ptr get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const; void set_positions(std::string positions); - std::string const& get_positions() const; + std::string get_positions(); private: std::string positions_; std::vector direction_; @@ -64,8 +65,12 @@ private: class text_placement_info_simple : public text_placement_info { public: - text_placement_info_simple(text_placements_simple const* parent) : - text_placement_info(parent), state(0), position_state(0), parent_(parent) {} + text_placement_info_simple(text_placements_simple const* parent, + double scale_factor, dimension_type dim, bool has_dimensions) + : text_placement_info(parent, scale_factor, dim, has_dimensions), + state(0), position_state(0), parent_(parent) + { + } bool next(); protected: bool next_position_only(); diff --git a/include/mapnik/text_processing.hpp b/include/mapnik/text_processing.hpp index 450acb0bb..8bc4dcc1d 100644 --- a/include/mapnik/text_processing.hpp +++ b/include/mapnik/text_processing.hpp @@ -46,14 +46,14 @@ enum text_transform CAPITALIZE, text_transform_MAX }; - DEFINE_ENUM( text_transform_e, text_transform ); + struct char_properties { char_properties(); /** Construct object from XML. */ - void set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); + void from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets); /** Write object to XML ptree. */ void to_xml(boost::property_tree::ptree &node, bool explicit_defaults, char_properties const &dfl=char_properties()) const; std::string face_name; @@ -79,7 +79,8 @@ public: UnicodeString str; }; -class processed_text + +class processed_text : boost::noncopyable { public: processed_text(face_manager & font_manager, double scale_factor); @@ -88,8 +89,8 @@ public: unsigned empty() const { return expr_list_.empty(); } void clear(); typedef std::list expression_list; - expression_list::const_iterator begin(); - expression_list::const_iterator end(); + expression_list::const_iterator begin() const; + expression_list::const_iterator end() const; string_info &get_string_info(); private: expression_list expr_list_; @@ -98,7 +99,86 @@ private: string_info info_; }; -class abstract_token; + +namespace formating { +class node; +typedef boost::shared_ptr node_ptr; +class node +{ +public: + virtual ~node() {} + virtual void to_xml(boost::property_tree::ptree &xml) const; + static node_ptr from_xml(boost::property_tree::ptree const& xml); + virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const = 0; + virtual void add_expressions(std::set &expressions) const; +}; + +class list_node: public node { +public: + list_node() : node(), children_() {} + virtual void to_xml(boost::property_tree::ptree &xml) const; + virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const; + virtual void add_expressions(std::set &expressions) const; + + void push_back(node_ptr n); + void set_children(std::vector const& children); + std::vector const& get_children() const; + void clear(); +private: + std::vector children_; +}; + +class text_node: public node { +public: + text_node(expression_ptr text): node(), text_(text) {} + void to_xml(boost::property_tree::ptree &xml) const; + static node_ptr from_xml(boost::property_tree::ptree const& xml); + virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const; + virtual void add_expressions(std::set &expressions) const; + + void set_text(expression_ptr text); + expression_ptr get_text() const; +private: + expression_ptr text_; +}; + +class format_node: public node { +public: + format_node(); + void to_xml(boost::property_tree::ptree &xml) const; + static node_ptr from_xml(boost::property_tree::ptree const& xml); + virtual void apply(char_properties const& p, Feature const& feature, processed_text &output) const; + + void set_child(node_ptr child); + node_ptr get_child() const; + + void set_face_name(boost::optional face_name); + void set_text_size(boost::optional text_size); + void set_character_spacing(boost::optional character_spacing); + void set_line_spacing(boost::optional line_spacing); + void set_text_opacity(boost::optional opacity); + void set_wrap_before(boost::optional wrap_before); + void set_wrap_char(boost::optional wrap_char); + void set_text_transform(boost::optional text_trans); + void set_fill(boost::optional fill); + void set_halo_fill(boost::optional halo_fill); + void set_halo_radius(boost::optional radius); +private: + boost::optional face_name_; + boost::optional text_size_; + boost::optional character_spacing_; + boost::optional line_spacing_; + boost::optional text_opacity_; + boost::optional wrap_before_; + boost::optional wrap_char_; + boost::optional text_transform_; + boost::optional fill_; + boost::optional halo_fill_; + boost::optional halo_radius_; + node_ptr child_; +}; + +} //namespace formating /** Stores formating information and uses this to produce formated text for a given feature. */ class text_processor @@ -113,22 +193,21 @@ public: /** Takes a feature and produces formated text as output. * The output object has to be created by the caller and passed in for thread safety. */ - void process(processed_text &output, Feature const& feature); + void process(processed_text &output, Feature const& feature) const; /** Automatically create processing instructions for a single expression. */ void set_old_style_expression(expression_ptr expr); - /** Add a new formating token. */ - void push_back(abstract_token *token); + /** Sets new format tree. */ + void set_format_tree(formating::node_ptr tree); + /** Get format tree. */ + formating::node_ptr get_format_tree() const; /** Get a list of all expressions used in any placement. This function is used to collect attributes. */ std::set get_all_expressions() const; /** Default values for char_properties. */ char_properties defaults; -protected: - void from_xml_recursive(boost::property_tree::ptree const& pt, std::map const &fontsets); private: - std::list list_; - bool clear_on_write; //Clear list once + formating::node_ptr tree_; }; -} /* namespace */ +} /* namespace mapnik*/ #endif diff --git a/src/agg/process_shield_symbolizer.cpp b/src/agg/process_shield_symbolizer.cpp index fc74ea2ad..d3db07e25 100644 --- a/src/agg/process_shield_symbolizer.cpp +++ b/src/agg/process_shield_symbolizer.cpp @@ -23,17 +23,13 @@ #include #include -#include #include -#include #include #include #include -#include -#include "agg_basics.h" -#include "agg_rendering_buffer.h" -#include "agg_scanline_u.h" +#include + // boost #include @@ -45,227 +41,28 @@ void agg_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { -#if 0 - typedef coord_transform2 path_type; + shield_symbolizer_helper, + label_collision_detector4> helper( + sym, feature, prj_trans, + width_, height_, + scale_factor_, + t_, font_manager_, *detector_); + text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - placement_options->next(); - placement_options->next_position_only(); - - UnicodeString text; - if( sym.get_no_text() ) - text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render - else - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - text = result.to_unicode(); - } - - if ( sym.get_text_transform() == UPPERCASE) - { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - agg::trans_affine tr; - boost::array const& m = sym.get_transform(); - tr.load_from(&m[0]); - - std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); - boost::optional marker; - if ( !filename.empty() ) - { - marker = marker_cache::instance()->find(filename, true); - } - else - { - marker.reset(boost::make_shared()); - } - - - if (text.length() > 0 && marker) - { - int w = (*marker)->width(); - int h = (*marker)->height(); - - double px0 = - 0.5 * w; - double py0 = - 0.5 * h; - double px1 = 0.5 * w; - double py1 = 0.5 * h; - double px2 = px1; - double py2 = py0; - double px3 = px0; - double py3 = py1; - tr.transform(&px0,&py0); - tr.transform(&px1,&py1); - tr.transform(&px2,&py2); - tr.transform(&px3,&py3); - box2d label_ext (px0, py0, px1, py1); - label_ext.expand_to_include(px2, py2); - label_ext.expand_to_include(px3, py3); - - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } + std::pair marker_pos = helper.get_marker_position(placement->placements[ii]); + render_marker(marker_pos.first, marker_pos.second, helper.get_marker(), helper.get_transform(), sym.get_opacity()); - stroker_ptr strk = font_manager_.get_stroker(); - if (strk && faces->size() > 0) - { - text_renderer ren(pixmap_, faces, *strk); - - ren.set_character_size(sym.get_text_size() * scale_factor_); - ren.set_fill(sym.get_fill()); - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - ren.set_opacity(sym.get_text_opacity()); - - placement_finder finder(*detector_); - - string_info info(text); - - faces->get_string_info(info, text, 0); - - metawriter_with_properties writer = sym.get_metawriter(); - - for (unsigned i = 0; i < feature.num_geometries(); ++i) - { - geometry_type const& geom = feature.get_geometry(i); - if (geom.num_points() > 0 ) - { - path_type path(t_,geom,prj_trans); - - label_placement_enum how_placed = sym.get_label_placement(); - if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT || how_placed == INTERIOR_PLACEMENT) - { - // for every vertex, try and place a shield/text - geom.rewind(0); - placement text_placement(info, sym, scale_factor_, w, h, false); - text_placement.avoid_edges = sym.get_avoid_edges(); - text_placement.allow_overlap = sym.get_allow_overlap(); - if (writer.first) - text_placement.collect_extents =true; // needed for inmem metawriter - position const& pos = sym.get_displacement(); - position const& shield_pos = sym.get_shield_displacement(); - for( unsigned jj = 0; jj < geom.num_points(); jj++ ) - { - double label_x; - double label_y; - double z=0.0; - - if( how_placed == VERTEX_PLACEMENT ) - geom.vertex(&label_x,&label_y); // by vertex - else if( how_placed == INTERIOR_PLACEMENT ) - geom.label_interior_position(&label_x,&label_y); - else - geom.label_position(&label_x, &label_y); // by middle of line or by point - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - label_x += boost::get<0>(shield_pos); - label_y += boost::get<1>(shield_pos); - - finder.find_point_placement( text_placement, placement_options, - label_x, label_y, 0.0, - sym.get_line_spacing(), - sym.get_character_spacing()); - - // check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies - if( text_placement.placements.size() > 0) - { - double x = floor(text_placement.placements[0].starting_x); - double y = floor(text_placement.placements[0].starting_y); - int px; - int py; - - if( !sym.get_unlock_image() ) - { - // center image at text center position - // remove displacement from image label - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - px=int(floor(lx - (0.5 * w))) + 1; - py=int(floor(ly - (0.5 * h))) + 1; - label_ext.re_center(lx,ly); - } - else - { // center image at reference location - px=int(floor(label_x - 0.5 * w)); - py=int(floor(label_y - 0.5 * h)); - label_ext.re_center(label_x,label_y); - } - - if ( sym.get_allow_overlap() || detector_->has_placement(label_ext) ) - { - render_marker(px,py,**marker,tr,sym.get_opacity()); - - box2d dim = ren.prepare_glyphs(&text_placement.placements[0]); - ren.render(x,y); - detector_->insert(label_ext); - finder.update_detector(text_placement); - if (writer.first) { - writer.first->add_box(label_ext, feature, t_, writer.second); - writer.first->add_text(text_placement, faces, feature, t_, writer.second); - } - } - } - } - } - - else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT) - { - placement text_placement(info, sym, scale_factor_, w, h, false); - position const& pos = sym.get_displacement(); - - text_placement.avoid_edges = sym.get_avoid_edges(); - text_placement.additional_boxes.push_back( - box2d(-0.5 * label_ext.width() - boost::get<0>(pos), - -0.5 * label_ext.height() - boost::get<1>(pos), - 0.5 * label_ext.width() - boost::get<0>(pos), - 0.5 * label_ext.height() - boost::get<1>(pos))); - finder.find_point_placements(text_placement, placement_options, path); - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii) - { - double x = floor(text_placement.placements[ii].starting_x); - double y = floor(text_placement.placements[ii].starting_y); - - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - int px=int(floor(lx - (0.5*w))) + 1; - int py=int(floor(ly - (0.5*h))) + 1; - label_ext.re_center(lx, ly); - - render_marker(px,py,**marker,tr,sym.get_opacity()); - - box2d dim = ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render(x,y); - if (writer.first) writer.first->add_box(label_ext, feature, t_, writer.second); - } - finder.update_detector(text_placement); - if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second); - } - } - } + double x = placement->placements[ii].starting_x; + double y = placement->placements[ii].starting_y; + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render(x, y); } } -#endif } diff --git a/src/agg/process_text_symbolizer.cpp b/src/agg/process_text_symbolizer.cpp index fd857a576..d693c15e7 100644 --- a/src/agg/process_text_symbolizer.cpp +++ b/src/agg/process_text_symbolizer.cpp @@ -33,23 +33,24 @@ void agg_renderer::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - /* This could also be a member of the renderer class, but I would have - to check if any of the variables changes and notify the helper. - It could be done at a later point, but for now keep the code simple. - */ - text_symbolizer_helper, label_collision_detector4> helper(width_, height_, scale_factor_, t_, font_manager_, *detector_); - - text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); - - if (!placement) return; + text_symbolizer_helper, + label_collision_detector4> helper( + sym, feature, prj_trans, + width_, height_, + scale_factor_, + t_, font_manager_, *detector_); text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); - for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) - { - double x = placement->placements[ii].starting_x; - double y = placement->placements[ii].starting_y; - ren.prepare_glyphs(&(placement->placements[ii])); - ren.render(x, y); + + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) + { + double x = placement->placements[ii].starting_x; + double y = placement->placements[ii].starting_y; + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render(x, y); + } } } diff --git a/src/build.py b/src/build.py index b5a4330d0..1fc21f836 100644 --- a/src/build.py +++ b/src/build.py @@ -145,6 +145,7 @@ source = Split( memory_datasource.cpp stroke.cpp symbolizer.cpp + symbolizer_helpers.cpp arrow.cpp unicode.cpp markers_symbolizer.cpp diff --git a/src/cairo_renderer.cpp b/src/cairo_renderer.cpp index 13db7f47e..c26509bf3 100644 --- a/src/cairo_renderer.cpp +++ b/src/cairo_renderer.cpp @@ -1051,207 +1051,26 @@ void cairo_renderer_base::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { -#if 0 - typedef coord_transform2 path_type; + shield_symbolizer_helper, + label_collision_detector4> helper( + sym, feature, prj_trans, + detector_.extent().width(), detector_.extent().height(), + 1.0 /*scale_factor*/, + t_, font_manager_, detector_); - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - placement_options->next(); + cairo_context context(context_); - UnicodeString text; - if( sym.get_no_text() ) - text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render - else - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - text = result.to_unicode(); - } - - if ( sym.get_text_transform() == UPPERCASE) - { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - agg::trans_affine tr; - boost::array const& m = sym.get_transform(); - tr.load_from(&m[0]); - - std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); - boost::optional marker; - if ( !filename.empty() ) - { - marker = marker_cache::instance()->find(filename, true); - } - else - { - marker.reset(boost::make_shared()); - } - - if (text.length() > 0 && marker) - { - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } - - if (faces->size() > 0) - { - cairo_context context(context_); - string_info info(text); - - placement_finder finder(detector_); - - faces->set_character_sizes(placement_options->text_size); - faces->get_string_info(info, text, 0); - - int w = (*marker)->width(); - int h = (*marker)->height(); - - metawriter_with_properties writer = sym.get_metawriter(); - - for (unsigned i = 0; i < feature.num_geometries(); ++i) - { - geometry_type const& geom = feature.get_geometry(i); - if (geom.num_points() > 0) // don't bother with empty geometries - { - path_type path(t_, geom, prj_trans); - - label_placement_enum how_placed = sym.get_label_placement(); - if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT || how_placed == INTERIOR_PLACEMENT) - { - // for every vertex, try and place a shield/text - geom.rewind(0); - placement text_placement(info, sym, 1.0, w, h, false); - text_placement.avoid_edges = sym.get_avoid_edges(); - text_placement.allow_overlap = sym.get_allow_overlap(); - if (writer.first) - text_placement.collect_extents = true; // needed for inmem metawriter - position const& pos = sym.get_displacement(); - position const& shield_pos = sym.get_shield_displacement(); - for( unsigned jj = 0; jj < geom.num_points(); jj++ ) - { - double label_x; - double label_y; - double z=0.0; - - if( how_placed == VERTEX_PLACEMENT ) - geom.vertex(&label_x,&label_y); // by vertex - else if( how_placed == INTERIOR_PLACEMENT ) - geom.label_interior_position(&label_x,&label_y); - else - geom.label_position(&label_x, &label_y); // by middle of line or by point - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - label_x += boost::get<0>(shield_pos); - label_y += boost::get<1>(shield_pos); - - finder.find_point_placement(text_placement, placement_options, - label_x, label_y, 0.0, - sym.get_line_spacing(), - sym.get_character_spacing()); - - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii) - { - double x = text_placement.placements[ii].starting_x; - double y = text_placement.placements[ii].starting_y; - - int px; - int py; - box2d label_ext; - - if( !sym.get_unlock_image() ) - { - // center image at text center position - // remove displacement from image label - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - px=int(floor(lx - (0.5 * w))); - py=int(floor(ly - (0.5 * h))); - label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) ); - } - else - { // center image at reference location - px=int(floor(label_x - 0.5 * w)); - py=int(floor(label_y - 0.5 * h)); - label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h)); - } - - if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) ) - { - render_marker(px,py,**marker, tr, sym.get_opacity()); - - context.add_text(text_placement.placements[ii], - face_manager_, - faces, - placement_options->text_size, - sym.get_fill(), - sym.get_halo_radius(), - sym.get_halo_fill() - ); - if (writer.first) { - writer.first->add_box(box2d(px,py,px+w,py+h), feature, t_, writer.second); - writer.first->add_text(text_placement, faces, feature, t_, writer.second); //Only 1 placement - } - detector_.insert(label_ext); - } - } - - finder.update_detector(text_placement); - } - } - else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT) - { - placement text_placement(info, sym, 1.0, w, h, true); - - text_placement.avoid_edges = sym.get_avoid_edges(); - finder.find_point_placements(text_placement, placement_options, path); - - position const& pos = sym.get_displacement(); - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii) - { - double x = text_placement.placements[ii].starting_x; - double y = text_placement.placements[ii].starting_y; - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - int px=int(floor(lx - (0.5*w))); - int py=int(floor(ly - (0.5*h))); - - render_marker(px,py,**marker, tr, sym.get_opacity()); - - context.add_text(text_placement.placements[ii], - face_manager_, - faces, - placement_options->text_size, - sym.get_fill(), - sym.get_halo_radius(), - sym.get_halo_fill() - ); - if (writer.first) writer.first->add_box(box2d(px,py,px+w,py+h), feature, t_, writer.second); - } - finder.update_detector(text_placement); - if (writer.first) writer.first->add_text(text_placement, faces, feature, t_, writer.second); //More than one placement - } - } - } + std::pair marker_pos = helper.get_marker_position(placement->placements[ii]); + render_marker(marker_pos.first, marker_pos.second, + helper.get_marker(), helper.get_transform(), + sym.get_opacity()); + context.add_text(placement->placements[ii], face_manager_, font_manager_); } } -#endif } void cairo_renderer_base::process(line_pattern_symbolizer const& sym, @@ -1435,17 +1254,15 @@ void cairo_renderer_base::process(text_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { - text_symbolizer_helper, label_collision_detector4> helper(detector_.extent().width(), detector_.extent().height(), 1.0 /*scale_factor*/, t_, font_manager_, detector_); - - text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); - - if (!placement) return; + text_symbolizer_helper, label_collision_detector4> helper(sym, feature, prj_trans, detector_.extent().width(), detector_.extent().height(), 1.0 /*scale_factor*/, t_, font_manager_, detector_); cairo_context context(context_); - - for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) - { - context.add_text(placement->placements[ii], face_manager_, font_manager_); + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) + { + context.add_text(placement->placements[ii], face_manager_, font_manager_); + } } } diff --git a/src/grid/process_shield_symbolizer.cpp b/src/grid/process_shield_symbolizer.cpp index c286f7409..b1aac00e6 100644 --- a/src/grid/process_shield_symbolizer.cpp +++ b/src/grid/process_shield_symbolizer.cpp @@ -28,8 +28,8 @@ #include #include -#include -#include +#include + #include #include #include @@ -44,201 +44,36 @@ void grid_renderer::process(shield_symbolizer const& sym, Feature const& feature, proj_transform const& prj_trans) { -#if 0 - typedef coord_transform2 path_type; + shield_symbolizer_helper, + label_collision_detector4> helper( + sym, feature, prj_trans, + width_, height_, + scale_factor_, + t_, font_manager_, detector_); bool placement_found = false; - text_placement_info_ptr placement_options = sym.get_placement_options()->get_placement_info(); - placement_options->next(); - placement_options->next_position_only(); + text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); - UnicodeString text; - if( sym.get_no_text() ) - text = UnicodeString( " " ); // TODO: fix->use 'space' as the text to render - else - { - expression_ptr name_expr = sym.get_name(); - if (!name_expr) return; - value_type result = boost::apply_visitor(evaluate(feature),*name_expr); - text = result.to_unicode(); - } - - if ( sym.get_text_transform() == UPPERCASE) - { - text = text.toUpper(); - } - else if ( sym.get_text_transform() == LOWERCASE) - { - text = text.toLower(); - } - else if ( sym.get_text_transform() == CAPITALIZE) - { - text = text.toTitle(NULL); - } - - agg::trans_affine tr; - boost::array const& m = sym.get_transform(); - tr.load_from(&m[0]); - tr = agg::trans_affine_scaling(scale_factor_) * tr; - - std::string filename = path_processor_type::evaluate( *sym.get_filename(), feature); - boost::optional marker; - if ( !filename.empty() ) - { - marker = marker_cache::instance()->find(filename, true); - } - else - { - marker.reset(boost::make_shared()); - } - - if (text.length() > 0 && marker) - { - face_set_ptr faces; - - if (sym.get_fontset().size() > 0) + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + placement_found = true; + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) { - faces = font_manager_.get_face_set(sym.get_fontset()); - } - else - { - faces = font_manager_.get_face_set(sym.get_face_name()); - } + std::pair marker_pos = helper.get_marker_position(placement->placements[ii]); + render_marker(feature, pixmap_.get_resolution(), + marker_pos.first, marker_pos.second, + helper.get_marker(), helper.get_transform(), + sym.get_opacity()); - stroker_ptr strk = font_manager_.get_stroker(); - if (strk && faces->size() > 0) - { - text_renderer ren(pixmap_, faces, *strk); - - ren.set_character_size(sym.get_text_size() * scale_factor_ * (1.0/pixmap_.get_resolution())); - ren.set_fill(sym.get_fill()); - ren.set_halo_fill(sym.get_halo_fill()); - ren.set_halo_radius(sym.get_halo_radius() * scale_factor_); - ren.set_opacity(sym.get_text_opacity()); - - placement_finder finder(detector_); - - string_info info(text); - - faces->get_string_info(info, text, 0); - - // TODO- clamp to at least 4 px otherwise interactivity is too small - int w = (*marker)->width()/pixmap_.get_resolution(); - int h = (*marker)->height()/pixmap_.get_resolution(); - - for (unsigned i = 0; i < feature.num_geometries(); ++i) - { - geometry_type const& geom = feature.get_geometry(i); - if (geom.num_points() > 0 ) - { - path_type path(t_,geom,prj_trans); - - label_placement_enum how_placed = sym.get_label_placement(); - if (how_placed == POINT_PLACEMENT || how_placed == VERTEX_PLACEMENT || how_placed == INTERIOR_PLACEMENT) - { - // for every vertex, try and place a shield/text - geom.rewind(0); - placement text_placement(info, sym, scale_factor_, w, h, false); - text_placement.avoid_edges = sym.get_avoid_edges(); - text_placement.allow_overlap = sym.get_allow_overlap(); - position const& pos = sym.get_displacement(); - position const& shield_pos = sym.get_shield_displacement(); - for( unsigned jj = 0; jj < geom.num_points(); jj++ ) - { - double label_x; - double label_y; - double z=0.0; - - if( how_placed == VERTEX_PLACEMENT ) - geom.vertex(&label_x,&label_y); // by vertex - else if( how_placed == INTERIOR_PLACEMENT ) - geom.label_interior_position(&label_x,&label_y); - else - geom.label_position(&label_x, &label_y); // by middle of line or by point - prj_trans.backward(label_x,label_y, z); - t_.forward(&label_x,&label_y); - - label_x += boost::get<0>(shield_pos); - label_y += boost::get<1>(shield_pos); - - finder.find_point_placement( text_placement, placement_options, label_x, label_y, 0.0, - sym.get_line_spacing(), - sym.get_character_spacing()); - - // check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies - if( text_placement.placements.size() > 0) - { - placement_found = true; - double x = floor(text_placement.placements[0].starting_x); - double y = floor(text_placement.placements[0].starting_y); - int px; - int py; - box2d label_ext; - - if( !sym.get_unlock_image() ) - { - // center image at text center position - // remove displacement from image label - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - px=int(floor(lx - (0.5 * w))); - py=int(floor(ly - (0.5 * h))); - label_ext.init( floor(lx - 0.5 * w), floor(ly - 0.5 * h), ceil (lx + 0.5 * w), ceil (ly + 0.5 * h) ); - } - else - { // center image at reference location - px=int(floor(label_x - 0.5 * w)); - py=int(floor(label_y - 0.5 * h)); - label_ext.init( floor(label_x - 0.5 * w), floor(label_y - 0.5 * h), ceil (label_x + 0.5 * w), ceil (label_y + 0.5 * h)); - } - - if ( sym.get_allow_overlap() || detector_.has_placement(label_ext) ) - { - render_marker(feature,pixmap_.get_resolution(),px,py,**marker,tr,sym.get_opacity()); - - box2d dim = ren.prepare_glyphs(&text_placement.placements[0]); - ren.render_id(feature.id(),x,y,2); - detector_.insert(label_ext); - finder.update_detector(text_placement); - } - } - } - } - - else if (geom.num_points() > 1 && how_placed == LINE_PLACEMENT) - { - placement text_placement(info, sym, scale_factor_, w, h, true); - - text_placement.avoid_edges = sym.get_avoid_edges(); - finder.find_point_placements(text_placement, placement_options, path); - - position const& pos = sym.get_displacement(); - for (unsigned int ii = 0; ii < text_placement.placements.size(); ++ ii) - { - placement_found= true; - double x = floor(text_placement.placements[ii].starting_x); - double y = floor(text_placement.placements[ii].starting_y); - - double lx = x - boost::get<0>(pos); - double ly = y - boost::get<1>(pos); - int px=int(floor(lx - (0.5*w))); - int py=int(floor(ly - (0.5*h))); - - render_marker(feature,pixmap_.get_resolution(),px,py,**marker,tr,sym.get_opacity()); - - box2d dim = ren.prepare_glyphs(&text_placement.placements[ii]); - ren.render_id(feature.id(),x,y,2); - } - finder.update_detector(text_placement); - } - } - } + double x = floor(placement->placements[ii].starting_x); + double y = floor(placement->placements[ii].starting_y); + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render_id(feature.id(), x, y, 2); } } if (placement_found) pixmap_.add_feature(feature); -#endif } template void grid_renderer::process(shield_symbolizer const&, diff --git a/src/grid/process_text_symbolizer.cpp b/src/grid/process_text_symbolizer.cpp index 527196d36..ae61d13d4 100644 --- a/src/grid/process_text_symbolizer.cpp +++ b/src/grid/process_text_symbolizer.cpp @@ -23,8 +23,6 @@ // mapnik #include -#include -#include #include namespace mapnik { @@ -36,23 +34,26 @@ void grid_renderer::process(text_symbolizer const& sym, { text_symbolizer_helper, label_collision_detector4> helper( + sym, feature, prj_trans, width_, height_, scale_factor_ * (1.0/pixmap_.get_resolution()), t_, font_manager_, detector_); - - text_placement_info_ptr placement = helper.get_placement(sym, feature, prj_trans); - - if (!placement) return; + bool placement_found = false; text_renderer ren(pixmap_, font_manager_, *(font_manager_.get_stroker())); - for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) - { - double x = placement->placements[ii].starting_x; - double y = placement->placements[ii].starting_y; - ren.prepare_glyphs(&(placement->placements[ii])); - ren.render_id(feature.id(),x,y,2); + + text_placement_info_ptr placement; + while ((placement = helper.get_placement())) { + placement_found = true; + for (unsigned int ii = 0; ii < placement->placements.size(); ++ii) + { + double x = placement->placements[ii].starting_x; + double y = placement->placements[ii].starting_y; + ren.prepare_glyphs(&(placement->placements[ii])); + ren.render_id(feature.id(),x,y,2); + } } - pixmap_.add_feature(feature); + if (placement_found) pixmap_.add_feature(feature); } diff --git a/src/load_map.cpp b/src/load_map.cpp index c6e3f47e8..79f3d5c2e 100644 --- a/src/load_map.cpp +++ b/src/load_map.cpp @@ -1281,8 +1281,7 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) placement_finder = text_placements_ptr(new text_placements_dummy()); } - text_symbolizer text_symbol = text_symbolizer(placement_finder); - placement_finder->properties.set_values_from_xml(sym, fontsets_); + placement_finder->properties.from_xml(sym, fontsets_); if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name); if (list) { ptree::const_iterator symIter = sym.begin(); @@ -1296,10 +1295,12 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) } ensure_attrs(symIter->second, "TextSymbolizer/Placement", s_common.str()); text_symbolizer_properties & p = list->add(); - p.set_values_from_xml(symIter->second, fontsets_); + p.from_xml(symIter->second, fontsets_); if (strict_) ensure_font_face(p.processor.defaults.face_name); } } + + text_symbolizer text_symbol = text_symbolizer(placement_finder); parse_metawriter_in_symbolizer(text_symbol, sym); rule.append(text_symbol); } @@ -1312,26 +1313,64 @@ void map_parser::parse_text_symbolizer( rule & rule, ptree const & sym ) void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) { + std::string s_common( + "name,face-name,fontset-name,size,fill,orientation," + "dx,dy,placement,vertical-alignment,halo-fill," + "halo-radius,text-ratio,wrap-width,wrap-before," + "wrap-character,text-transform,line-spacing," + "label-position-tolerance,character-spacing," + "spacing,minimum-distance,minimum-padding,minimum-path-length," + "avoid-edges,allow-overlap,opacity,max-char-angle-delta," + "horizontal-alignment,justify-alignment"); - std::stringstream s; - s << "name,face-name,fontset-name,size,fill," - << "dx,dy,placement,vertical-alignment,halo-fill," - << "halo-radius,text-ratio,wrap-width,wrap-before," - << "wrap-character,text-transform,line-spacing," - << "label-position-tolerance,character-spacing," - << "spacing,minimum-distance,minimum-padding," - << "avoid-edges,allow-overlap,opacity,max-char-angle-delta," - << "horizontal-alignment,justify-alignment," - // additional for shield - /* transform instead of orientation */ - << "file,base,transform,shield-dx,shield-dy," - << "text-opacity,unlock-image,no-text," - << "meta-writer,meta-output"; + std::string s_symbolizer(s_common + ",file,base," + "transform,shield-dx,shield-dy,text-opacity," + "unlock-image" + "placements,placement-type,meta-writer,meta-output"); - ensure_attrs(sym, "ShieldSymbolizer", s.str()); + ensure_attrs(sym, "ShieldSymbolizer", s_symbolizer); try { - shield_symbolizer shield_symbol = shield_symbolizer(); + text_placements_ptr placement_finder; + text_placements_list *list = 0; + optional placement_type = get_opt_attr(sym, "placement-type"); + if (placement_type) { + if (*placement_type == "simple") { + placement_finder = text_placements_ptr( + new text_placements_simple( + get_attr(sym, "placements", "X"))); + } else if (*placement_type == "list") { + list = new text_placements_list(); + placement_finder = text_placements_ptr(list); + } else if (*placement_type != "dummy" && *placement_type != "") { + throw config_error(std::string("Unknown placement type '"+*placement_type+"'")); + } + } + if (!placement_finder) { + placement_finder = text_placements_ptr(new text_placements_dummy()); + } + + placement_finder->properties.from_xml(sym, fontsets_); + if (strict_) ensure_font_face(placement_finder->properties.processor.defaults.face_name); + if (list) { + ptree::const_iterator symIter = sym.begin(); + ptree::const_iterator endSym = sym.end(); + for( ;symIter != endSym; ++symIter) { + if (symIter->first.find('<') != std::string::npos) continue; + if (symIter->first != "Placement") + { +// throw config_error("Unknown element '" + symIter->first + "'"); TODO + continue; + } + ensure_attrs(symIter->second, "TextSymbolizer/Placement", s_common); + text_symbolizer_properties & p = list->add(); + p.from_xml(symIter->second, fontsets_); + if (strict_) ensure_font_face(p.processor.defaults.face_name); + } + } + + shield_symbolizer shield_symbol = shield_symbolizer(placement_finder); + /* Symbolizer specific attributes. */ optional transform_wkt = get_opt_attr(sym, "transform"); if (transform_wkt) { @@ -1378,14 +1417,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) shield_symbol.set_unlock_image( * unlock_image ); } - // no text - optional no_text = - get_opt_attr(sym, "no-text"); - if (no_text) - { - shield_symbol.set_no_text( * no_text ); - } - parse_metawriter_in_symbolizer(shield_symbol, sym); std::string image_file = get_attr(sym, "file"); @@ -1418,8 +1449,6 @@ void map_parser::parse_shield_symbolizer( rule & rule, ptree const & sym ) std::clog << "### WARNING: " << msg << endl; } } - text_placements_ptr placement_finder = shield_symbol.get_placement_options(); - placement_finder->properties.set_values_from_xml(sym, fontsets_); rule.append(shield_symbol); } catch (const config_error & ex) diff --git a/src/metawriter.cpp b/src/metawriter.cpp index e54dcac2a..02cd62e65 100644 --- a/src/metawriter.cpp +++ b/src/metawriter.cpp @@ -193,7 +193,7 @@ void metawriter_json_stream::add_text(text_placement_info const& p, */ for (unsigned n = 0; n < p.placements.size(); n++) { - placement_element & current_placement = const_cast(p.placements[n]); + text_path & current_placement = const_cast(p.placements[n]); bool inside = false; /* Part of text is inside rendering region */ bool straight = true; diff --git a/src/placement_finder.cpp b/src/placement_finder.cpp index 2d2888cf4..da5583033 100644 --- a/src/placement_finder.cpp +++ b/src/placement_finder.cpp @@ -103,6 +103,7 @@ placement_finder::placement_finder(text_placement_info &placement_inf dimensions_(detector_.extent()), info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_() { + placement_info.placements.clear(); //Remove left overs } template @@ -111,6 +112,7 @@ placement_finder::placement_finder(text_placement_info &placement_inf dimensions_(extent), info_(info), p(placement_info.properties), pi(placement_info), string_width_(0), string_height_(0), first_line_space_(0), valign_(V_AUTO), halign_(H_AUTO), line_breaks_(), line_sizes_() { + placement_info.placements.clear(); //Remove left overs } template @@ -289,9 +291,9 @@ void placement_finder::init_alignment() { valign_ = p.valign; if (valign_ == V_AUTO) { - if (p.displacement.get<1>() > 0.0) + if (p.displacement.second > 0.0) valign_ = V_BOTTOM; - else if (p.displacement.get<1>() < 0.0) + else if (p.displacement.second < 0.0) valign_ = V_TOP; else valign_ = V_MIDDLE; @@ -299,9 +301,9 @@ void placement_finder::init_alignment() halign_ = p.halign; if (halign_ == H_AUTO) { - if (p.displacement.get<0>() > 0.0) + if (p.displacement.first > 0.0) halign_ = H_RIGHT; - else if (p.displacement.get<0>() < 0.0) + else if (p.displacement.first < 0.0) halign_ = H_LEFT; else halign_ = H_MIDDLE; @@ -310,7 +312,7 @@ void placement_finder::init_alignment() template -void placement_finder::adjust_position(placement_element *current_placement, double label_x, double label_y) +void placement_finder::adjust_position(text_path *current_placement, double label_x, double label_y) { // if needed, adjust for desired vertical alignment current_placement->starting_y = label_y; // no adjustment, default is MIDDLE @@ -331,8 +333,8 @@ void placement_finder::adjust_position(placement_element *current_pla current_placement->starting_x += 0.5 * string_width_; // move center right by 1/2 the string width // adjust text envelope position by user's x-y displacement (dx, dy) - current_placement->starting_x += pi.get_scale_factor() * boost::tuples::get<0>(p.displacement); - current_placement->starting_y += pi.get_scale_factor() * boost::tuples::get<1>(p.displacement); + current_placement->starting_x += pi.get_scale_factor() * p.displacement.first; + current_placement->starting_y += pi.get_scale_factor() * p.displacement.second; } @@ -348,7 +350,7 @@ void placement_finder::find_point_placement(double label_x, double la double sina = std::sin(rad); double x, y; - std::auto_ptr current_placement(new placement_element); + std::auto_ptr current_placement(new text_path); adjust_position(current_placement.get(), label_x, label_y); @@ -456,12 +458,10 @@ void placement_finder::find_point_placement(double label_x, double la x += cwidth; // move position to next character } -#if 0 - //TODO // check the placement of any additional envelopes - if (!p.allow_overlap && !p.additional_boxes.empty()) + if (!p.allow_overlap && !pi.additional_boxes.empty()) { - BOOST_FOREACH(box2d box, p.additional_boxes) + BOOST_FOREACH(box2d box, pi.additional_boxes) { box2d pt(box.minx() + current_placement->starting_x, box.miny() + current_placement->starting_y, @@ -474,7 +474,6 @@ void placement_finder::find_point_placement(double label_x, double la c_envelopes.push(pt); } } -#endif // since there was no early exit, add the character envelopes to the placements' envelopes while( !c_envelopes.empty() ) @@ -535,7 +534,7 @@ void placement_finder::find_line_placements(PathT & shape_path) double distance = 0.0; - double displacement = boost::tuples::get<1>(p.displacement); // displace by dy + double displacement = p.displacement.second; // displace by dy //Calculate a target_distance that will place the labels centered evenly rather than offset from the start of the linestring if (total_distance < string_width_) //Can't place any strings @@ -595,7 +594,7 @@ void placement_finder::find_line_placements(PathT & shape_path) { //Record details for the start of the string placement int orientation = 0; - std::auto_ptr current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir)); + std::auto_ptr current_placement = get_placement_offset(path_positions, path_distances, orientation, index, segment_length - (distance - target_distance) + (diff*dir)); //We were unable to place here if (current_placement.get() == NULL) @@ -657,7 +656,7 @@ void placement_finder::find_line_placements(PathT & shape_path) } template -std::auto_ptr placement_finder::get_placement_offset(const std::vector &path_positions, const std::vector &path_distances, int &orientation, unsigned index, double distance) +std::auto_ptr placement_finder::get_placement_offset(const std::vector &path_positions, const std::vector &path_distances, int &orientation, unsigned index, double distance) { //Check that the given distance is on the given index and find the correct index and distance if not while (distance < 0 && index > 1) @@ -666,7 +665,7 @@ std::auto_ptr placement_finder::get_placement_offs distance += path_distances[index]; } if (index <= 1 && distance < 0) //We've gone off the start, fail out - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); //Same thing, checking if we go off the end while (index < path_distances.size() && distance > path_distances[index]) @@ -675,13 +674,13 @@ std::auto_ptr placement_finder::get_placement_offs index++; } if (index >= path_distances.size()) - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); //Keep track of the initial index,distance incase we need to re-call get_placement_offset const unsigned initial_index = index; const double initial_distance = distance; - std::auto_ptr current_placement(new placement_element); + std::auto_ptr current_placement(new text_path); double old_x = path_positions[index-1].x; double old_y = path_positions[index-1].y; @@ -695,7 +694,7 @@ std::auto_ptr placement_finder::get_placement_offs double segment_length = path_distances[index]; if (segment_length == 0) { // Not allowed to place across on 0 length segments or discontinuities - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); } current_placement->starting_x = old_x + dx*distance/segment_length; @@ -719,7 +718,7 @@ std::auto_ptr placement_finder::get_placement_offs //Coordinates this character will start at if (segment_length == 0) { // Not allowed to place across on 0 length segments or discontinuities - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); } double start_x = old_x + dx*distance/segment_length; double start_y = old_y + dy*distance/segment_length; @@ -747,7 +746,7 @@ std::auto_ptr placement_finder::get_placement_offs if (index >= path_positions.size()) //Bail out if we run off the end of the shape { //std::clog << "FAIL: Out of space" << std::endl; - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); } new_x = path_positions[index].x; new_y = path_positions[index].y; @@ -784,7 +783,7 @@ std::auto_ptr placement_finder::get_placement_offs fabs(angle_delta) > p.max_char_angle_delta) { //std::clog << "FAIL: Too Bendy!" << std::endl; - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); } double render_angle = angle; @@ -833,7 +832,7 @@ std::auto_ptr placement_finder::get_placement_offs { //Otherwise we have failed to find a placement //std::clog << "FAIL: Double upside-down!" << std::endl; - return std::auto_ptr(NULL); + return std::auto_ptr(NULL); } } @@ -841,7 +840,7 @@ std::auto_ptr placement_finder::get_placement_offs } template -bool placement_finder::test_placement(const std::auto_ptr & current_placement, const int & orientation) +bool placement_finder::test_placement(const std::auto_ptr & current_placement, const int & orientation) { //Create and test envelopes bool status = true; @@ -996,53 +995,6 @@ void placement_finder::clear() detector_.clear(); } -template -void placement_finder::find_placement(double angle, geometry_type const& geom, CoordTransform const& t, proj_transform const& prj_trans) -{ - double label_x=0.0; - double label_y=0.0; - double z=0.0; - if (p.label_placement == POINT_PLACEMENT || - p.label_placement == VERTEX_PLACEMENT || - p.label_placement == INTERIOR_PLACEMENT) - { - unsigned iterations = 1; - if (p.label_placement == VERTEX_PLACEMENT) - { - iterations = geom.num_points(); - geom.rewind(0); - } - for(unsigned jj = 0; jj < iterations; jj++) { - switch (p.label_placement) - { - case POINT_PLACEMENT: - geom.label_position(&label_x, &label_y); - break; - case INTERIOR_PLACEMENT: - geom.label_interior_position(&label_x, &label_y); - break; - case VERTEX_PLACEMENT: - geom.vertex(&label_x, &label_y); - break; - case LINE_PLACEMENT: - case label_placement_enum_MAX: - /*not handled here*/ - break; - } - prj_trans.backward(label_x, label_y, z); - t.forward(&label_x, &label_y); - - find_point_placement(label_x, label_y, angle); - } - update_detector(); - } else if (p.label_placement == LINE_PLACEMENT && geom.num_points() > 1) - { - typedef coord_transform2 path_type; - path_type path(t, geom, prj_trans); - find_line_placements(path); - } -} - typedef coord_transform2 PathType; typedef label_collision_detector4 DetectorType; diff --git a/src/save_map.cpp b/src/save_map.cpp index 87bc14c4f..13912a52d 100644 --- a/src/save_map.cpp +++ b/src/save_map.cpp @@ -189,8 +189,8 @@ public: ptree::value_type("ShieldSymbolizer", ptree()))->second; - add_font_attributes( sym_node, sym); - add_image_attributes( sym_node, sym); + add_font_attributes(sym_node, sym); + add_image_attributes(sym_node, sym); add_metawriter_attributes(sym_node, sym); // pseudo-default-construct a shield_symbolizer. It is used @@ -199,28 +199,24 @@ public: // maybe add a real, explicit default-ctor? - shield_symbolizer dfl(expression_ptr(), "", 0, color(0,0,0), path_expression_ptr()); + shield_symbolizer dfl; - if (sym.get_unlock_image() != dfl.get_unlock_image() || explicit_defaults_ ) + if (sym.get_unlock_image() != dfl.get_unlock_image() || explicit_defaults_) { - set_attr( sym_node, "unlock-image", sym.get_unlock_image() ); + set_attr(sym_node, "unlock-image", sym.get_unlock_image()); } - if (sym.get_no_text() != dfl.get_no_text() || explicit_defaults_ ) + if (sym.get_text_opacity() != dfl.get_text_opacity() || explicit_defaults_) { - set_attr( sym_node, "no-text", sym.get_no_text() ); - } - if (sym.get_text_opacity() != dfl.get_text_opacity() || explicit_defaults_ ) - { - set_attr( sym_node, "text-opacity", sym.get_text_opacity() ); + set_attr(sym_node, "text-opacity", sym.get_text_opacity()); } position displacement = sym.get_shield_displacement(); - if ( displacement.get<0>() != dfl.get_shield_displacement().get<0>() || explicit_defaults_ ) + if (displacement.first != dfl.get_shield_displacement().first || explicit_defaults_) { - set_attr( sym_node, "shield-dx", displacement.get<0>() ); + set_attr(sym_node, "shield-dx", displacement.first); } - if ( displacement.get<1>() != dfl.get_shield_displacement().get<1>() || explicit_defaults_ ) + if (displacement.second != dfl.get_shield_displacement().second || explicit_defaults_) { - set_attr( sym_node, "shield-dy", displacement.get<1>() ); + set_attr(sym_node, "shield-dy", displacement.second); } } diff --git a/src/shield_symbolizer.cpp b/src/shield_symbolizer.cpp index 5de905935..c5a03a453 100644 --- a/src/shield_symbolizer.cpp +++ b/src/shield_symbolizer.cpp @@ -38,8 +38,7 @@ shield_symbolizer::shield_symbolizer(text_placements_ptr placements) : text_symbolizer(placements), symbolizer_with_image(), unlock_image_(false), - no_text_(false), - shield_displacement_(boost::make_tuple(0,0)) + shield_displacement_(0,0) { } @@ -52,8 +51,7 @@ shield_symbolizer::shield_symbolizer( : text_symbolizer(name, face_name, size, fill), symbolizer_with_image(file), unlock_image_(false), - no_text_(false), - shield_displacement_(boost::make_tuple(0,0)) + shield_displacement_(0, 0) { } @@ -65,8 +63,7 @@ shield_symbolizer::shield_symbolizer( : text_symbolizer(name, size, fill), symbolizer_with_image(file), unlock_image_(false), - no_text_(false), - shield_displacement_(boost::make_tuple(0,0)) + shield_displacement_(0, 0) { } @@ -80,22 +77,12 @@ bool shield_symbolizer::get_unlock_image() const return unlock_image_; } -void shield_symbolizer::set_no_text(bool no_text) -{ - no_text_ = no_text; -} - -bool shield_symbolizer::get_no_text() const -{ - return no_text_; -} - void shield_symbolizer::set_shield_displacement(double shield_dx,double shield_dy) { - shield_displacement_ = boost::make_tuple(shield_dx,shield_dy); + shield_displacement_ = std::make_pair(shield_dx, shield_dy); } -boost::tuple const& shield_symbolizer::get_shield_displacement() const +position const& shield_symbolizer::get_shield_displacement() const { return shield_displacement_; } diff --git a/src/symbolizer_helpers.cpp b/src/symbolizer_helpers.cpp new file mode 100644 index 000000000..72b9cc8f3 --- /dev/null +++ b/src/symbolizer_helpers.cpp @@ -0,0 +1,351 @@ +#include + +namespace mapnik { + +template +text_placement_info_ptr text_symbolizer_helper::get_placement() +{ + if (!placement_valid_) return text_placement_info_ptr(); + if (point_placement_) + return get_point_placement(); + else + return get_line_placement(); +} + +template +text_placement_info_ptr text_symbolizer_helper::get_line_placement() +{ + while (geometries_to_process_.size()) + { + if (geo_itr_ == geometries_to_process_.end()) + { + //Just processed the last geometry. Try next placement. + if (!next_placement()) return text_placement_info_ptr(); //No more placements + //Start again from begin of list + geo_itr_ = geometries_to_process_.begin(); + continue; //Reexecute size check + } + //TODO: Avoid calling constructor repeatedly + placement_finder finder(*placement_, *info_, detector_, dims_); + typedef coord_transform2 path_type; + path_type path(t_, **geo_itr_, prj_trans_); + finder.find_line_placements(path); + //Keep reference to current object so we can delete it. + std::list::iterator current_object = geo_itr_; + geo_itr_++; + if (placement_->placements.size()) + { + //Found a placement + geometries_to_process_.erase(current_object); + if (writer_.first) writer_.first->add_text( + *placement_, font_manager_, + feature_, t_, writer_.second); + return placement_; + } + //No placement for this geometry. Keep it in geometries_to_process_ for next try. + } + return text_placement_info_ptr(); +} + +template +text_placement_info_ptr text_symbolizer_helper::get_point_placement() +{ + while (points_.size()) + { + if (point_itr_ == points_.end()) + { + //Just processed the last point. Try next placement. + if (!next_placement()) return text_placement_info_ptr(); //No more placements + //Start again from begin of list + point_itr_ = points_.begin(); + continue; //Reexecute size check + } + placement_finder finder(*placement_, *info_, detector_, dims_); + finder.find_point_placement(point_itr_->first, point_itr_->second, angle_); + //Keep reference to current object so we can delete it. + std::list::iterator current_object = point_itr_; + point_itr_++; + if (placement_->placements.size()) + { + //Found a placement + points_.erase(current_object); + if (writer_.first) writer_.first->add_text( + *placement_, font_manager_, + feature_, t_, writer_.second); + finder.update_detector(); + return placement_; + } + //No placement for this point. Keep it in points_ for next try. + + } + return text_placement_info_ptr(); +} + + +template +void text_symbolizer_helper::initialize_geometries() +{ + unsigned num_geom = feature_.num_geometries(); + for (unsigned i=0; i 0) + { + // TODO - find less costly method than fetching full envelope + box2d gbox = t_.forward(geom.envelope(), prj_trans_); + if (gbox.width() < sym_.get_minimum_path_length()) + { + continue; + } + } + // TODO - calculate length here as well + geometries_to_process_.push_back(const_cast(&geom)); + } + geo_itr_ = geometries_to_process_.begin(); +} + +template +void text_symbolizer_helper::initialize_points() +{ + label_placement_enum how_placed = placement_->properties.label_placement; + if (how_placed == LINE_PLACEMENT) { + point_placement_ = false; + return; + } else { + point_placement_ = true; + } + + double label_x=0.0; + double label_y=0.0; + double z=0.0; + + std::list::const_iterator itr = geometries_to_process_.begin(); + std::list::const_iterator end = geometries_to_process_.end(); + for (; itr != end; itr++) + { + geometry_type const& geom = **itr; + if (how_placed == VERTEX_PLACEMENT) + { + geom.rewind(0); + for(unsigned i = 0; i < geom.num_points(); i++) + { + geom.vertex(&label_x, &label_y); + prj_trans_.backward(label_x, label_y, z); + t_.forward(&label_x, &label_y); + points_.push_back(std::make_pair(label_x, label_y)); + } + } else { + if (how_placed == POINT_PLACEMENT) + { + geom.label_position(&label_x, &label_y); + } else if (how_placed == INTERIOR_PLACEMENT) + { + geom.label_interior_position(&label_x, &label_y); + } else { +#ifdef MAPNIK_DEBUG + std::cerr << "ERROR: Unknown placement type in initialize_points();\n"; +#endif + } + prj_trans_.backward(label_x, label_y, z); + t_.forward(&label_x, &label_y); + points_.push_back(std::make_pair(label_x, label_y)); + } + } + point_itr_ = points_.begin(); +} + + +template +bool text_symbolizer_helper::next_placement() +{ + if (!placement_->next()) { + placement_valid_ = false; + return false; + } + placement_->properties.processor.process(text_, feature_); + info_ = &(text_.get_string_info()); + if (placement_->properties.orientation) + { + angle_ = boost::apply_visitor( + evaluate(feature_), + *(placement_->properties.orientation)).to_double(); + } else { + angle_ = 0.0; + } + return true; +} + + +/*****************************************************************************/ + + +template +text_placement_info_ptr shield_symbolizer_helper::get_placement() +{ + if (!placement_valid_ || !marker_) return text_placement_info_ptr(); + if (point_placement_) + return get_point_placement(); + else + return get_line_placement(); +} + +template +text_placement_info_ptr shield_symbolizer_helper::get_point_placement() +{ + position const& shield_pos = sym_.get_shield_displacement(); + while (points_.size()) + { + if (point_itr_ == points_.end()) + { + //Just processed the last point. Try next placement. + if (!next_placement()) return text_placement_info_ptr(); //No more placements + //Start again from begin of list + point_itr_ = points_.begin(); + continue; //Reexecute size check + } + position const& pos = placement_->properties.displacement; + double label_x = point_itr_->first + shield_pos.first; + double label_y = point_itr_->second + shield_pos.second; + + placement_finder finder(*placement_, *info_, detector_, dims_); + finder.find_point_placement(label_x, label_y, angle_); + //Keep reference to current object so we can delete it. + std::list::iterator current_object = point_itr_; + point_itr_++; + if (!placement_->placements.size()) + { + //No placement for this point. Keep it in points_ for next try. + continue; + } + //Found a label placement but not necessarily also a marker placement + // check to see if image overlaps anything too, there is only ever 1 placement found for points and verticies + double x = floor(placement_->placements[0].starting_x); + double y = floor(placement_->placements[0].starting_y); + if (!sym_.get_unlock_image()) + { + // center image at text center position + // remove displacement from image label + double lx = x - pos.first; + double ly = y - pos.second; + marker_x_ = int(floor(lx - (0.5 * marker_w_))) + 1; + marker_y_ = int(floor(ly - (0.5 * marker_h_))) + 1; + marker_ext_.re_center(lx, ly); + } + else + { // center image at reference location + marker_x_ = int(floor(label_x - 0.5 * marker_w_)); + marker_y_ = int(floor(label_y - 0.5 * marker_h_)); + marker_ext_.re_center(label_x, label_y); + } + + if (placement_->properties.allow_overlap || detector_.has_placement(marker_ext_)) + { + detector_.insert(marker_ext_); + finder.update_detector(); + if (writer_.first) { + writer_.first->add_box(marker_ext_, feature_, t_, writer_.second); + writer_.first->add_text(*placement_, font_manager_, feature_, t_, writer_.second); + } + points_.erase(current_object); + return placement_; + } + } + return text_placement_info_ptr(); + +} + + +template +text_placement_info_ptr shield_symbolizer_helper::get_line_placement() +{ + position const& pos = placement_->properties.displacement; + placement_->additional_boxes.push_back( + /*TODO: I'm not sure this is correct. It's what the old code did, but + I think transfroms can make the marker non-centered. + */ + box2d(-0.5 * marker_ext_.width() - pos.first, + -0.5 * marker_ext_.height() - pos.second, + 0.5 * marker_ext_.width() - pos.first, + 0.5 * marker_ext_.height() - pos.second)); + return text_symbolizer_helper::get_line_placement(); +} + + +template +void shield_symbolizer_helper::init_marker() +{ + std::string filename = path_processor_type::evaluate(*sym_.get_filename(), this->feature_); + boost::array const& m = sym_.get_transform(); + transform_.load_from(&m[0]); + marker_.reset(); + if (!filename.empty()) + { + marker_ = marker_cache::instance()->find(filename, true); + } + if (!marker_) { + marker_w_ = 0; + marker_h_ = 0; + marker_ext_.init(0, 0, 0, 0); + return; + } + marker_w_ = (*marker_)->width(); + marker_h_ = (*marker_)->height(); + double px0 = - 0.5 * marker_w_; + double py0 = - 0.5 * marker_h_; + double px1 = 0.5 * marker_w_; + double py1 = 0.5 * marker_h_; + double px2 = px1; + double py2 = py0; + double px3 = px0; + double py3 = py1; + transform_.transform(&px0,&py0); + transform_.transform(&px1,&py1); + transform_.transform(&px2,&py2); + transform_.transform(&px3,&py3); + marker_ext_.init(px0, py0, px1, py1); + marker_ext_.expand_to_include(px2, py2); + marker_ext_.expand_to_include(px3, py3); +} + +template +std::pair shield_symbolizer_helper::get_marker_position(text_path &p) +{ + position const& pos = placement_->properties.displacement; + if (placement_->properties.label_placement == LINE_PLACEMENT) { + double x = floor(p.starting_x); + double y = floor(p.starting_y); + + double lx = x - pos.first; + double ly = y - pos.second; + int px = int(floor(lx - (0.5*marker_w_))) + 1; + int py = int(floor(ly - (0.5*marker_h_))) + 1; + marker_ext_.re_center(lx, ly); +// detector_->insert(label_ext); //TODO: Is this done by placement_finder? + + if (writer_.first) writer_.first->add_box(marker_ext_, feature_, t_, writer_.second); + return std::make_pair(px, py); + } else { + return std::make_pair(marker_x_, marker_y_); + } +} + + +template +marker& shield_symbolizer_helper::get_marker() const +{ + return **marker_; +} + +template +agg::trans_affine const& shield_symbolizer_helper::get_transform() const +{ + return transform_; +} + +template class text_symbolizer_helper, label_collision_detector4>; +template class shield_symbolizer_helper, label_collision_detector4>; +} //namespace diff --git a/src/text_placements.cpp b/src/text_placements.cpp index ff1656c77..457e24d78 100644 --- a/src/text_placements.cpp +++ b/src/text_placements.cpp @@ -63,7 +63,7 @@ text_symbolizer_properties::text_symbolizer_properties() : } -void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) +void text_symbolizer_properties::from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) { optional placement_ = get_opt_attr(sym, "placement"); if (placement_) label_placement = *placement_; @@ -95,9 +95,9 @@ void text_symbolizer_properties::set_values_from_xml(boost::property_tree::ptree optional orientation_ = get_opt_attr(sym, "orientation"); if (orientation_) orientation = parse_expression(*orientation_, "utf8"); optional dx = get_opt_attr(sym, "dx"); - if (dx) displacement.get<0>() = *dx; + if (dx) displacement.first = *dx; optional dy = get_opt_attr(sym, "dy"); - if (dy) displacement.get<1>() = *dy; + if (dy) displacement.second = *dy; optional max_char_angle_delta_ = get_opt_attr(sym, "max-char-angle-delta"); if (max_char_angle_delta_) max_char_angle_delta=(*max_char_angle_delta_)*(M_PI/180); processor.from_xml(sym, fontsets); @@ -118,13 +118,13 @@ void text_symbolizer_properties::to_xml(boost::property_tree::ptree &node, bool } } - if (displacement.get<0>() != dfl.displacement.get<0>() || explicit_defaults) + if (displacement.first != dfl.displacement.first || explicit_defaults) { - set_attr(node, "dx", displacement.get<0>()); + set_attr(node, "dx", displacement.first); } - if (displacement.get<1>() != dfl.displacement.get<1>() || explicit_defaults) + if (displacement.second != dfl.displacement.second || explicit_defaults) { - set_attr(node, "dy", displacement.get<1>()); + set_attr(node, "dy", displacement.second); } if (label_placement != dfl.label_placement || explicit_defaults) { @@ -204,7 +204,7 @@ char_properties::char_properties() : } -void char_properties::set_values_from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) +void char_properties::from_xml(boost::property_tree::ptree const &sym, std::map const & fontsets) { optional text_size_ = get_opt_attr(sym, "size"); if (text_size_) text_size = *text_size_; @@ -328,11 +328,13 @@ std::set text_placements::get_all_expressions() /************************************************************************/ -text_placement_info::text_placement_info(text_placements const* parent): - properties(parent->properties), - scale_factor(1), - has_dimensions(false), - collect_extents(false) +text_placement_info::text_placement_info(text_placements const* parent, + double scale_factor_, dimension_type dim, bool has_dimensions_) + : properties(parent->properties), + scale_factor(scale_factor_), + has_dimensions(has_dimensions_), + dimensions(dim), + collect_extents(false) { } @@ -344,17 +346,11 @@ bool text_placement_info_dummy::next() return true; } -text_placement_info_ptr text_placements_dummy::get_placement_info() const +text_placement_info_ptr text_placements_dummy::get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const { - return text_placement_info_ptr(new text_placement_info_dummy(this)); -} - -void text_placement_info::init(double scale_factor_, - unsigned w, unsigned h, bool has_dimensions_) -{ - scale_factor = scale_factor_; - dimensions = std::make_pair(w, h); - has_dimensions = has_dimensions_; + return text_placement_info_ptr(new text_placement_info_dummy( + this, scale_factor, dim, has_dimensions)); } /************************************************************************/ @@ -389,36 +385,28 @@ bool text_placement_info_simple::next_position_only() displacement = pdisp; break; case NORTH: - displacement = boost::make_tuple(0, -abs(pdisp.get<1>())); + displacement = std::make_pair(0, -abs(pdisp.second)); break; case EAST: - displacement = boost::make_tuple(abs(pdisp.get<0>()), 0); + displacement = std::make_pair(abs(pdisp.first), 0); break; case SOUTH: - displacement = boost::make_tuple(0, abs(pdisp.get<1>())); + displacement = std::make_pair(0, abs(pdisp.second)); break; case WEST: - displacement = boost::make_tuple(-abs(pdisp.get<0>()), 0); + displacement = std::make_pair(-abs(pdisp.first), 0); break; case NORTHEAST: - displacement = boost::make_tuple( - abs(pdisp.get<0>()), - -abs(pdisp.get<1>())); + displacement = std::make_pair(abs(pdisp.first), -abs(pdisp.second)); break; case SOUTHEAST: - displacement = boost::make_tuple( - abs(pdisp.get<0>()), - abs(pdisp.get<1>())); + displacement = std::make_pair(abs(pdisp.first), abs(pdisp.second)); break; case NORTHWEST: - displacement = boost::make_tuple( - -abs(pdisp.get<0>()), - -abs(pdisp.get<1>())); + displacement = std::make_pair(-abs(pdisp.first), -abs(pdisp.second)); break; case SOUTHWEST: - displacement = boost::make_tuple( - -abs(pdisp.get<0>()), - abs(pdisp.get<1>())); + displacement = std::make_pair(-abs(pdisp.first), abs(pdisp.second)); break; default: std::cerr << "WARNING: Unknown placement\n"; @@ -427,22 +415,24 @@ bool text_placement_info_simple::next_position_only() return true; } -text_placement_info_ptr text_placements_simple::get_placement_info() const +text_placement_info_ptr text_placements_simple::get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const { - return text_placement_info_ptr(new text_placement_info_simple(this)); + return text_placement_info_ptr(new text_placement_info_simple(this, + scale_factor, dim, has_dimensions)); } /** Position string: [POS][SIZE] - * [POS] is any combination of - * N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas) - * [SIZE] is a list of font sizes, separated by commas. The first font size - * is always the one given in the TextSymbolizer's parameters. - * First all directions are tried, then font size is reduced - * and all directions are tried again. The process ends when a placement is - * found or the last fontsize is tried without success. - * Example: N,S,15,10,8 (tries placement above, then below and if - * that fails it tries the additional font sizes 15, 10 and 8. - */ + * [POS] is any combination of + * N, E, S, W, NE, SE, NW, SW, X (exact position) (separated by commas) + * [SIZE] is a list of font sizes, separated by commas. The first font size + * is always the one given in the TextSymbolizer's parameters. + * First all directions are tried, then font size is reduced + * and all directions are tried again. The process ends when a placement is + * found or the last fontsize is tried without success. + * Example: N,S,15,10,8 (tries placement above, then below and if + * that fails it tries the additional font sizes 15, 10 and 8. + */ void text_placements_simple::set_positions(std::string positions) { positions_ = positions; @@ -460,16 +450,16 @@ void text_placements_simple::set_positions(std::string positions) ("NW", NORTHWEST) ("SW", SOUTHWEST) ("X" , EXACT_POSITION) - ; + ; } } direction_name; std::string::iterator first = positions.begin(), last = positions.end(); qi::phrase_parse(first, last, - (direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]), - space - ); + (direction_name[push_back(phoenix::ref(direction_), _1)] % ',') >> *(',' >> qi::float_[push_back(phoenix::ref(text_sizes_), _1)]), + space + ); if (first != last) { std::cerr << "WARNING: Could not parse text_placement_simple placement string ('" << positions << "').\n"; } @@ -483,16 +473,15 @@ text_placements_simple::text_placements_simple() set_positions("X"); } -std::string const& text_placements_simple::get_positions() const -{ - return positions_; -} - text_placements_simple::text_placements_simple(std::string positions) { set_positions(positions); } +std::string text_placements_simple::get_positions() +{ + return positions_; //TODO: Build string from data in direction_ and text_sizes_ +} /***************************************************************************/ @@ -526,9 +515,11 @@ text_symbolizer_properties & text_placements_list::get(unsigned i) /***************************************************************************/ -text_placement_info_ptr text_placements_list::get_placement_info() const +text_placement_info_ptr text_placements_list::get_placement_info( + double scale_factor, dimension_type dim, bool has_dimensions) const { - return text_placement_info_ptr(new text_placement_info_list(this)); + return text_placement_info_ptr(new text_placement_info_list(this, + scale_factor, dim, has_dimensions)); } text_placements_list::text_placements_list() : text_placements(), list_(0) diff --git a/src/text_processing.cpp b/src/text_processing.cpp index 7d616ff18..8f65cbce9 100644 --- a/src/text_processing.cpp +++ b/src/text_processing.cpp @@ -25,371 +25,378 @@ #include #include #include -#include #include +#include #include #include +#include #include +#include namespace mapnik { using boost::property_tree::ptree; using boost::optional; -class abstract_token -{ -public: - virtual ~abstract_token() {} - virtual ptree *to_xml(ptree *node) = 0; -}; +namespace formating { -class abstract_formating_token : public abstract_token +void node::to_xml(boost::property_tree::ptree &xml) const { -public: - virtual void apply(char_properties &p, Feature const& feature) = 0; -}; + //TODO: Should this throw a config_error? +#ifdef MAPNIK_DEBUG + std::cerr << "Error: Trying to write unsupported node type to XML.\n"; +#endif +} -class abstract_text_token : public abstract_token +node_ptr node::from_xml(boost::property_tree::ptree const& xml) { -public: - virtual UnicodeString to_string(Feature const& feature) = 0; -}; + list_node *list = new list_node(); + node_ptr list_ptr(list); + ptree::const_iterator itr = xml.begin(); + ptree::const_iterator end = xml.end(); + for (; itr != end; ++itr) { + node_ptr n; + if (itr->first == "") { + n = text_node::from_xml(itr->second); + } else if (itr->first == "Format") { + n = format_node::from_xml(itr->second); + } else if (itr->first != "" && itr->first != "" && itr->first != "Placement") { + throw config_error("Unknown item " + itr->first); + } + if (n) list->push_back(n); + } + if (list->get_children().size() == 1) { + return list->get_children()[0]; + } else if (list->get_children().size() > 1) { + return list_ptr; + } else { + return node_ptr(); + } +} -class end_format_token : public abstract_token +void node::add_expressions(std::set &expressions) const { -public: - end_format_token() {} - ptree *to_xml(ptree *node); -}; - -class expression_token: public abstract_text_token -{ -public: - expression_token(expression_ptr text); - UnicodeString to_string(Feature const& feature); - ptree *to_xml(ptree *node); - void set_expression(expression_ptr text); - expression_ptr get_expression(); -private: - expression_ptr text_; -}; - -class fixed_formating_token : public abstract_formating_token -{ -public: - fixed_formating_token(); - virtual void apply(char_properties &p, Feature const& feature); - ptree* to_xml(ptree *node); - void from_xml(ptree const& node); - void set_face_name(optional face_name); - void set_text_size(optional text_size); - void set_character_spacing(optional character_spacing); - void set_line_spacing(optional line_spacing); - void set_text_opacity(optional opacity); - void set_wrap_before(optional wrap_before); - void set_wrap_char(optional wrap_char); - void set_text_transform(optional text_trans); - void set_fill(optional fill); - void set_halo_fill(optional halo_fill); - void set_halo_radius(optional radius); -private: - boost::optional face_name_; -// font_set fontset; - boost::optional text_size_; - boost::optional character_spacing_; - boost::optional line_spacing_; - boost::optional text_opacity_; - boost::optional wrap_before_; - boost::optional wrap_char_; - boost::optional text_transform_; - boost::optional fill_; - boost::optional halo_fill_; - boost::optional halo_radius_; -}; + //Do nothing by default +} /************************************************************/ -expression_token::expression_token(expression_ptr text): - text_(text) +void list_node::to_xml(boost::property_tree::ptree &xml) const { + std::vector::const_iterator itr = children_.begin(); + std::vector::const_iterator end = children_.end(); + for (;itr != end; itr++) + { + (*itr)->to_xml(xml); + } } -void expression_token::set_expression(expression_ptr text) + +void list_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const +{ + std::vector::const_iterator itr = children_.begin(); + std::vector::const_iterator end = children_.end(); + for (;itr != end; itr++) + { + (*itr)->apply(p, feature, output); + } +} + + +void list_node::add_expressions(std::set &expressions) const +{ + std::vector::const_iterator itr = children_.begin(); + std::vector::const_iterator end = children_.end(); + for (;itr != end; itr++) + { + (*itr)->add_expressions(expressions); + } +} + + +void list_node::push_back(node_ptr n) +{ + children_.push_back(n); +} + + +void list_node::clear() +{ + children_.clear(); +} + +void list_node::set_children(std::vector const& children) +{ + children_ = children; +} + +std::vector const& list_node::get_children() const +{ + return children_; +} + +/************************************************************/ + +void text_node::to_xml(ptree &xml) const +{ + ptree &new_node = xml.push_back(ptree::value_type( + "", ptree()))->second; + new_node.put_value(to_expression_string(*text_)); +} + + +node_ptr text_node::from_xml(boost::property_tree::ptree const& xml) +{ + std::string data = xml.data(); + boost::trim(data); + if (data.empty()) return node_ptr(); //No text + return node_ptr(new text_node(parse_expression(data, "utf8"))); +} + +void text_node::apply(char_properties const& p, Feature const& feature, processed_text &output) const +{ + UnicodeString text_str = boost::apply_visitor(evaluate(feature), *text_).to_unicode(); + if (p.text_transform == UPPERCASE) + { + text_str = text_str.toUpper(); + } + else if (p.text_transform == LOWERCASE) + { + text_str = text_str.toLower(); + } + else if (p.text_transform == CAPITALIZE) + { + text_str = text_str.toTitle(NULL); + } + if (text_str.length() > 0) { + output.push_back(processed_expression(p, text_str)); + } else { +#ifdef MAPNIK_DEBUG + std::cerr << "Warning: Empty expression.\n"; +#endif + } +} + + +void text_node::add_expressions(std::set &expressions) const +{ + if (text_) expressions.insert(text_); +} + + +void text_node::set_text(expression_ptr text) { text_ = text; } -expression_ptr expression_token::get_expression() + +expression_ptr text_node::get_text() const { return text_; } -UnicodeString expression_token::to_string(const Feature &feature) -{ - value_type result = boost::apply_visitor(evaluate(feature), *text_); - return result.to_unicode(); -} - -ptree *expression_token::to_xml(ptree *node) -{ - ptree &new_node = node->push_back(ptree::value_type( - "", ptree()))->second; - new_node.put_value(to_expression_string(*text_)); - return &new_node; -} - /************************************************************/ -fixed_formating_token::fixed_formating_token(): - fill_() +format_node::format_node(): + node(), + fill_(), + child_() { + } -void fixed_formating_token::apply(char_properties &p, const Feature &feature) +void format_node::to_xml(ptree &xml) const { - if (face_name_) p.face_name = *face_name_; - if (text_size_) p.text_size = *text_size_; - if (character_spacing_) p.character_spacing = *character_spacing_; - if (line_spacing_) p.line_spacing = *line_spacing_; - if (text_opacity_) p.text_opacity = *text_opacity_; - if (wrap_before_) p.wrap_before = *wrap_before_; - if (wrap_char_) p.wrap_char = *wrap_char_; - if (text_transform_) p.text_transform = *text_transform_; - if (fill_) p.fill = *fill_; - if (halo_fill_) p.halo_fill = *halo_fill_; - if (halo_radius_) p.halo_radius = *halo_radius_; + ptree &new_node = xml.push_back(ptree::value_type("Format", ptree()))->second; + if (face_name_) set_attr(new_node, "face-name", *face_name_); + if (text_size_) set_attr(new_node, "size", *text_size_); + if (character_spacing_) set_attr(new_node, "character-spacing", *character_spacing_); + if (line_spacing_) set_attr(new_node, "line-spacing", *line_spacing_); + if (text_opacity_) set_attr(new_node, "opacity", *text_opacity_); + if (wrap_before_) set_attr(new_node, "wrap-before", *wrap_before_); + if (wrap_char_) set_attr(new_node, "wrap-character", *wrap_char_); + if (text_transform_) set_attr(new_node, "text-transform", *text_transform_); + if (fill_) set_attr(new_node, "fill", *fill_); + if (halo_fill_) set_attr(new_node, "halo-fill", *halo_fill_); + if (halo_radius_) set_attr(new_node, "halo-radius", *halo_radius_); + if (child_) child_->to_xml(new_node); } -ptree *fixed_formating_token::to_xml(ptree *node) -{ - ptree &new_node = node->push_back(ptree::value_type("Format", ptree()))->second; - if (face_name_) set_attr(new_node, "face-name", face_name_); - if (text_size_) set_attr(new_node, "size", text_size_); - if (character_spacing_) set_attr(new_node, "character-spacing", character_spacing_); - if (line_spacing_) set_attr(new_node, "line-spacing", line_spacing_); - if (text_opacity_) set_attr(new_node, "opacity", text_opacity_); - if (wrap_before_) set_attr(new_node, "wrap-before", wrap_before_); - if (wrap_char_) set_attr(new_node, "wrap-character", wrap_char_); - if (text_transform_) set_attr(new_node, "text-transform", text_transform_); - if (fill_) set_attr(new_node, "fill", fill_); - if (halo_fill_) set_attr(new_node, "halo-fill", halo_fill_); - if (halo_radius_) set_attr(new_node, "halo-radius", halo_radius_); - return &new_node; -} - -void fixed_formating_token::from_xml(ptree const& node) +node_ptr format_node::from_xml(ptree const& xml) { - set_face_name(get_opt_attr(node, "face-name")); + format_node *n = new format_node(); + node_ptr np(n); + + node_ptr child = node::from_xml(xml); + n->set_child(child); + + n->set_face_name(get_opt_attr(xml, "face-name")); /*TODO: Fontset is problematic. We don't have the fontsets pointer here... */ - set_text_size(get_opt_attr(node, "size")); - set_character_spacing(get_opt_attr(node, "character-spacing")); - set_line_spacing(get_opt_attr(node, "line-spacing")); - set_text_opacity(get_opt_attr(node, "opactity")); - set_wrap_before(get_opt_attr(node, "wrap-before")); - set_wrap_char(get_opt_attr(node, "wrap-character")); - set_text_transform(get_opt_attr(node, "text-transform")); - set_fill(get_opt_attr(node, "fill")); - set_halo_fill(get_opt_attr(node, "halo-fill")); - set_halo_radius(get_opt_attr(node, "halo-radius")); + n->set_text_size(get_opt_attr(xml, "size")); + n->set_character_spacing(get_opt_attr(xml, "character-spacing")); + n->set_line_spacing(get_opt_attr(xml, "line-spacing")); + n->set_text_opacity(get_opt_attr(xml, "opactity")); + boost::optional wrap = get_opt_attr(xml, "wrap-before"); + boost::optional wrap_before; + if (wrap) wrap_before = *wrap; + n->set_wrap_before(wrap_before); + n->set_wrap_char(get_opt_attr(xml, "wrap-character")); + n->set_text_transform(get_opt_attr(xml, "text-transform")); + n->set_fill(get_opt_attr(xml, "fill")); + n->set_halo_fill(get_opt_attr(xml, "halo-fill")); + n->set_halo_radius(get_opt_attr(xml, "halo-radius")); + return np; } -void fixed_formating_token::set_face_name(optional face_name) + +void format_node::apply(char_properties const& p, const Feature &feature, processed_text &output) const +{ + char_properties new_properties = p; + if (face_name_) new_properties.face_name = *face_name_; + if (text_size_) new_properties.text_size = *text_size_; + if (character_spacing_) new_properties.character_spacing = *character_spacing_; + if (line_spacing_) new_properties.line_spacing = *line_spacing_; + if (text_opacity_) new_properties.text_opacity = *text_opacity_; + if (wrap_before_) new_properties.wrap_before = *wrap_before_; + if (wrap_char_) new_properties.wrap_char = *wrap_char_; + if (text_transform_) new_properties.text_transform = *text_transform_; + if (fill_) new_properties.fill = *fill_; + if (halo_fill_) new_properties.halo_fill = *halo_fill_; + if (halo_radius_) new_properties.halo_radius = *halo_radius_; + + if (child_) { + child_->apply(new_properties, feature, output); + } else { +#ifdef MAPNIK_DEBUG + std::cerr << "Warning: Useless format: No text to format\n"; +#endif + } +} + + +void format_node::set_child(node_ptr child) +{ + child_ = child; +} + + +node_ptr format_node::get_child() const +{ + return child_; +} + + +void format_node::set_face_name(optional face_name) { face_name_ = face_name; } -void fixed_formating_token::set_text_size(optional text_size) +void format_node::set_text_size(optional text_size) { text_size_ = text_size; } -void fixed_formating_token::set_character_spacing(optional character_spacing) +void format_node::set_character_spacing(optional character_spacing) { character_spacing_ = character_spacing; } -void fixed_formating_token::set_line_spacing(optional line_spacing) +void format_node::set_line_spacing(optional line_spacing) { line_spacing_ = line_spacing; } -void fixed_formating_token::set_text_opacity(optional text_opacity) +void format_node::set_text_opacity(optional text_opacity) { text_opacity_ = text_opacity; } -void fixed_formating_token::set_wrap_before(optional wrap_before) +void format_node::set_wrap_before(optional wrap_before) { wrap_before_ = wrap_before; } -void fixed_formating_token::set_wrap_char(optional wrap_char) +void format_node::set_wrap_char(optional wrap_char) { wrap_char_ = wrap_char; } -void fixed_formating_token::set_text_transform(optional text_transform) +void format_node::set_text_transform(optional text_transform) { text_transform_ = text_transform; } -void fixed_formating_token::set_fill(optional c) +void format_node::set_fill(optional c) { fill_ = c; } -void fixed_formating_token::set_halo_fill(optional c) +void format_node::set_halo_fill(optional c) { halo_fill_ = c; } -void fixed_formating_token::set_halo_radius(optional radius) +void format_node::set_halo_radius(optional radius) { halo_radius_ = radius; } - -/************************************************************/ - -ptree *end_format_token::to_xml(ptree *node) -{ - return 0; -} +} //namespace formating /************************************************************/ text_processor::text_processor(): - list_(), clear_on_write(false) + tree_() { } -void text_processor::push_back(abstract_token *token) +void text_processor::set_format_tree(formating::node_ptr tree) { - if (clear_on_write) list_.clear(); - clear_on_write = false; - list_.push_back(token); + tree_ = tree; +} + +formating::node_ptr text_processor::get_format_tree() const +{ + return tree_; } void text_processor::from_xml(const boost::property_tree::ptree &pt, std::map const &fontsets) { - clear_on_write = true; - defaults.set_values_from_xml(pt, fontsets); - from_xml_recursive(pt, fontsets); + defaults.from_xml(pt, fontsets); + formating::node_ptr n = formating::node::from_xml(pt); + if (n) set_format_tree(n); } -void text_processor::from_xml_recursive(const boost::property_tree::ptree &pt, std::map const &fontsets) -{ - ptree::const_iterator itr = pt.begin(); - ptree::const_iterator end = pt.end(); - for (; itr != end; ++itr) { - if (itr->first == "") { - std::string data = itr->second.data(); - boost::trim(data); - if (data.empty()) continue; - expression_token *token = new expression_token(parse_expression(data, "utf8")); - push_back(token); - } else if (itr->first == "Format") { - fixed_formating_token *token = new fixed_formating_token(); - token->from_xml(itr->second); - push_back(token); - from_xml_recursive(itr->second, fontsets); /* Parse children, making a list out of a tree. */ - push_back(new end_format_token()); - } else if (itr->first != "" && itr->first != "" && itr->first != "Placement") { - std::cerr << "Unknown item" << itr->first; - } - } -} void text_processor::to_xml(boost::property_tree::ptree &node, bool explicit_defaults, text_processor const& dfl) const { defaults.to_xml(node, explicit_defaults, dfl.defaults); - std::list::const_iterator itr = list_.begin(); - std::list::const_iterator end = list_.end(); - std::stack nodes; - ptree *current_node = &node; - for (; itr != end; ++itr) { - abstract_token *token = *itr; - ptree *new_node = token->to_xml(current_node); - if (dynamic_cast(token)) { - nodes.push(current_node); - current_node = new_node; - } else if (dynamic_cast(token)) { - current_node = nodes.top(); - nodes.pop(); - } - } + if (tree_) tree_->to_xml(node); } -void text_processor::process(processed_text &output, Feature const& feature) +void text_processor::process(processed_text &output, Feature const& feature) const { - std::list::const_iterator itr = list_.begin(); - std::list::const_iterator end = list_.end(); - std::stack formats; - formats.push(defaults); - - for (; itr != end; ++itr) { - abstract_text_token *text = dynamic_cast(*itr); - abstract_formating_token *format = dynamic_cast(*itr);; - end_format_token *end = dynamic_cast(*itr);; - if (text) { - UnicodeString text_str = text->to_string(feature); - char_properties const& p = formats.top(); - /* TODO: Make a class out of text_transform which does the work! */ - if (p.text_transform == UPPERCASE) - { - text_str = text_str.toUpper(); - } - else if (p.text_transform == LOWERCASE) - { - text_str = text_str.toLower(); - } - else if (p.text_transform == CAPITALIZE) - { - text_str = text_str.toTitle(NULL); - } - if (text_str.length() > 0) { - output.push_back(processed_expression(p, text_str)); - } else { + output.clear(); + if (tree_) { + tree_->apply(defaults, feature, output); + } else { #ifdef MAPNIK_DEBUG - std::cerr << "Warning: Empty expression.\n"; + std::cerr << "Warning: text_processor can't produce text: No formating tree!\n"; #endif - } - } else if (format) { - char_properties next_properties = formats.top(); - format->apply(next_properties, feature); - formats.push(next_properties); - } else if (end) { - /* Always keep at least the defaults_ on stack. */ - if (formats.size() > 1) { - formats.pop(); - } else { - std::cerr << "Warning: Internal mapnik error. More elements popped than pushed in text_processor::process()\n"; - output.clear(); - return; - } - } - } - if (formats.size() != 1) { - std::cerr << "Warning: Internal mapnik error. Less elements popped than pushed in text_processor::process()\n"; } } std::set text_processor::get_all_expressions() const { std::set result; - std::list::const_iterator itr = list_.begin(); - std::list::const_iterator end = list_.end(); - for (; itr != end; ++itr) { - expression_token *text = dynamic_cast(*itr); - if (text) result.insert(text->get_expression()); - } + if (tree_) tree_->add_expressions(result); return result; } void text_processor::set_old_style_expression(expression_ptr expr) { - list_.push_back(new expression_token(expr)); + tree_ = formating::node_ptr(new formating::text_node(expr)); } /************************************************************/ @@ -399,12 +406,12 @@ void processed_text::push_back(processed_expression const& exp) expr_list_.push_back(exp); } -processed_text::expression_list::const_iterator processed_text::begin() +processed_text::expression_list::const_iterator processed_text::begin() const { return expr_list_.begin(); } -processed_text::expression_list::const_iterator processed_text::end() +processed_text::expression_list::const_iterator processed_text::end() const { return expr_list_.end(); } @@ -424,7 +431,7 @@ void processed_text::clear() string_info &processed_text::get_string_info() { - //info_.clear(); TODO: if this function is called twice invalid results are returned, so clear string_info first + info_.clear(); //if this function is called twice invalid results are returned, so clear string_info first expression_list::iterator itr = expr_list_.begin(); expression_list::iterator end = expr_list_.end(); for (; itr != end; ++itr) diff --git a/src/text_symbolizer.cpp b/src/text_symbolizer.cpp index 2966e73d1..20e26ffdb 100644 --- a/src/text_symbolizer.cpp +++ b/src/text_symbolizer.cpp @@ -342,7 +342,7 @@ label_placement_e text_symbolizer::get_label_placement() const void text_symbolizer::set_displacement(double x, double y) { - placement_options_->properties.displacement = boost::make_tuple(x,y); + placement_options_->properties.displacement = std::make_pair(x,y); } void text_symbolizer::set_displacement(position const& p) diff --git a/tests/data/good_maps/shield_symbolizer.xml b/tests/data/good_maps/shield_symbolizer.xml index 5a4da13ad..e0221fa64 100644 --- a/tests/data/good_maps/shield_symbolizer.xml +++ b/tests/data/good_maps/shield_symbolizer.xml @@ -2,7 +2,7 @@ @@ -35,4 +35,4 @@ - \ No newline at end of file + diff --git a/tests/python_tests/object_test.py b/tests/python_tests/object_test.py index a4caf1c3a..7981cb16a 100644 --- a/tests/python_tests/object_test.py +++ b/tests/python_tests/object_test.py @@ -24,7 +24,6 @@ def test_line_symbolizer_init(): # ShieldSymbolizer initialization def test_shieldsymbolizer_init(): s = mapnik.ShieldSymbolizer(mapnik.Expression('[Field Name]'), 'DejaVu Sans Bold', 6, mapnik.Color('#000000'), mapnik.PathExpression('../data/images/dummy.png')) - eq_(s.anchor, (0.0,0.5,)) eq_(s.displacement, (0.0,0.0)) eq_(s.allow_overlap, False) eq_(s.avoid_edges, False) @@ -41,7 +40,7 @@ def test_shieldsymbolizer_init(): eq_(s.text_ratio, 0) eq_(s.text_size, 6) eq_(s.wrap_width, 0) - eq_(s.vertical_alignment, mapnik.vertical_alignment.MIDDLE) + eq_(s.vertical_alignment, mapnik.vertical_alignment.AUTO) eq_(s.label_spacing, 0) eq_(s.label_position_tolerance, 0) # 22.5 * M_PI/180.0 initialized by default @@ -54,7 +53,7 @@ def test_shieldsymbolizer_init(): # r1341 eq_(s.wrap_before, False) - eq_(s.horizontal_alignment, mapnik.horizontal_alignment.MIDDLE) + eq_(s.horizontal_alignment, mapnik.horizontal_alignment.AUTO) eq_(s.justify_alignment, mapnik.justify_alignment.MIDDLE) eq_(s.opacity, 1.0) diff --git a/tests/python_tests/pickling_test.py b/tests/python_tests/pickling_test.py index 56a1e1fb1..092c4e60a 100644 --- a/tests/python_tests/pickling_test.py +++ b/tests/python_tests/pickling_test.py @@ -74,6 +74,7 @@ def test_linesymbolizer_pickle(): # TextSymbolizer pickling def test_textsymbolizer_pickle(): + raise Todo("text_symbolizer pickling currently disabled") ts = mapnik.TextSymbolizer(mapnik.Expression('[Field_Name]'), 'Font Name', 8, mapnik.Color('black')) eq_(str(ts.name), str(mapnik.Expression('[Field_Name]'))) @@ -81,7 +82,6 @@ def test_textsymbolizer_pickle(): eq_(ts.text_size, 8) eq_(ts.fill, mapnik.Color('black')) - raise Todo("text_symbolizer pickling currently disabled") ts2 = pickle.loads(pickle.dumps(ts,pickle.HIGHEST_PROTOCOL)) eq_(ts.name, ts2.name) diff --git a/tests/run_tests.py b/tests/run_tests.py index a4be7190d..bbe469a68 100755 --- a/tests/run_tests.py +++ b/tests/run_tests.py @@ -63,8 +63,9 @@ def main(): # 3 * '-v' gets us debugging information from nose argv.append('-v') argv.append('-v') - - argv.extend(['-w','./tests/python_tests']) + + dirname = os.path.dirname(sys.argv[0]) + argv.extend(['-w', dirname+'/python_tests']) if not nose.run(argv=argv, plugins=[TodoPlugin(), Doctest()]): sys.exit(1) diff --git a/tests/visual_tests/formating-1-500-reference.png b/tests/visual_tests/formating-1-500-reference.png new file mode 100644 index 000000000..f863a4169 Binary files /dev/null and b/tests/visual_tests/formating-1-500-reference.png differ diff --git a/tests/visual_tests/formating-1.xml b/tests/visual_tests/formating-1.xml new file mode 100644 index 000000000..75158e8b5 --- /dev/null +++ b/tests/visual_tests/formating-1.xml @@ -0,0 +1,22 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/formating-2-500-reference.png b/tests/visual_tests/formating-2-500-reference.png new file mode 100644 index 000000000..f863a4169 Binary files /dev/null and b/tests/visual_tests/formating-2-500-reference.png differ diff --git a/tests/visual_tests/formating-2.xml b/tests/visual_tests/formating-2.xml new file mode 100644 index 000000000..4fda094db --- /dev/null +++ b/tests/visual_tests/formating-2.xml @@ -0,0 +1,22 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/formating-3-500-reference.png b/tests/visual_tests/formating-3-500-reference.png new file mode 100644 index 000000000..788f2a33b Binary files /dev/null and b/tests/visual_tests/formating-3-500-reference.png differ diff --git a/tests/visual_tests/formating-3.xml b/tests/visual_tests/formating-3.xml new file mode 100644 index 000000000..1e1bbd048 --- /dev/null +++ b/tests/visual_tests/formating-3.xml @@ -0,0 +1,22 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/formating-4-500-reference.png b/tests/visual_tests/formating-4-500-reference.png new file mode 100644 index 000000000..fbc8d6bb1 Binary files /dev/null and b/tests/visual_tests/formating-4-500-reference.png differ diff --git a/tests/visual_tests/formating-4.xml b/tests/visual_tests/formating-4.xml new file mode 100644 index 000000000..746f38d02 --- /dev/null +++ b/tests/visual_tests/formating-4.xml @@ -0,0 +1,21 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/shieldsymbolizer-1-500-reference.png b/tests/visual_tests/shieldsymbolizer-1-500-reference.png new file mode 100644 index 000000000..820509dd5 Binary files /dev/null and b/tests/visual_tests/shieldsymbolizer-1-500-reference.png differ diff --git a/tests/visual_tests/shieldsymbolizer-1.xml b/tests/visual_tests/shieldsymbolizer-1.xml new file mode 100644 index 000000000..a0f726d38 --- /dev/null +++ b/tests/visual_tests/shieldsymbolizer-1.xml @@ -0,0 +1,19 @@ + + + + + + My Style + + shape + points.shp + + + + + + diff --git a/tests/visual_tests/test.py b/tests/visual_tests/test.py index 7fb48166a..616b0cb4b 100755 --- a/tests/visual_tests/test.py +++ b/tests/visual_tests/test.py @@ -11,7 +11,9 @@ dirname = os.path.dirname(sys.argv[0]) widths = [ 800, 600, 400, 300, 250, 200, 150, 100] filenames = ["list", "simple"] filenames_one_width = ["simple-E", "simple-NE", "simple-NW", "simple-N", - "simple-SE", "simple-SW", "simple-S", "simple-W", "formating"] + "simple-SE", "simple-SW", "simple-S", "simple-W", + "formating-1", "formating-2", "formating-3", "formating-4", + "shieldsymbolizer-1"] def render(filename, width): print "Rendering style \"%s\" with width %d" % (filename, width) @@ -22,12 +24,16 @@ def render(filename, width): mapnik.render_to_file(m, '%s-%d-agg.png' % (filename, width)) return m - +if len(sys.argv) > 1: + filenames = [] + filenames_one_width = sys.argv[1:] + for filename in filenames: for width in widths: m = render(filename, width) mapnik.save_map(m, "%s-out.xml" % filename) for filename in filenames_one_width: - render(filename, 500) + m = render(filename, 500) + mapnik.save_map(m, "%s-out.xml" % filename) diff --git a/utils/xml/mapnik2.dtd b/utils/xml/mapnik2.dtd index 9dd639a9c..3bd1a8e43 100644 --- a/utils/xml/mapnik2.dtd +++ b/utils/xml/mapnik2.dtd @@ -196,7 +196,6 @@ line_spacing CDATA "0" min_distance CDATA "0" name CDATA #IMPLIED - no_text (true|false) "false" placement (point|line|vertex) "point" size CDATA "10" spacing CDATA "0"